diff --git a/app/build.gradle b/app/build.gradle index 08323c48..0ac3ce8c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -38,6 +38,7 @@ android { } dependencies { + implementation project(':jchucomponentscompose') implementation 'androidx.appcompat:appcompat:1.3.1' implementation 'com.google.android.material:material:1.4.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 02d147a9..ae815dcd 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,8 @@ + + + + diff --git a/jchucomponentscompose/build.gradle b/jchucomponentscompose/build.gradle index 01f557e2..25c4c031 100644 --- a/jchucomponentscompose/build.gradle +++ b/jchucomponentscompose/build.gradle @@ -49,7 +49,6 @@ dependencies { implementation 'androidx.compose.ui:ui-tooling-preview:1.0.1' implementation 'androidx.activity:activity-compose:1.3.1' debugImplementation 'androidx.compose.ui:ui-tooling:1.0.1' - implementation 'androidx.activity:activity-compose:1.3.1' implementation 'androidx.compose.material:material-icons-extended:1.0.1' implementation 'androidx.compose.foundation:foundation:1.0.1' implementation 'androidx.compose.foundation:foundation-layout:1.0.1' @@ -58,7 +57,6 @@ dependencies { implementation 'androidx.compose.runtime:runtime-livedata:1.0.1' implementation 'androidx.constraintlayout:constraintlayout-compose:1.0.0-beta02' implementation 'androidx.navigation:navigation-compose:2.4.0-alpha06' - implementation 'androidx.activity:activity-compose:1.3.1' // ACCOMPANIST GOOGLE LIBRARY ------------------------------------------------------------------ implementation 'com.google.accompanist:accompanist-systemuicontroller:0.16.1' @@ -66,7 +64,6 @@ dependencies { // ANDROIDX LIBRARY ---------------------------------------------------------------------------- implementation 'androidx.core:core-ktx:1.6.0' - implementation 'androidx.appcompat:appcompat:1.3.1' implementation 'androidx.browser:browser:1.3.0' implementation 'androidx.preference:preference-ktx:1.1.1' @@ -91,7 +88,7 @@ afterEvaluate { from components.release groupId = "com.jeluchu" artifactId = "jchucomponentscompose" - version = "0.0.4" + version = "0.0.6" } } } diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/activities/ActivityExtensions.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/activities/ActivityExtensions.kt new file mode 100644 index 00000000..fbfe14ea --- /dev/null +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/activities/ActivityExtensions.kt @@ -0,0 +1,23 @@ +package com.jeluchu.jchucomponentscompose.core.extensions.activities + +import android.Manifest +import android.app.Activity +import android.content.pm.PackageManager +import androidx.core.app.ActivityCompat + + +private val permissionsList = + arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE) + +val Activity.permissions: Unit + get() { + val perm = + ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) + if (perm != PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions( + this, + permissionsList, + 1 + ) + } + } \ No newline at end of file diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/context/ContextExensionsGetters.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/context/ContextExensions.kt similarity index 91% rename from jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/context/ContextExensionsGetters.kt rename to jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/context/ContextExensions.kt index 17f2fa03..fff53527 100644 --- a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/context/ContextExensionsGetters.kt +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/context/ContextExensions.kt @@ -23,7 +23,9 @@ import android.view.LayoutInflater import android.widget.Toast import androidx.annotation.DrawableRes import androidx.browser.customtabs.CustomTabsIntent +import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat +import com.jeluchu.jchucomponentscompose.R import com.jeluchu.jchucomponentscompose.core.extensions.coroutines.noCrash import com.jeluchu.jchucomponentscompose.core.extensions.packageutils.buildIsMAndLower import com.jeluchu.jchucomponentscompose.core.extensions.sharedprefs.SharedPrefsHelpers @@ -31,6 +33,11 @@ import com.jeluchu.jchucomponentscompose.utils.broadcast.CustomTabsCopyReceiver import com.jeluchu.jchucomponentscompose.utils.broadcast.ShareBroadcastReceiver import java.io.IOException +/** ---- PERMISSIONS --------------------------------------------------------------------------- **/ + +fun Context.checkSelfPermissionCompat(permission: String) = + ActivityCompat.checkSelfPermission(this, permission) + /** ---- NETWORKS ------------------------------------------------------------------------------ **/ @Suppress("DEPRECATION") @@ -99,10 +106,13 @@ fun Context.addToClipboard(str: CharSequence?) { /** ---- BROWSER TABS -------------------------------------------------------------------------- **/ -/*fun Context.openInCustomTab(string: String) = customTabsWeb(string) +fun Context.openInCustomTab(url: String, colorBar: Int) = customTabsWeb(url, colorBar) @Suppress("DEPRECATION") -private fun Context.customTabsWeb(string: String) { +private fun Context.customTabsWeb( + string: String, + colorBar: Int = R.color.browser_actions_bg_grey +) { try { val share = Intent(this, ShareBroadcastReceiver::class.java) @@ -123,7 +133,7 @@ private fun Context.customTabsWeb(string: String) { "#" + Integer.toHexString( ContextCompat.getColor( this, - R.color.chipBackground + colorBar ) ) ) @@ -150,7 +160,7 @@ private fun Context.customTabsWeb(string: String) { intent.putExtra(Browser.EXTRA_APPLICATION_ID, packageName) startActivity(intent) } -}*/ +} /** ---- PRIVATE METHODS ----------------------------------------------------------------------- **/ diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/file/FileExtensions.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/file/FileExtensions.kt new file mode 100644 index 00000000..d48a2044 --- /dev/null +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/file/FileExtensions.kt @@ -0,0 +1,6 @@ +package com.jeluchu.jchucomponentscompose.core.extensions.file + +import java.io.File + +val File.extension: String + get() = name.substringAfterLast('.', "") diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/ints/IntExtensions.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/ints/IntExtensions.kt index 9aa71350..f9de1f92 100644 --- a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/ints/IntExtensions.kt +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/ints/IntExtensions.kt @@ -6,7 +6,7 @@ import java.text.DecimalFormat fun Int.Companion.empty() = 0 fun Int?.orEmpty() = this ?: Int.empty() - +fun Long.bytesToMeg(): String = (this / (1024L * 1024L)).toString() fun Int.fixedDecimalsTime(): String { val decimalFormat = DecimalFormat("00") diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/lists/ListsExtensions.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/lists/ListsExtensions.kt new file mode 100644 index 00000000..2412f941 --- /dev/null +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/lists/ListsExtensions.kt @@ -0,0 +1,23 @@ +package com.jeluchu.jchucomponentscompose.core.extensions.lists + +import androidx.compose.foundation.MutatePriority +import androidx.compose.foundation.lazy.LazyListState +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.awaitCancellation +import kotlinx.coroutines.launch + +/** Await indefinitely, blocking scrolls **/ +fun LazyListState.disableScrolling(scope: CoroutineScope) { + scope.launch { + scroll(scrollPriority = MutatePriority.PreventUserInput) { + awaitCancellation() + } + } +} + +/** Cancel the previous indefinite "scroll" blocking **/ +fun LazyListState.reenableScrolling(scope: CoroutineScope) { + scope.launch { + scroll(scrollPriority = MutatePriority.PreventUserInput) {} + } +} \ No newline at end of file diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/strings/StringExtensions.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/strings/StringExtensions.kt index e0db7c22..307e6ffb 100644 --- a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/strings/StringExtensions.kt +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/core/extensions/strings/StringExtensions.kt @@ -118,8 +118,6 @@ fun String.decodeBase64toImage(): Bitmap { /** ---- CONVERTERS AND MODIFIERS -------------------------------------------------------------- **/ -fun Long.bytesToMeg(): String = (this / (1024L * 1024L)).toString() - fun String.capitalizeWords(): String = split(" ").joinToString(" ") { it.uppercase(Locale.getDefault()) } @@ -133,6 +131,7 @@ fun String.remove(@RegExp pattern: String) = remove(Regex(pattern, RegexOption.I fun String.remove(regex: Regex) = replace(regex, "") fun String.Companion.empty() = "" +fun CharSequence.isEmpty(): Boolean = length == 0 fun String.isEmpty(): Boolean = length == 0 fun String.replace(): String = replace("-", " ") diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/images/DoubleTapAnimation.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/images/DoubleTapAnimation.kt new file mode 100644 index 00000000..f079b954 --- /dev/null +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/images/DoubleTapAnimation.kt @@ -0,0 +1,86 @@ +package com.jeluchu.jchucomponentscompose.ui.images + +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.animation.core.spring +import androidx.compose.foundation.Image +import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.size +import androidx.compose.material.Icon +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.jeluchu.jchucomponentscompose.core.extensions.ints.empty +import com.jeluchu.jchucomponentscompose.core.extensions.strings.empty + +@Composable +fun DoubleTapAnimation( + imageRemote: String = String.empty(), + imageResources: Int = Int.empty(), + iconResource: Int, + size: Dp = 250.dp, + onDoubleTap: () -> Unit +) { + var isLike by remember { mutableStateOf(false) } + val animatedSize by animateDpAsState( + targetValue = if (isLike) size else 0.dp, + animationSpec = spring( + dampingRatio = Spring.DampingRatioMediumBouncy, + stiffness = 500f + ) + ) + Box(modifier = Modifier.fillMaxSize()) { + + if (imageRemote.isNotEmpty()) { + NetworkImage( + url = imageRemote, + modifier = Modifier + .align(Alignment.Center) + .pointerInput(Unit) { + detectTapGestures( + onDoubleTap = { + isLike = true + onDoubleTap() + } + ) + } + ) + } else { + Image( + painter = painterResource(id = imageResources), + contentDescription = String.empty(), + modifier = Modifier + .align(Alignment.Center) + .pointerInput(Unit) { + detectTapGestures( + onDoubleTap = { + isLike = true + onDoubleTap() + } + ) + }) + } + + + if (isLike) { + Icon( + painterResource(id = iconResource), + tint = Color.White, + modifier = Modifier + .size(animatedSize) + .align(Alignment.Center), + contentDescription = "" + ) + if (animatedSize == size) { + isLike = false + } + } + } +} diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/loaders/PulseLoading.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/loaders/PulseLoading.kt new file mode 100644 index 00000000..bbbc74cf --- /dev/null +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/loaders/PulseLoading.kt @@ -0,0 +1,55 @@ +package com.jeluchu.jchucomponentscompose.ui.loaders + +import androidx.compose.animation.core.* +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.Card +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp + +@Composable +fun PulseLoading( + durationMillis:Int = 1000, + maxPulseSize:Float = 300f, + minPulseSize:Float = 50f, + pulseColor: Color = Color(234,240,246), + centreColor:Color = Color(66,133,244) +){ + val infiniteTransition = rememberInfiniteTransition() + val size by infiniteTransition.animateFloat( + initialValue = minPulseSize, + targetValue = maxPulseSize, + animationSpec = infiniteRepeatable( + animation = tween(durationMillis, easing = LinearEasing), + repeatMode = RepeatMode.Restart + ) + ) + val alpha by infiniteTransition.animateFloat( + initialValue = 1f, + targetValue = 0f, + animationSpec = infiniteRepeatable( + animation = tween(durationMillis, easing = LinearEasing), + repeatMode = RepeatMode.Restart + ) + ) + Box(contentAlignment = Alignment.Center,modifier = Modifier.fillMaxSize()) { + Card( + shape = CircleShape, + modifier = Modifier.size(size.dp).align(Alignment.Center).alpha(alpha), + backgroundColor = pulseColor, + elevation = 0.dp + ) {} + Card(modifier = Modifier + .size(minPulseSize.dp) + .align(Alignment.Center), + shape = CircleShape, + backgroundColor = centreColor){} + } +}