From 3f3aa0127e7afd021e924fc7ea86da2112c0eddc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Mar=C3=ADa?= Date: Wed, 25 Aug 2021 19:52:22 +0200 Subject: [PATCH] Include SplashScreenController, new Composables and Extensions --- .../compposecomponents/MainActivity.kt | 30 ++-- build.gradle | 2 +- jchucomponentscompose/build.gradle | 1 + .../ui/animations/SplashScreenController.kt | 152 ++++++++++++++++++ .../ui/cards/DebutCard.kt | 2 +- .../ui/cards/StoriesCard.kt | 4 +- .../ui/chips/InterestTag.kt | 100 ++++++++++++ .../ui/listview/VerticalGrid.kt | 58 +++++++ .../jchucomponentscompose/ui/theme/Colors.kt | 7 + .../jchucomponentscompose/ui/theme/Type.kt | 43 +++++ .../utils/ScreenCapture.kt | 54 +++++++ 11 files changed, 434 insertions(+), 19 deletions(-) create mode 100644 jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/animations/SplashScreenController.kt create mode 100644 jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/chips/InterestTag.kt create mode 100644 jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/listview/VerticalGrid.kt create mode 100644 jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/theme/Colors.kt create mode 100644 jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/theme/Type.kt create mode 100644 jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/ScreenCapture.kt diff --git a/app/src/main/java/com/jeluchu/compposecomponents/MainActivity.kt b/app/src/main/java/com/jeluchu/compposecomponents/MainActivity.kt index 8f5b332d..1504547b 100644 --- a/app/src/main/java/com/jeluchu/compposecomponents/MainActivity.kt +++ b/app/src/main/java/com/jeluchu/compposecomponents/MainActivity.kt @@ -56,14 +56,14 @@ class MainActivity : ComponentActivity() { StoryCard( modifier = Modifier.animateItem(), title = item.toString(), - iconMainUrl = "https://i.picsum.photos/id/1003/1181/1772.jpg?hmac=oN9fHMXiqe9Zq2RM6XT-RVZkojgPnECWwyEF1RvvTZk", + iconMainUrl = "https://i.picsum.photos/id/1016/3844/2563.jpg?hmac=WEryKFRvTdeae2aUrY-DHscSmZuyYI9jd_-p94stBvc", circleImage = R.drawable.ic_btnfavourite, navigateToScreen = { context.shortToast("Clicked!") } ) } } - if (imageUriState.value != null) { + if (false && imageUriState.value != null) { NetworkImage( modifier = Modifier @@ -76,22 +76,24 @@ class MainActivity : ComponentActivity() { url = imageUriState.value!! ) - } + Button( + onClick = { selectImageLauncher.launch("image/*") }, + modifier = Modifier.padding(vertical = 8.dp) + ) { + androidx.compose.material.Text("Open Gallery") + } - Button( - onClick = { selectImageLauncher.launch("image/*") }, - modifier = Modifier.padding(vertical = 8.dp) - ) { - androidx.compose.material.Text("Open Gallery") - } + DoubleTapAnimation( + "https://i.picsum.photos/id/1016/3844/2563.jpg?hmac=WEryKFRvTdeae2aUrY-DHscSmZuyYI9jd_-p94stBvc", + iconResource = R.drawable.ic_btnfavourite + ) { + // Action when double tap + } - DoubleTapAnimation( - "https://i.picsum.photos/id/1016/3844/2563.jpg?hmac=WEryKFRvTdeae2aUrY-DHscSmZuyYI9jd_-p94stBvc", - iconResource = R.drawable.ic_btnfavourite - ) { - // Action when double tap } + + } } } diff --git a/build.gradle b/build.gradle index 6ed449ef..33521335 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.0.0' + classpath 'com.android.tools.build:gradle:7.0.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.21" } } diff --git a/jchucomponentscompose/build.gradle b/jchucomponentscompose/build.gradle index 0ba4c7c6..af52747b 100644 --- a/jchucomponentscompose/build.gradle +++ b/jchucomponentscompose/build.gradle @@ -48,6 +48,7 @@ dependencies { implementation 'androidx.compose.material:material:1.0.1' implementation 'androidx.compose.ui:ui-tooling-preview:1.0.1' implementation 'androidx.activity:activity-compose:1.3.1' + implementation 'androidx.core:core-splashscreen:1.0.0-alpha01' debugImplementation 'androidx.compose.ui:ui-tooling:1.0.1' implementation 'androidx.compose.material:material-icons-extended:1.0.1' implementation 'androidx.compose.foundation:foundation:1.0.1' diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/animations/SplashScreenController.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/animations/SplashScreenController.kt new file mode 100644 index 00000000..97e10225 --- /dev/null +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/animations/SplashScreenController.kt @@ -0,0 +1,152 @@ +package com.jeluchu.jchucomponentscompose.ui.animations + +import android.animation.AnimatorSet +import android.animation.ObjectAnimator +import android.annotation.SuppressLint +import android.graphics.Path +import android.view.View +import android.view.animation.AnticipateInterpolator +import androidx.core.animation.doOnEnd +import androidx.core.splashscreen.SplashScreen + +/** + * + * Author: @Jeluchu + * + * This class allows you to quickly and easily + * implement a Splash Screen with the new Jetpack API + * + * @param splashScreen you must pass by parameter Activity.installSplashScreen() from [SplashScreen] + * @param defaultExitDuration you will be able to spend a certain amount of time + * for the duration of the animation. By default it is set to 300 + * + */ + +@SuppressLint("CustomSplashScreen") +class SplashScreenController( + private val splashScreen: SplashScreen, + private val defaultExitDuration: Long = 300, +) { + + fun customizeSplashScreenExit(keys: List, onExitExtraActions: () -> Unit = {}) = + splashScreen.setOnExitAnimationListener { splashScreenViewProvider -> + val onExit = { + splashScreenViewProvider.remove() + onExitExtraActions() + } + showSplashExitAnimator(splashScreenViewProvider.view, keys, onExit) + showSplashIconExitAnimator(splashScreenViewProvider.iconView, onExit) + } + + private fun showSplashExitAnimator( + splashScreenView: View, + keys: List, + onExit: () -> Unit = {}) { + + AnimatorSet().run { + duration = defaultExitDuration + interpolator = AnticipateInterpolator() + splashScreenView.apply { + playTogether(getAnimation(keys, splashScreenView).toList()) + } + doOnEnd { onExit() } + start() + } + + } + + private fun showSplashIconExitAnimator(iconView: View, onExit: () -> Unit = {}) { + + val alphaOut = ObjectAnimator.ofFloat( + iconView, + View.ALPHA, + 1f, + 0f + ) + + val scaleOut = ObjectAnimator.ofFloat( + iconView, + View.SCALE_X, + View.SCALE_Y, + Path().apply { + moveTo(1.0f, 1.0f) + lineTo(0.3f, 0.3f) + } + ) + + val slideUp = ObjectAnimator.ofFloat( + iconView, + View.TRANSLATION_Y, + 0f, + -(iconView.height).toFloat() * 2.25f + ) + + AnimatorSet().run { + interpolator = AnticipateInterpolator() + duration = defaultExitDuration + + playTogether(alphaOut, scaleOut, slideUp) + doOnEnd { onExit() } + start() + } + + } + + private fun getAnimation(keys: List, splashScreenView: View): MutableList { + + val listAnimations: MutableList = mutableListOf() + + for (key in keys) { + val animation = when(key) { + SplashAnimations.SlideUp -> ObjectAnimator.ofFloat( + splashScreenView, + View.TRANSLATION_Y, + 0f, + -splashScreenView.height.toFloat() + ) + SplashAnimations.SlideLeft -> ObjectAnimator.ofFloat( + splashScreenView, + View.TRANSLATION_X, + 0f, + -splashScreenView.width.toFloat() + ) + SplashAnimations.ScaleXOut -> ObjectAnimator.ofFloat( + splashScreenView, + View.SCALE_X, + 1.0f, + 0f + ) + SplashAnimations.AlphaOut -> ObjectAnimator.ofFloat( + splashScreenView, + View.ALPHA, + 1f, + 0f + ) + SplashAnimations.ScaleOut -> ObjectAnimator.ofFloat( + splashScreenView, + View.SCALE_X, + View.SCALE_Y, + Path().apply { + moveTo(1.0f, 1.0f) + lineTo(0f, 0f) + } + ) + } + + listAnimations.add(animation) + + } + + return listAnimations + + } + +} + +enum class SplashAnimations { + SlideUp, + SlideLeft, + ScaleXOut, + AlphaOut, + ScaleOut +} \ No newline at end of file diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/cards/DebutCard.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/cards/DebutCard.kt index 0c5560fc..68069cf0 100644 --- a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/cards/DebutCard.kt +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/cards/DebutCard.kt @@ -39,7 +39,7 @@ import com.jeluchu.jchucomponentscompose.ui.images.NetworkImage @Composable fun DebutCard( - modifier: Modifier, + modifier: Modifier = Modifier, title: String, image: String, iconDebut: String, diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/cards/StoriesCard.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/cards/StoriesCard.kt index 6b57febb..2210a900 100644 --- a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/cards/StoriesCard.kt +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/cards/StoriesCard.kt @@ -71,13 +71,11 @@ fun StoryCard( NetworkImage( url = iconMainUrl, modifier = Modifier - .size(23.dp) - .padding(start = 7.dp) .constrainAs(storyImg) { linkTo(parent.start, parent.end) linkTo(parent.top, parent.bottom) }, - contentScale = ContentScale.Fit, + contentScale = ContentScale.Crop, ) } else if (iconMainResource.isNotEmpty()) { diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/chips/InterestTag.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/chips/InterestTag.kt new file mode 100644 index 00000000..c48cdac9 --- /dev/null +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/chips/InterestTag.kt @@ -0,0 +1,100 @@ +package com.jeluchu.jchucomponentscompose.ui.chips + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.jeluchu.jchucomponentscompose.ui.theme.green200 +import com.jeluchu.jchucomponentscompose.ui.theme.green700 +import com.jeluchu.jchucomponentscompose.ui.theme.typography + +/** + * + * Author: @Jeluchu + * + * This component is similar to the Chips, + * in which you can display a text or a text and an icon + * + * @sample ChipTagViewPreview + * + * @param modifier modifier that will be used to change the color, size... + * @param title text to be displayed on the chip + * @param colors color of the text and background + * @param shape type of shape desired for this chip + * @param style style of text to be displayed + * @param navigateToScreen action to be performed after pressing + * + */ + +@Composable +fun InterestTag( + modifier: Modifier = Modifier, + title: String, + colors: TagColors = TagDefaults.tagColors(), + shape: Shape = RoundedCornerShape(4.dp), + style: TextStyle = typography.body2.copy(fontWeight = FontWeight.Bold), + navigateToScreen: () -> Unit = {} +) { + val tagModifier = modifier + .padding(4.dp) + .clickable(onClick = navigateToScreen) + .clip(shape = shape) + .background(colors.backgroundColor(enabled = true).value) + .padding(horizontal = 8.dp, vertical = 4.dp) + Text( + text = title, + color = colors.contentColor(enabled = true).value, + modifier = tagModifier, + style = style + ) +} + +@Stable +interface TagColors { + @Composable + fun backgroundColor(enabled: Boolean): State + + @Composable + fun contentColor(enabled: Boolean): State +} + +@Immutable +private class DefaultTagColors( + private val backgroundColor: Color, + private val contentColor: Color +) : TagColors { + @Composable + override fun backgroundColor(enabled: Boolean): State { + return rememberUpdatedState(newValue = backgroundColor) + } + + @Composable + override fun contentColor(enabled: Boolean): State { + return rememberUpdatedState(newValue = contentColor) + } +} + +object TagDefaults { + @Composable + fun tagColors( + backgroundColor: Color = green200.copy(alpha = .2f), + contentColor: Color = green700 + ): TagColors = DefaultTagColors(backgroundColor = backgroundColor, contentColor = contentColor) +} + + +@Preview +@Composable +fun InterestTagPreview() { + InterestTag(title = "Name") +} \ No newline at end of file diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/listview/VerticalGrid.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/listview/VerticalGrid.kt new file mode 100644 index 00000000..4bfc146e --- /dev/null +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/listview/VerticalGrid.kt @@ -0,0 +1,58 @@ +package com.jeluchu.jchucomponentscompose.ui.listview + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.Layout + +/** + * + * Author: @Jeluchu + * + * A custom layout to recreate a GridLayout which lays elements + * out vertically in evenly sized columns + * + * @param modifier modifier that will be used to change the color, size... + * @param columns number of columns to be displayed + * @param content the item to be painted within the layout + * + */ + +@Composable +fun VerticalGrid( + modifier: Modifier = Modifier, + columns: Int = 2, + content: @Composable () -> Unit +) { + Layout( + content = content, + modifier = modifier + ) { measurables, constraints -> + val itemWidth = constraints.maxWidth / columns + val itemConstraints = constraints.copy( + minWidth = itemWidth, + maxWidth = itemWidth + ) + val placeables = measurables.map { it.measure(itemConstraints) } + val columnHeights = Array(columns) { 0 } + placeables.forEachIndexed { index, placeable -> + val column = index % columns + columnHeights[column] += placeable.height + } + val height = (columnHeights.maxOrNull() ?: constraints.minHeight) + .coerceAtMost(constraints.maxHeight) + layout( + width = constraints.maxWidth, + height = height + ) { + val columnY = Array(columns) { 0 } + placeables.forEachIndexed { index, placeable -> + val column = index % columns + placeable.place( + x = column * itemWidth, + y = columnY[column] + ) + columnY[column] += placeable.height + } + } + } +} \ No newline at end of file diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/theme/Colors.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/theme/Colors.kt new file mode 100644 index 00000000..c8dae27b --- /dev/null +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/theme/Colors.kt @@ -0,0 +1,7 @@ +package com.jeluchu.jchucomponentscompose.ui.theme + +import androidx.compose.ui.graphics.Color + +val green200 = Color(0xffa5d6a7) +val green500 = Color(0xff4caf50) +val green700 = Color(0xff388e3c) \ No newline at end of file diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/theme/Type.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/theme/Type.kt new file mode 100644 index 00000000..a7cc4c4a --- /dev/null +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/ui/theme/Type.kt @@ -0,0 +1,43 @@ +package com.jeluchu.jchucomponentscompose.ui.theme + +import androidx.compose.material.Typography +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +val typography = Typography( + body1 = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp + ), + body2 = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 14.sp + ), + button = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.W500, + fontSize = 14.sp + ), + caption = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 12.sp, + ), + subtitle1 = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + color = Color.Gray + ), + subtitle2 = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 14.sp, + color = Color.Gray + ), +) \ No newline at end of file diff --git a/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/ScreenCapture.kt b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/ScreenCapture.kt new file mode 100644 index 00000000..1d197f49 --- /dev/null +++ b/jchucomponentscompose/src/main/java/com/jeluchu/jchucomponentscompose/utils/ScreenCapture.kt @@ -0,0 +1,54 @@ +package com.jeluchu.jchucomponentscompose.utils + +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Rect +import android.os.Handler +import android.os.Looper +import android.view.PixelCopy +import android.view.View +import android.view.Window +import androidx.core.splashscreen.SplashScreen +import com.jeluchu.jchucomponentscompose.core.extensions.packageutils.buildIsOAndUp + +/** + * + * Author: @Jeluchu + * + * With this class you will be able to take screenshots + * of the current screen quickly and easily + * + */ + +class ScreenCapture { + + fun captureView(view: View, window: Window, bitmapCallback: (Bitmap)->Unit) { + if (buildIsOAndUp) { + + val bitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888) + val location = IntArray(2) + view.getLocationInWindow(location) + PixelCopy.request(window, + Rect(location[0], location[1], location[0] + view.width, location[1] + view.height), + bitmap, + { + if (it == PixelCopy.SUCCESS) { + bitmapCallback.invoke(bitmap) + } + }, Handler(Looper.getMainLooper()) + ) + + } else { + + val tBitmap = Bitmap.createBitmap( + view.width, view.height, Bitmap.Config.RGB_565 + ) + val canvas = Canvas(tBitmap) + view.draw(canvas) + canvas.setBitmap(null) + bitmapCallback.invoke(tBitmap) + + } + } + +} \ No newline at end of file