From 74191e2dc21718477ddffa56d053789825d3d8b6 Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Mon, 19 Feb 2024 13:55:51 +0100 Subject: [PATCH 1/2] Migrated to Jetpack Compose Signed-off-by: Arnau Mora Gras --- .../davdroid/ui/intro/WelcomeFragment.kt | 284 ++++++++++++++++-- .../main/res/layout-land/intro_welcome.xml | 71 ----- app/src/main/res/layout/intro_welcome.xml | 67 ----- 3 files changed, 256 insertions(+), 166 deletions(-) delete mode 100644 app/src/main/res/layout-land/intro_welcome.xml delete mode 100644 app/src/main/res/layout/intro_welcome.xml diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/intro/WelcomeFragment.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/intro/WelcomeFragment.kt index 59492c891..723cd6d04 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/intro/WelcomeFragment.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/intro/WelcomeFragment.kt @@ -5,12 +5,49 @@ package at.bitfire.davdroid.ui.intro import android.content.Context +import android.content.res.Configuration import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.tween +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawWithContent +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.platform.LocalInspectionMode +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.fragment.app.Fragment -import at.bitfire.davdroid.databinding.IntroWelcomeBinding +import at.bitfire.davdroid.R import dagger.Binds import dagger.Module import dagger.hilt.InstallIn @@ -20,39 +57,230 @@ import javax.inject.Inject class WelcomeFragment: Fragment() { - private var _binding: IntroWelcomeBinding? = null - private val binding get() = _binding!! - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { - _binding = IntroWelcomeBinding.inflate(inflater, container, false) - - if (true /* ose build */) { - binding.logo.apply { - alpha = 0f - animate() - .alpha(1f) - .setDuration(300) - } - binding.yourDataYourChoice.apply { - translationX = -1000f - animate() - .translationX(0f) - .setDuration(300) - } - binding.takeControl.apply { - translationX = 1000f - animate() - .translationX(0f) - .setDuration(300) + return ComposeView(requireContext()).apply { + setContent { + val configuration = LocalConfiguration.current + when (configuration.orientation) { + Configuration.ORIENTATION_LANDSCAPE -> { + ContentLandscape() + } + else -> { + ContentPortrait() + } + } } } + } + + @Preview(showSystemUi = true) + @Composable + private fun ContentPortrait() { + val isPreview = LocalInspectionMode.current + var animate by remember { mutableStateOf(false) } + val logoAlpha by animateFloatAsState( + targetValue = if (isPreview) 1f else if (animate) 1f else 0f, + label = "Animate the alpha of the DAVx5 logo", + animationSpec = tween(300) + ) + val offset1 by animateDpAsState( + targetValue = if (isPreview) 0.dp else if (animate) 0.dp else (-1000).dp, + label = "Animate the offset of the 'Your data, your choice' text", + animationSpec = tween(300) + ) + val offset2 by animateDpAsState( + targetValue = if (isPreview) 0.dp else if (animate) 0.dp else 1000.dp, + label = "Animate the offset of the 'Take control of your data' text", + animationSpec = tween(300) + ) + + LaunchedEffect(Unit) { + animate = true // Trigger the animation + } + + Column( + modifier = Modifier + .fillMaxSize() + .background(color = colorResource(R.color.primaryDarkColor)), + ) { + Image( + painter = painterResource(R.drawable.ic_launcher_foreground), + contentDescription = null, + modifier = Modifier + .fillMaxWidth() + .padding(top = 48.dp) + .weight(2f), + alpha = logoAlpha + ) + + val textStyleSubtitle1 = MaterialTheme.typography.subtitle1.copy(fontSize = 34.sp) + var textStyle1 by remember { mutableStateOf(textStyleSubtitle1) } + var readyToDraw1 by remember { mutableStateOf(false) } + Text( + text = stringResource(R.string.intro_slogan1), + color = Color.White, + softWrap = false, + maxLines = 1, + style = textStyle1, + modifier = Modifier + .fillMaxWidth() + .weight(1f) + .padding(horizontal = 32.dp) + .drawWithContent { + if (readyToDraw1) drawContent() + } + .wrapContentHeight(Alignment.Bottom) + .offset(x = offset1), + onTextLayout = { textLayoutResult -> + if (textLayoutResult.didOverflowWidth) { + textStyle1 = textStyle1.copy(fontSize = textStyle1.fontSize * 0.9) + } else { + readyToDraw1 = true + } + } + ) - return binding.root + val textStyleH5 = MaterialTheme.typography.h5.copy(fontSize = 48.sp) + var textStyle2 by remember { mutableStateOf(textStyleSubtitle1) } + var readyToDraw2 by remember { mutableStateOf(false) } + Text( + text = stringResource(R.string.intro_slogan2), + color = Color.White, + softWrap = false, + maxLines = 1, + style = textStyleH5, + textAlign = TextAlign.Center, + modifier = Modifier + .fillMaxWidth() + .weight(1f) + .padding(horizontal = 32.dp) + .padding( + bottom = dimensionResource( + com.github.appintro.R.dimen.appintro2_bottombar_height + ) + ) + .drawWithContent { + if (readyToDraw2) drawContent() + } + .offset(x = offset2), + onTextLayout = { textLayoutResult -> + if (textLayoutResult.didOverflowWidth) { + textStyle2 = textStyle2.copy(fontSize = textStyle2.fontSize * 0.9) + } else { + readyToDraw2 = true + } + } + ) + } } - override fun onDestroyView() { - super.onDestroyView() - _binding = null + @Preview( + showSystemUi = true, + device = "spec:width=411dp,height=891dp,dpi=420,isRound=false,chinSize=0dp,orientation=landscape" + ) + @Composable + private fun ContentLandscape() { + val isPreview = LocalInspectionMode.current + var animate by remember { mutableStateOf(false) } + val logoAlpha by animateFloatAsState( + targetValue = if (isPreview) 1f else if (animate) 1f else 0f, + label = "Animate the alpha of the DAVx5 logo", + animationSpec = tween(300) + ) + val offset1 by animateDpAsState( + targetValue = if (isPreview) 0.dp else if (animate) 0.dp else (-1000).dp, + label = "Animate the offset of the 'Your data, your choice' text", + animationSpec = tween(300) + ) + val offset2 by animateDpAsState( + targetValue = if (isPreview) 0.dp else if (animate) 0.dp else 1000.dp, + label = "Animate the offset of the 'Take control of your data' text", + animationSpec = tween(300) + ) + + LaunchedEffect(Unit) { + animate = true // Trigger the animation + } + + Row( + modifier = Modifier + .fillMaxSize() + .background(color = colorResource(R.color.primaryDarkColor)) + .padding( + bottom = dimensionResource( + com.github.appintro.R.dimen.appintro2_bottombar_height + ) + ), + verticalAlignment = Alignment.CenterVertically + ) { + Image( + painter = painterResource(R.drawable.ic_launcher_foreground), + contentDescription = null, + modifier = Modifier + .fillMaxHeight() + .padding(top = 48.dp) + .weight(1f), + alpha = logoAlpha + ) + + Column( + modifier = Modifier + .weight(1f) + .padding(end = 24.dp) + ) { + val textStyleSubtitle1 = MaterialTheme.typography.subtitle1.copy(fontSize = 34.sp) + var textStyle1 by remember { mutableStateOf(textStyleSubtitle1) } + var readyToDraw1 by remember { mutableStateOf(false) } + Text( + text = stringResource(R.string.intro_slogan1), + color = Color.White, + softWrap = false, + maxLines = 1, + style = textStyle1, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 32.dp) + .drawWithContent { + if (readyToDraw1) drawContent() + } + .wrapContentHeight(Alignment.Bottom) + .offset(x = offset1), + onTextLayout = { textLayoutResult -> + if (textLayoutResult.didOverflowWidth) { + textStyle1 = textStyle1.copy(fontSize = textStyle1.fontSize * 0.9) + } else { + readyToDraw1 = true + } + } + ) + + val textStyleH5 = MaterialTheme.typography.h5.copy(fontSize = 48.sp) + var textStyle2 by remember { mutableStateOf(textStyleSubtitle1) } + var readyToDraw2 by remember { mutableStateOf(false) } + Text( + text = stringResource(R.string.intro_slogan2), + color = Color.White, + softWrap = false, + maxLines = 1, + style = textStyleH5, + textAlign = TextAlign.Center, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 32.dp) + .drawWithContent { + if (readyToDraw2) drawContent() + } + .offset(x = offset2), + onTextLayout = { textLayoutResult -> + if (textLayoutResult.didOverflowWidth) { + textStyle2 = textStyle2.copy(fontSize = textStyle2.fontSize * 0.9) + } else { + readyToDraw2 = true + } + } + ) + } + } } diff --git a/app/src/main/res/layout-land/intro_welcome.xml b/app/src/main/res/layout-land/intro_welcome.xml deleted file mode 100644 index 16bc0ef1d..000000000 --- a/app/src/main/res/layout-land/intro_welcome.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/intro_welcome.xml b/app/src/main/res/layout/intro_welcome.xml deleted file mode 100644 index adc3c4279..000000000 --- a/app/src/main/res/layout/intro_welcome.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - From 85a42a6a769ff9d6a2cb1c2fba0ccbf6d1a2936f Mon Sep 17 00:00:00 2001 From: Ricki Hirner Date: Wed, 28 Feb 2024 17:26:53 +0100 Subject: [PATCH 2/2] Simplify layout, remove animations --- .../davdroid/ui/intro/WelcomeFragment.kt | 171 +++--------------- 1 file changed, 22 insertions(+), 149 deletions(-) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/intro/WelcomeFragment.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/intro/WelcomeFragment.kt index 723cd6d04..f6a5f725c 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/intro/WelcomeFragment.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/intro/WelcomeFragment.kt @@ -10,9 +10,6 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.compose.animation.core.animateDpAsState -import androidx.compose.animation.core.animateFloatAsState -import androidx.compose.animation.core.tween import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column @@ -20,24 +17,16 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.drawWithContent import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.LocalConfiguration -import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.painterResource @@ -60,15 +49,10 @@ class WelcomeFragment: Fragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { return ComposeView(requireContext()).apply { setContent { - val configuration = LocalConfiguration.current - when (configuration.orientation) { - Configuration.ORIENTATION_LANDSCAPE -> { - ContentLandscape() - } - else -> { - ContentPortrait() - } - } + if (LocalConfiguration.current.orientation == Configuration.ORIENTATION_LANDSCAPE) + ContentLandscape() + else + ContentPortrait() } } } @@ -76,28 +60,6 @@ class WelcomeFragment: Fragment() { @Preview(showSystemUi = true) @Composable private fun ContentPortrait() { - val isPreview = LocalInspectionMode.current - var animate by remember { mutableStateOf(false) } - val logoAlpha by animateFloatAsState( - targetValue = if (isPreview) 1f else if (animate) 1f else 0f, - label = "Animate the alpha of the DAVx5 logo", - animationSpec = tween(300) - ) - val offset1 by animateDpAsState( - targetValue = if (isPreview) 0.dp else if (animate) 0.dp else (-1000).dp, - label = "Animate the offset of the 'Your data, your choice' text", - animationSpec = tween(300) - ) - val offset2 by animateDpAsState( - targetValue = if (isPreview) 0.dp else if (animate) 0.dp else 1000.dp, - label = "Animate the offset of the 'Take control of your data' text", - animationSpec = tween(300) - ) - - LaunchedEffect(Unit) { - animate = true // Trigger the animation - } - Column( modifier = Modifier .fillMaxSize() @@ -109,46 +71,26 @@ class WelcomeFragment: Fragment() { modifier = Modifier .fillMaxWidth() .padding(top = 48.dp) - .weight(2f), - alpha = logoAlpha + .weight(2f) ) - val textStyleSubtitle1 = MaterialTheme.typography.subtitle1.copy(fontSize = 34.sp) - var textStyle1 by remember { mutableStateOf(textStyleSubtitle1) } - var readyToDraw1 by remember { mutableStateOf(false) } Text( text = stringResource(R.string.intro_slogan1), color = Color.White, - softWrap = false, - maxLines = 1, - style = textStyle1, + style = MaterialTheme.typography.subtitle1.copy(fontSize = 34.sp), + lineHeight = 38.sp, + textAlign = TextAlign.Center, modifier = Modifier .fillMaxWidth() - .weight(1f) - .padding(horizontal = 32.dp) - .drawWithContent { - if (readyToDraw1) drawContent() - } - .wrapContentHeight(Alignment.Bottom) - .offset(x = offset1), - onTextLayout = { textLayoutResult -> - if (textLayoutResult.didOverflowWidth) { - textStyle1 = textStyle1.copy(fontSize = textStyle1.fontSize * 0.9) - } else { - readyToDraw1 = true - } - } + .wrapContentHeight() + .padding(horizontal = 16.dp) ) - val textStyleH5 = MaterialTheme.typography.h5.copy(fontSize = 48.sp) - var textStyle2 by remember { mutableStateOf(textStyleSubtitle1) } - var readyToDraw2 by remember { mutableStateOf(false) } Text( text = stringResource(R.string.intro_slogan2), color = Color.White, - softWrap = false, - maxLines = 1, - style = textStyleH5, + style = MaterialTheme.typography.h5.copy(fontSize = 48.sp), + lineHeight = 52.sp, textAlign = TextAlign.Center, modifier = Modifier .fillMaxWidth() @@ -159,17 +101,6 @@ class WelcomeFragment: Fragment() { com.github.appintro.R.dimen.appintro2_bottombar_height ) ) - .drawWithContent { - if (readyToDraw2) drawContent() - } - .offset(x = offset2), - onTextLayout = { textLayoutResult -> - if (textLayoutResult.didOverflowWidth) { - textStyle2 = textStyle2.copy(fontSize = textStyle2.fontSize * 0.9) - } else { - readyToDraw2 = true - } - } ) } } @@ -180,28 +111,6 @@ class WelcomeFragment: Fragment() { ) @Composable private fun ContentLandscape() { - val isPreview = LocalInspectionMode.current - var animate by remember { mutableStateOf(false) } - val logoAlpha by animateFloatAsState( - targetValue = if (isPreview) 1f else if (animate) 1f else 0f, - label = "Animate the alpha of the DAVx5 logo", - animationSpec = tween(300) - ) - val offset1 by animateDpAsState( - targetValue = if (isPreview) 0.dp else if (animate) 0.dp else (-1000).dp, - label = "Animate the offset of the 'Your data, your choice' text", - animationSpec = tween(300) - ) - val offset2 by animateDpAsState( - targetValue = if (isPreview) 0.dp else if (animate) 0.dp else 1000.dp, - label = "Animate the offset of the 'Take control of your data' text", - animationSpec = tween(300) - ) - - LaunchedEffect(Unit) { - animate = true // Trigger the animation - } - Row( modifier = Modifier .fillMaxSize() @@ -218,66 +127,30 @@ class WelcomeFragment: Fragment() { contentDescription = null, modifier = Modifier .fillMaxHeight() - .padding(top = 48.dp) - .weight(1f), - alpha = logoAlpha + .weight(1f) ) Column( modifier = Modifier - .weight(1f) - .padding(end = 24.dp) + .padding(horizontal = 32.dp) + .weight(2f) ) { - val textStyleSubtitle1 = MaterialTheme.typography.subtitle1.copy(fontSize = 34.sp) - var textStyle1 by remember { mutableStateOf(textStyleSubtitle1) } - var readyToDraw1 by remember { mutableStateOf(false) } Text( text = stringResource(R.string.intro_slogan1), color = Color.White, - softWrap = false, - maxLines = 1, - style = textStyle1, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 32.dp) - .drawWithContent { - if (readyToDraw1) drawContent() - } - .wrapContentHeight(Alignment.Bottom) - .offset(x = offset1), - onTextLayout = { textLayoutResult -> - if (textLayoutResult.didOverflowWidth) { - textStyle1 = textStyle1.copy(fontSize = textStyle1.fontSize * 0.9) - } else { - readyToDraw1 = true - } - } + style = MaterialTheme.typography.subtitle1.copy(fontSize = 34.sp), + lineHeight = 38.sp, + textAlign = TextAlign.Center, + modifier = Modifier.fillMaxWidth() ) - val textStyleH5 = MaterialTheme.typography.h5.copy(fontSize = 48.sp) - var textStyle2 by remember { mutableStateOf(textStyleSubtitle1) } - var readyToDraw2 by remember { mutableStateOf(false) } Text( text = stringResource(R.string.intro_slogan2), color = Color.White, - softWrap = false, - maxLines = 1, - style = textStyleH5, + style = MaterialTheme.typography.h5.copy(fontSize = 48.sp), + lineHeight = 52.sp, textAlign = TextAlign.Center, - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 32.dp) - .drawWithContent { - if (readyToDraw2) drawContent() - } - .offset(x = offset2), - onTextLayout = { textLayoutResult -> - if (textLayoutResult.didOverflowWidth) { - textStyle2 = textStyle2.copy(fontSize = textStyle2.fontSize * 0.9) - } else { - readyToDraw2 = true - } - } + modifier = Modifier.fillMaxWidth() ) } }