From 5bf9049d9f878854d8f11828ad056043e74a545a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Saleniuk?= <30429749+saleniuk@users.noreply.github.com> Date: Tue, 21 Jan 2025 13:15:45 +0100 Subject: [PATCH] feat: adjust new login skeleton to edge-to-edge [WPB-14337] (#3812) --- app/build.gradle.kts | 1 - .../com/wire/android/ui/WireActivity.kt | 21 ++++++- .../ui/authentication/login/LoginScreen.kt | 28 +++++----- .../authentication/login/NewLoginContainer.kt | 55 ++++++++++++++----- .../login/WireAuthBackgroundComponent.kt | 36 ++++++------ .../login/email/LoginEmailScreen.kt | 19 +++++-- .../authentication/start/StartLoginScreen.kt | 32 ++++++----- .../authentication/welcome/WelcomeScreen.kt | 21 ++++--- .../ui/common/topappbar/CommonTopAppBar.kt | 50 +++++++++++++---- .../navigation/style/BackgroundStyle.kt | 26 +++++++++ .../style/NavigationAnimationStyles.kt | 14 ++++- core/ui-common/build.gradle.kts | 4 +- .../ui/common/preview/EdgeToEdgePreview.kt | 51 +++++++++++++++++ gradle/libs.versions.toml | 7 ++- 14 files changed, 272 insertions(+), 93 deletions(-) create mode 100644 core/navigation/src/main/kotlin/com/wire/android/navigation/style/BackgroundStyle.kt create mode 100644 core/ui-common/src/main/kotlin/com/wire/android/ui/common/preview/EdgeToEdgePreview.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 77fc097d467..03975fea264 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -205,7 +205,6 @@ dependencies { ksp(libs.compose.destinations.ksp) // Accompanist - implementation(libs.accompanist.systemUI) implementation(libs.accompanist.placeholder) implementation(libs.androidx.paging3) diff --git a/app/src/main/kotlin/com/wire/android/ui/WireActivity.kt b/app/src/main/kotlin/com/wire/android/ui/WireActivity.kt index ae25831bedc..3846fd9ba8e 100644 --- a/app/src/main/kotlin/com/wire/android/ui/WireActivity.kt +++ b/app/src/main/kotlin/com/wire/android/ui/WireActivity.kt @@ -39,6 +39,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.runtime.rememberUpdatedState @@ -55,6 +56,7 @@ import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.NavController +import androidx.navigation.compose.currentBackStackEntryAsState import com.ramcosta.composedestinations.spec.Route import com.wire.android.BuildConfig import com.wire.android.R @@ -69,7 +71,10 @@ import com.wire.android.navigation.MainNavHost import com.wire.android.navigation.NavigationCommand import com.wire.android.navigation.Navigator import com.wire.android.navigation.rememberNavigator +import com.wire.android.navigation.style.BackgroundStyle +import com.wire.android.navigation.style.BackgroundType import com.wire.android.ui.authentication.login.LoginNavArgs +import com.wire.android.ui.authentication.login.WireAuthBackgroundLayout import com.wire.android.ui.calling.getIncomingCallIntent import com.wire.android.ui.calling.getOutgoingCallIntent import com.wire.android.ui.calling.ongoing.getOngoingCallIntent @@ -233,13 +238,25 @@ class WireActivity : AppCompatActivity() { LocalActivity provides this ) { WireTheme { + val navigator = rememberNavigator(this@WireActivity::finish) + val currentBackStackEntryState = navigator.navController.currentBackStackEntryAsState() + val backgroundType by remember { + derivedStateOf { + currentBackStackEntryState.value?.appDestination()?.style.let { + (it as? BackgroundStyle)?.backgroundType() ?: BackgroundType.Default + } + } + } + if (backgroundType == BackgroundType.Auth) { + WireAuthBackgroundLayout() + } Column( modifier = Modifier .semantics { testTagsAsResourceId = true } ) { - val navigator = rememberNavigator(this@WireActivity::finish) WireTopAppBar( commonTopAppBarState = commonTopAppBarViewModel.state, + backgroundType = backgroundType, ) CompositionLocalProvider(LocalNavigator provides navigator) { MainNavHost( @@ -263,11 +280,13 @@ class WireActivity : AppCompatActivity() { @Composable private fun WireTopAppBar( commonTopAppBarState: CommonTopAppBarState, + backgroundType: BackgroundType, modifier: Modifier = Modifier, ) { CommonTopAppBar( modifier = modifier, commonTopAppBarState = commonTopAppBarState, + backgroundType = backgroundType, onReturnToCallClick = { establishedCall -> getOngoingCallIntent( this@WireActivity, diff --git a/app/src/main/kotlin/com/wire/android/ui/authentication/login/LoginScreen.kt b/app/src/main/kotlin/com/wire/android/ui/authentication/login/LoginScreen.kt index a73d51e4b16..924fffd6005 100644 --- a/app/src/main/kotlin/com/wire/android/ui/authentication/login/LoginScreen.kt +++ b/app/src/main/kotlin/com/wire/android/ui/authentication/login/LoginScreen.kt @@ -22,8 +22,8 @@ import androidx.annotation.StringRes import androidx.compose.animation.AnimatedContent import androidx.compose.animation.togetherWith import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -38,6 +38,7 @@ import com.wire.android.navigation.BackStackMode import com.wire.android.navigation.NavigationCommand import com.wire.android.navigation.Navigator import com.wire.android.navigation.WireDestination +import com.wire.android.navigation.style.AuthSlideNavigationAnimation import com.wire.android.navigation.style.TransitionAnimationType import com.wire.android.ui.authentication.ServerTitle import com.wire.android.ui.authentication.login.email.LoginEmailScreen @@ -62,7 +63,8 @@ import com.wire.android.util.ui.PreviewMultipleThemes @RootNavGraph @WireDestination( - navArgsDelegate = LoginNavArgs::class + navArgsDelegate = LoginNavArgs::class, + style = AuthSlideNavigationAnimation::class, ) @Composable fun LoginScreen( @@ -76,7 +78,6 @@ fun LoginScreen( onNavigateBack = navigator::navigateBack ) { LoginContent( - onBackPressed = navigator::navigateBack, onSuccess = { initialSyncCompleted, isE2EIRequired -> val destination = if (isE2EIRequired) E2EIEnrollmentScreenDestination else if (initialSyncCompleted) HomeScreenDestination @@ -88,22 +89,19 @@ fun LoginScreen( navigator.navigate(NavigationCommand(RemoveDeviceScreenDestination, BackStackMode.CLEAR_WHOLE)) }, loginEmailViewModel = loginEmailViewModel, - ssoLoginResult = loginNavArgs.ssoLoginResult ) } } @Composable private fun LoginContent( - onBackPressed: () -> Unit, onSuccess: (initialSyncCompleted: Boolean, isE2EIRequired: Boolean) -> Unit, onRemoveDeviceNeeded: () -> Unit, loginEmailViewModel: LoginEmailViewModel, - ssoLoginResult: DeepLinkResult.SSOLogin? ) { Column( modifier = Modifier - .fillMaxHeight(0.6f) + .wrapContentHeight() .fillMaxWidth() ) { /* @@ -118,7 +116,7 @@ private fun LoginContent( if (isCodeInputNecessary) { LoginEmailVerificationCodeScreen(loginEmailViewModel) } else { - MainLoginContent(onBackPressed, onSuccess, onRemoveDeviceNeeded, loginEmailViewModel, ssoLoginResult) + MainLoginContent(onSuccess, onRemoveDeviceNeeded, loginEmailViewModel) } } } @@ -127,13 +125,10 @@ private fun LoginContent( @Suppress("UnusedParameter") @Composable private fun MainLoginContent( - onBackPressed: () -> Unit, onSuccess: (initialSyncCompleted: Boolean, isE2EIRequired: Boolean) -> Unit, onRemoveDeviceNeeded: () -> Unit, loginEmailViewModel: LoginEmailViewModel, - ssoLoginResult: DeepLinkResult.SSOLogin? ) { - val ssoDisabledWithProxyDialogState = rememberVisibilityState() FeatureDisabledWithProxyDialogContent(dialogState = ssoDisabledWithProxyDialogState) @@ -144,7 +139,12 @@ private fun MainLoginContent( ) VerticalSpace.x8() } - LoginEmailScreen(onSuccess, onRemoveDeviceNeeded, loginEmailViewModel) + LoginEmailScreen( + onSuccess = onSuccess, + onRemoveDeviceNeeded = onRemoveDeviceNeeded, + loginEmailViewModel = loginEmailViewModel, + fillMaxHeight = false, + ) } @Composable @@ -270,14 +270,12 @@ data class LoginDialogErrorData( @PreviewMultipleThemes @Composable -private fun PreviewLoginScreen() = WireTheme { +private fun PreviewNewLoginEmailScreen() = WireTheme { WireTheme { MainLoginContent( - onBackPressed = {}, onSuccess = { _, _ -> }, onRemoveDeviceNeeded = {}, loginEmailViewModel = hiltViewModel(), - ssoLoginResult = null ) } } diff --git a/app/src/main/kotlin/com/wire/android/ui/authentication/login/NewLoginContainer.kt b/app/src/main/kotlin/com/wire/android/ui/authentication/login/NewLoginContainer.kt index 6feb8fe84e9..d1aaaf58661 100644 --- a/app/src/main/kotlin/com/wire/android/ui/authentication/login/NewLoginContainer.kt +++ b/app/src/main/kotlin/com/wire/android/ui/authentication/login/NewLoginContainer.kt @@ -19,25 +19,31 @@ package com.wire.android.ui.authentication.login import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import com.wire.android.ui.authentication.login.WireAuthBackgroundLayout import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.style.TextAlign +import com.wire.android.ui.common.bottomsheet.WireBottomSheetDefaults import com.wire.android.ui.common.colorsScheme import com.wire.android.ui.common.dimensions +import com.wire.android.ui.common.preview.EdgeToEdgePreview import com.wire.android.ui.common.scaffold.WireScaffold import com.wire.android.ui.common.spacers.VerticalSpace import com.wire.android.ui.theme.WireTheme @@ -61,22 +67,24 @@ private fun NewLoginContent( onNavigateBack: () -> Unit, content: @Composable () -> Unit = { } ) { + NavigationBarBackground() WireScaffold( + containerColor = Color.Transparent, bottomBar = { Column( modifier = Modifier - .fillMaxWidth() - .clip(RoundedCornerShape(topEnd = dimensions().spacing8x, topStart = dimensions().spacing8x)) - .background(colorsScheme().surface) - .padding(dimensions().spacing16x) + .clip(WireBottomSheetDefaults.WireBottomSheetShape) + .background(WireBottomSheetDefaults.WireSheetContainerColor) ) { Row( - modifier = Modifier.fillMaxWidth(), + modifier = Modifier + .fillMaxWidth() + .padding(dimensions().spacing16x), verticalAlignment = Alignment.CenterVertically, ) { if (canNavigateBack) { Icon( - imageVector = Icons.Filled.ArrowBack, + imageVector = Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back", modifier = Modifier.clickable(onClick = onNavigateBack) ) @@ -96,16 +104,33 @@ private fun NewLoginContent( content() } } - } - ) { _ -> - Column { - WireAuthBackgroundComponent() - } - } + }) { _ -> } +} + +@Composable +private fun NavigationBarBackground() = Box( + contentAlignment = Alignment.BottomCenter, + modifier = Modifier.fillMaxSize() +) { + Box( + modifier = Modifier + .fillMaxWidth() + .background(colorsScheme().background) + .navigationBarsPadding() + ) } @PreviewMultipleThemes @Composable private fun PreviewNewLoginContent() = WireTheme { - NewLoginContent("Enter your password to log in", true, {}) { Text(text = "EMPTY") } + EdgeToEdgePreview(useDarkIcons = false) { + WireAuthBackgroundLayout { + NewLoginContent("Enter your password to log in", true, {}) { + Text( + text = "EMPTY", + modifier = Modifier.padding(dimensions().spacing24x) + ) + } + } + } } diff --git a/app/src/main/kotlin/com/wire/android/ui/authentication/login/WireAuthBackgroundComponent.kt b/app/src/main/kotlin/com/wire/android/ui/authentication/login/WireAuthBackgroundComponent.kt index 123806c8b67..4b7b81a4de3 100644 --- a/app/src/main/kotlin/com/wire/android/ui/authentication/login/WireAuthBackgroundComponent.kt +++ b/app/src/main/kotlin/com/wire/android/ui/authentication/login/WireAuthBackgroundComponent.kt @@ -20,44 +20,48 @@ package com.wire.android.ui.authentication.login import android.annotation.SuppressLint import androidx.compose.foundation.Image import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource -import androidx.compose.ui.tooling.preview.Preview import com.wire.android.R -import com.wire.android.ui.theme.WireColorPalette import com.wire.android.ui.theme.WireTheme +import com.wire.android.ui.theme.wireDarkColorScheme import com.wire.android.util.ui.PreviewMultipleThemes @Composable -fun WireAuthBackgroundComponent() { - MainBackgroundContent() -} - -@SuppressLint("PackagePrivateId") -@Composable -private fun MainBackgroundContent() { - Column(modifier = Modifier.fillMaxSize()) { +fun WireAuthBackgroundLayout( + modifier: Modifier = Modifier, + content: @Composable BoxScope.() -> Unit = {}, +) { + Box( + modifier = modifier + .fillMaxSize() + .background(color = MaterialTheme.wireDarkColorScheme.background), // splash is always dark + ) { val image: Painter = painterResource(id = R.drawable.bg_waves) Image( painter = image, contentDescription = null, - contentScale = ContentScale.Crop, + contentScale = ContentScale.FillWidth, + alignment = Alignment.BottomCenter, modifier = Modifier .fillMaxWidth() - .background(color = WireColorPalette.Gray100) + .align(Alignment.BottomCenter) ) + content() } } @PreviewMultipleThemes -@Preview(showSystemUi = true) @Composable -private fun PreviewWireAuthBackgroundComponent() = WireTheme { - MainBackgroundContent() +private fun PreviewWireAuthBackgroundLayout() = WireTheme { + WireAuthBackgroundLayout {} } diff --git a/app/src/main/kotlin/com/wire/android/ui/authentication/login/email/LoginEmailScreen.kt b/app/src/main/kotlin/com/wire/android/ui/authentication/login/email/LoginEmailScreen.kt index d3f8a5186bc..ef1d2a6ebbc 100644 --- a/app/src/main/kotlin/com/wire/android/ui/authentication/login/email/LoginEmailScreen.kt +++ b/app/src/main/kotlin/com/wire/android/ui/authentication/login/email/LoginEmailScreen.kt @@ -28,6 +28,7 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.input.TextFieldState @@ -80,7 +81,8 @@ fun LoginEmailScreen( onSuccess: (initialSyncCompleted: Boolean, isE2EIRequired: Boolean) -> Unit, onRemoveDeviceNeeded: () -> Unit, loginEmailViewModel: LoginEmailViewModel, - scrollState: ScrollState = rememberScrollState() + scrollState: ScrollState = rememberScrollState(), + fillMaxHeight: Boolean = true, ) { val scope = rememberCoroutineScope() @@ -103,7 +105,8 @@ fun LoginEmailScreen( onLoginButtonClick = loginEmailViewModel::login, onUpdateApp = loginEmailViewModel::updateTheApp, forgotPasswordUrl = loginEmailViewModel.serverConfig.forgotPassword, - scope = scope + scope = scope, + fillMaxHeight = fillMaxHeight, ) LaunchedEffect(loginEmailViewModel.loginState.flowState) { @@ -129,16 +132,20 @@ private fun LoginEmailContent( onLoginButtonClick: () -> Unit, onUpdateApp: () -> Unit, forgotPasswordUrl: String, - scope: CoroutineScope + scope: CoroutineScope, + fillMaxHeight: Boolean = true, ) { Column( - modifier = Modifier - .fillMaxHeight() + modifier = Modifier.let { + if (fillMaxHeight) it.fillMaxHeight() else it.wrapContentHeight() + } ) { Column( modifier = Modifier - .weight(weight = 1f, fill = true) + .let { + if (fillMaxHeight) it.weight(weight = 1f, fill = true) else it + } .verticalScroll(scrollState) .padding(MaterialTheme.wireDimensions.spacing16x) .semantics { diff --git a/app/src/main/kotlin/com/wire/android/ui/authentication/start/StartLoginScreen.kt b/app/src/main/kotlin/com/wire/android/ui/authentication/start/StartLoginScreen.kt index bd207b1c8c0..62b150bf6b2 100644 --- a/app/src/main/kotlin/com/wire/android/ui/authentication/start/StartLoginScreen.kt +++ b/app/src/main/kotlin/com/wire/android/ui/authentication/start/StartLoginScreen.kt @@ -48,20 +48,21 @@ import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextDecoration -import androidx.compose.ui.tooling.preview.Preview import androidx.hilt.navigation.compose.hiltViewModel import com.wire.android.R import com.wire.android.config.LocalCustomUiConfigurationProvider import com.wire.android.navigation.NavigationCommand import com.wire.android.navigation.Navigator import com.wire.android.navigation.WireDestination -import com.wire.android.navigation.style.PopUpNavigationAnimation +import com.wire.android.navigation.style.AuthPopUpNavigationAnimation import com.wire.android.ui.authentication.login.LoginState import com.wire.android.ui.authentication.login.NewLoginContainer +import com.wire.android.ui.authentication.login.WireAuthBackgroundLayout import com.wire.android.ui.authentication.login.email.LoginEmailState import com.wire.android.ui.common.button.WireButtonState import com.wire.android.ui.common.button.WirePrimaryButton import com.wire.android.ui.common.dimensions +import com.wire.android.ui.common.preview.EdgeToEdgePreview import com.wire.android.ui.common.spacers.VerticalSpace import com.wire.android.ui.common.textfield.DefaultEmailNext import com.wire.android.ui.common.textfield.WireAutoFillType @@ -75,7 +76,7 @@ import com.wire.android.util.CustomTabsHelper import com.wire.android.util.ui.PreviewMultipleThemes @WireDestination( - style = PopUpNavigationAnimation::class, + style = AuthPopUpNavigationAnimation::class, navArgsDelegate = StartLoginScreenNavArgs::class, ) @Composable @@ -256,17 +257,18 @@ private fun WelcomeFooter(onTermsAndConditionClick: () -> Unit, modifier: Modifi @PreviewMultipleThemes @Composable -@Preview(showSystemUi = true) -fun PreviewWelcomeScreen() { - WireTheme { - StartLoginContent( - isCustomBackend = false, - isThereActiveSession = false, - loginEmailState = LoginEmailState(), - userIdentifierState = TextFieldState(), - onNextClicked = {}, - navigateBack = {}, - navigate = {} - ) +fun PreviewStartLoginScreen() = WireTheme { + EdgeToEdgePreview(useDarkIcons = false) { + WireAuthBackgroundLayout { + StartLoginContent( + isCustomBackend = false, + isThereActiveSession = false, + loginEmailState = LoginEmailState(), + userIdentifierState = TextFieldState(), + onNextClicked = {}, + navigateBack = {}, + navigate = {} + ) + } } } diff --git a/app/src/main/kotlin/com/wire/android/ui/authentication/welcome/WelcomeScreen.kt b/app/src/main/kotlin/com/wire/android/ui/authentication/welcome/WelcomeScreen.kt index 31430c0d3d8..53b8df9d929 100644 --- a/app/src/main/kotlin/com/wire/android/ui/authentication/welcome/WelcomeScreen.kt +++ b/app/src/main/kotlin/com/wire/android/ui/authentication/welcome/WelcomeScreen.kt @@ -20,21 +20,24 @@ package com.wire.android.ui.authentication.welcome +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.tooling.preview.Preview import androidx.hilt.navigation.compose.hiltViewModel import com.ramcosta.composedestinations.annotation.RootNavGraph import com.wire.android.navigation.NavigationCommand import com.wire.android.navigation.Navigator import com.wire.android.navigation.WireDestination -import com.wire.android.navigation.style.PopUpNavigationAnimation -import com.wire.android.ui.authentication.login.WireAuthBackgroundComponent +import com.wire.android.navigation.style.AuthPopUpNavigationAnimation +import com.wire.android.ui.authentication.login.WireAuthBackgroundLayout import com.wire.android.ui.common.dialogs.FeatureDisabledWithProxyDialogContent import com.wire.android.ui.common.dialogs.FeatureDisabledWithProxyDialogState import com.wire.android.ui.common.dialogs.MaxAccountsReachedDialog import com.wire.android.ui.common.dialogs.MaxAccountsReachedDialogState +import com.wire.android.ui.common.preview.EdgeToEdgePreview import com.wire.android.ui.common.visbility.rememberVisibilityState import com.wire.android.ui.destinations.StartLoginScreenDestination import com.wire.android.ui.theme.WireTheme @@ -45,7 +48,7 @@ import kotlinx.coroutines.delay @RootNavGraph(start = true) @WireDestination( - style = PopUpNavigationAnimation::class + style = AuthPopUpNavigationAnimation::class, ) @Composable fun WelcomeScreen( @@ -65,7 +68,6 @@ private fun WelcomeContent( navigateBack: () -> Unit, navigate: (NavigationCommand) -> Unit ) { - WireAuthBackgroundComponent() val enterpriseDisabledWithProxyDialogState = rememberVisibilityState() val createPersonalAccountDisabledWithProxyDialogState = rememberVisibilityState() val context = LocalContext.current @@ -82,6 +84,9 @@ private fun WelcomeContent( ) FeatureDisabledWithProxyDialogContent(dialogState = createPersonalAccountDisabledWithProxyDialogState) + // empty Box to keep proper bounds of the screen for transition animation to the next screen + Box(modifier = Modifier.fillMaxSize()) + with(state.startLoginDestination) { LaunchedEffect(this) { if (state.maxAccountsReached.not()) { @@ -100,9 +105,9 @@ private fun WelcomeContent( @PreviewMultipleThemes @Composable -@Preview(showSystemUi = true) -fun PreviewWelcomeScreen() { - WireTheme { +fun PreviewWelcomeScreen() = WireTheme { + EdgeToEdgePreview(useDarkIcons = false) { + WireAuthBackgroundLayout() WelcomeContent( state = WelcomeScreenState(ServerConfig.DEFAULT), navigateBack = {}, diff --git a/app/src/main/kotlin/com/wire/android/ui/common/topappbar/CommonTopAppBar.kt b/app/src/main/kotlin/com/wire/android/ui/common/topappbar/CommonTopAppBar.kt index ce9ef4e88b8..92030911e7f 100644 --- a/app/src/main/kotlin/com/wire/android/ui/common/topappbar/CommonTopAppBar.kt +++ b/app/src/main/kotlin/com/wire/android/ui/common/topappbar/CommonTopAppBar.kt @@ -49,6 +49,9 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import com.wire.android.BuildConfig import com.wire.android.R +import com.wire.android.navigation.style.BackgroundType +import com.wire.android.ui.authentication.login.WireAuthBackgroundLayout +import com.wire.android.ui.common.preview.EdgeToEdgePreview import com.wire.android.ui.theme.WireColorScheme import com.wire.android.ui.theme.WireTheme import com.wire.android.ui.theme.updateSystemBarIconsAppearance @@ -62,23 +65,24 @@ import com.wire.kalium.network.NetworkState @Composable fun CommonTopAppBar( commonTopAppBarState: CommonTopAppBarState, + backgroundType: BackgroundType, onReturnToCallClick: (ConnectivityUIState.Call.Established) -> Unit, onReturnToIncomingCallClick: (ConnectivityUIState.Call.Incoming) -> Unit, onReturnToOutgoingCallClick: (ConnectivityUIState.Call.Outgoing) -> Unit, modifier: Modifier = Modifier, ) { val transition = updateTransition( - targetState = MaterialTheme.wireColorScheme to commonTopAppBarState.connectivityState.toColorType(), + targetState = MaterialTheme.wireColorScheme to getColorType(commonTopAppBarState.connectivityState, backgroundType), label = "connectivity state transition" ) val backgroundColor = transition.animateColor(label = "top app bar background color") { (colorScheme, colorType) -> colorScheme.getBackgroundColor(colorType) } - val systemBarIconsAppearance = transition.animateFloat(label = "system bar icons appearance") { (colorScheme, colorType) -> - if (colorScheme.getStatusBarIconsAppearance(colorType)) 1f else 0f + val systemBarUseDarkIcons = transition.animateFloat(label = "system bar icons appearance") { (colorScheme, colorType) -> + if (colorScheme.getStatusBarUseDarkIcons(colorType)) 1f else 0f } - updateSystemBarIconsAppearance(systemBarIconsAppearance.value > 0.5f) + updateSystemBarIconsAppearance(systemBarUseDarkIcons.value > 0.5f) Column( modifier = modifier @@ -96,25 +100,31 @@ fun CommonTopAppBar( } } -private enum class ConnectivityStatusColorType { Calls, Connection, None } +private enum class ConnectivityStatusColorType { Calls, Connection, Auth, Default } -private fun ConnectivityUIState.toColorType() = when (this) { +private fun getColorType(connectivityState: ConnectivityUIState, backgroundType: BackgroundType) = when (connectivityState) { is ConnectivityUIState.Calls -> ConnectivityStatusColorType.Calls is ConnectivityUIState.Connecting, is ConnectivityUIState.WaitingConnection -> ConnectivityStatusColorType.Connection - is ConnectivityUIState.None -> ConnectivityStatusColorType.None + is ConnectivityUIState.None -> when (backgroundType) { + BackgroundType.Auth -> ConnectivityStatusColorType.Auth + BackgroundType.Default -> ConnectivityStatusColorType.Default + } } +@Composable private fun WireColorScheme.getBackgroundColor(statusColorType: ConnectivityStatusColorType): Color = when (statusColorType) { ConnectivityStatusColorType.Calls -> positive ConnectivityStatusColorType.Connection -> primary - ConnectivityStatusColorType.None -> background + ConnectivityStatusColorType.Auth, + ConnectivityStatusColorType.Default -> Color.Transparent } -private fun WireColorScheme.getStatusBarIconsAppearance(statusColorType: ConnectivityStatusColorType): Boolean = when (statusColorType) { +private fun WireColorScheme.getStatusBarUseDarkIcons(statusColorType: ConnectivityStatusColorType): Boolean = when (statusColorType) { ConnectivityStatusColorType.Calls, ConnectivityStatusColorType.Connection -> connectivityBarShouldUseDarkIcons - ConnectivityStatusColorType.None -> useDarkSystemBarIcons + ConnectivityStatusColorType.Auth -> false // splash is always dark so use light icons + ConnectivityStatusColorType.Default -> useDarkSystemBarIcons } @Composable @@ -378,8 +388,17 @@ private fun MicrophoneIcon( } @Composable -private fun PreviewCommonTopAppBar(connectivityUIState: ConnectivityUIState) = WireTheme { - CommonTopAppBar(CommonTopAppBarState(connectivityUIState), {}, {}, {}) +private fun PreviewCommonTopAppBar( + connectivityUIState: ConnectivityUIState, + backgroundType: BackgroundType = BackgroundType.Default, + content: @Composable () -> Unit = {}, +) = WireTheme { + EdgeToEdgePreview( + useDarkIcons = MaterialTheme.wireColorScheme.getStatusBarUseDarkIcons(getColorType(connectivityUIState, backgroundType)) + ) { + CommonTopAppBar(CommonTopAppBarState(connectivityUIState), backgroundType, {}, {}, {}) + content() + } } @PreviewMultipleThemes @@ -434,3 +453,10 @@ fun PreviewCommonTopAppBar_ConnectivityWaitingConnection() = @Composable fun PreviewCommonTopAppBar_ConnectivityNone() = PreviewCommonTopAppBar(ConnectivityUIState.None) + +@PreviewMultipleThemes +@Composable +fun PreviewCommonTopAppBar_ConnectivityNone_Splash() = + WireAuthBackgroundLayout { + PreviewCommonTopAppBar(ConnectivityUIState.None, BackgroundType.Auth) + } diff --git a/core/navigation/src/main/kotlin/com/wire/android/navigation/style/BackgroundStyle.kt b/core/navigation/src/main/kotlin/com/wire/android/navigation/style/BackgroundStyle.kt new file mode 100644 index 00000000000..e1c0cb3a3b0 --- /dev/null +++ b/core/navigation/src/main/kotlin/com/wire/android/navigation/style/BackgroundStyle.kt @@ -0,0 +1,26 @@ +/* + * Wire + * Copyright (C) 2025 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.wire.android.navigation.style + +interface BackgroundStyle { + fun backgroundType(): BackgroundType = BackgroundType.Default +} + +enum class BackgroundType { + Default, Auth +} diff --git a/core/navigation/src/main/kotlin/com/wire/android/navigation/style/NavigationAnimationStyles.kt b/core/navigation/src/main/kotlin/com/wire/android/navigation/style/NavigationAnimationStyles.kt index facbccec657..2f15bea51a3 100644 --- a/core/navigation/src/main/kotlin/com/wire/android/navigation/style/NavigationAnimationStyles.kt +++ b/core/navigation/src/main/kotlin/com/wire/android/navigation/style/NavigationAnimationStyles.kt @@ -20,10 +20,20 @@ package com.wire.android.navigation.style typealias DefaultNavigationAnimation = SlideNavigationAnimation -object SlideNavigationAnimation : WireDestinationStyleAnimated { +object SlideNavigationAnimation : WireDestinationStyleAnimated, BackgroundStyle { override fun animationType(): TransitionAnimationType = TransitionAnimationType.SLIDE } -object PopUpNavigationAnimation : WireDestinationStyleAnimated { +object PopUpNavigationAnimation : WireDestinationStyleAnimated, BackgroundStyle { override fun animationType(): TransitionAnimationType = TransitionAnimationType.POP_UP } + +object AuthSlideNavigationAnimation : WireDestinationStyleAnimated, BackgroundStyle { + override fun animationType(): TransitionAnimationType = TransitionAnimationType.SLIDE + override fun backgroundType(): BackgroundType = BackgroundType.Auth +} + +object AuthPopUpNavigationAnimation : WireDestinationStyleAnimated, BackgroundStyle { + override fun animationType(): TransitionAnimationType = TransitionAnimationType.POP_UP + override fun backgroundType(): BackgroundType = BackgroundType.Auth +} diff --git a/core/ui-common/build.gradle.kts b/core/ui-common/build.gradle.kts index 664adf293e8..bd1853f5495 100644 --- a/core/ui-common/build.gradle.kts +++ b/core/ui-common/build.gradle.kts @@ -28,9 +28,11 @@ dependencies { implementation(libs.compose.ui.preview) debugImplementation(libs.compose.ui.tooling) - implementation(libs.accompanist.systemUI) implementation(libs.visibilityModifiers) + // Compose Preview + implementation(libs.compose.edgetoedge.preview) + // Image loading implementation(libs.coil.core) implementation(libs.coil.gif) diff --git a/core/ui-common/src/main/kotlin/com/wire/android/ui/common/preview/EdgeToEdgePreview.kt b/core/ui-common/src/main/kotlin/com/wire/android/ui/common/preview/EdgeToEdgePreview.kt new file mode 100644 index 00000000000..ac98f104e93 --- /dev/null +++ b/core/ui-common/src/main/kotlin/com/wire/android/ui/common/preview/EdgeToEdgePreview.kt @@ -0,0 +1,51 @@ +/* + * Wire + * Copyright (C) 2025 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.wire.android.ui.common.preview + +import android.content.res.Configuration.UI_MODE_NIGHT_NO +import android.content.res.Configuration.UI_MODE_NIGHT_YES +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.platform.LocalConfiguration +import de.drick.compose.edgetoedgepreviewlib.CameraCutoutMode +import de.drick.compose.edgetoedgepreviewlib.EdgeToEdgeTemplate +import de.drick.compose.edgetoedgepreviewlib.NavigationMode + +@Composable +fun EdgeToEdgePreview( + useDarkIcons: Boolean, + content: @Composable () -> Unit, +) { + // isAppearanceLightStatusBars cannot be set for previews, so we need to apply it manually + // TODO: remove LocalConfiguration and just pass `isDarkMode` into EdgeToEdgeTemplate after updating edgetoedgepreviewlib to 0.6.0 + CompositionLocalProvider( + LocalConfiguration provides LocalConfiguration.current.apply { + uiMode = if (useDarkIcons) UI_MODE_NIGHT_NO else UI_MODE_NIGHT_YES + } + ) { + EdgeToEdgeTemplate( + navMode = NavigationMode.Gesture, + cameraCutoutMode = CameraCutoutMode.Middle, + isInvertedOrientation = false, + showInsetsBorder = false, + isNavigationBarVisible = true, + isStatusBarVisible = true, + content = content, + ) + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ea6a2d4a205..93be2dd8d7f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -55,6 +55,9 @@ compose-navigation = "2.7.7" # adjusted to work with compose-destinations "1.9.5 compose-destinations = "1.10.2" screenshot = "0.0.1-alpha08" +# Compose Preview +compose-edgetoedge-preview = "0.3.0" # update after changing target sdk to 35 and androidx-core to 1.15.0 + # Hilt hilt = "2.51.1" hilt-composeNavigation = "1.2.0" @@ -215,8 +218,10 @@ compose-navigation = { module = "androidx.navigation:navigation-compose", versio compose-destinations-core = { module = "io.github.raamcosta.compose-destinations:animations-core", version.ref = "compose-destinations" } compose-destinations-ksp = { module = "io.github.raamcosta.compose-destinations:ksp", version.ref = "compose-destinations" } +# Compose Preview +compose-edgetoedge-preview = { module = "de.drick.compose:edge-to-edge-preview", version.ref = "compose-edgetoedge-preview" } + # Accompanist -accompanist-systemUI = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanist" } accompanist-placeholder = { module = "com.google.accompanist:accompanist-placeholder", version.ref = "accompanist" } # Image Loading