From 9334609dafdd4fd30041285d51c911a23175b2d8 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 5 May 2025 11:47:30 +0300 Subject: [PATCH] Updated on 2026-08-14 --- app/src/main/AndroidManifest.xml | 1 + .../ui/common/DetailsComposeElements.kt | 3 - .../tap/features/home/DefaultHomeComponent.kt | 15 ++ .../com/tangem/tap/routing/RootContent.kt | 99 +++++--- .../tap/routing/component/RoutingComponent.kt | 7 - .../component/impl/DefaultRoutingComponent.kt | 2 + .../RoutingTransitionAnimationFactory.kt | 92 +++++++ .../com/tangem/core/ui/res/TangemTheme.kt | 10 +- .../features/details/ui/DetailsScreen.kt | 3 - .../impl/DefaultDisclaimerComponent.kt | 3 - .../impl/DefaultManageTokensComponent.kt | 3 - .../DefaultMarketsTokenDetailsComponent.kt | 4 - .../impl/DefaultMarketsEntryComponent.kt | 2 +- .../feature/referral/ui/ReferralScreen.kt | 3 - .../stories/impl/DefaultStoriesComponent.kt | 14 ++ .../tokendetails/ui/TokenDetailsScreen.kt | 2 - .../walletsettings/ui/WalletSettingsScreen.kt | 3 - .../wallet/child/wallet/WalletComponent.kt | 6 - .../presentation/wallet/ui/WalletScreen.kt | 231 ++++++++---------- 19 files changed, 297 insertions(+), 206 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/routing/transitions/RoutingTransitionAnimationFactory.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 12ecacf838..cac33d689c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -43,6 +43,7 @@ android:resizeableActivity="@bool/resizeable_activity" android:roundIcon="@mipmap/ic_launcher" android:supportsRtl="true" + android:enableOnBackInvokedCallback="true" tools:ignore="GoogleAppIndexingWarning" tools:replace="android:allowBackup, android:fullBackupContent, android:label"> diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/common/DetailsComposeElements.kt b/app/src/main/java/com/tangem/tap/features/details/ui/common/DetailsComposeElements.kt index 5646eee001..98c792c2ef 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/common/DetailsComposeElements.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/common/DetailsComposeElements.kt @@ -1,6 +1,5 @@ package com.tangem.tap.features.details.ui.common -import androidx.activity.compose.BackHandler import androidx.annotation.StringRes import androidx.compose.foundation.layout.* import androidx.compose.foundation.selection.selectable @@ -29,8 +28,6 @@ internal fun SettingsScreensScaffold( ) { val backgroundColor = TangemTheme.colors.background.secondary - BackHandler(onBack = onBackClick) - Scaffold( snackbarHost = { SnackbarHost(snackbarHostState) }, topBar = { diff --git a/app/src/main/java/com/tangem/tap/features/home/DefaultHomeComponent.kt b/app/src/main/java/com/tangem/tap/features/home/DefaultHomeComponent.kt index a7a853a86a..9f0bff8ab1 100644 --- a/app/src/main/java/com/tangem/tap/features/home/DefaultHomeComponent.kt +++ b/app/src/main/java/com/tangem/tap/features/home/DefaultHomeComponent.kt @@ -2,14 +2,18 @@ package com.tangem.tap.features.home import androidx.activity.compose.BackHandler import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import com.arkivanov.essenty.lifecycle.subscribe import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.components.SystemBarsIconsDisposable +import com.tangem.core.ui.res.LocalRootBackgroundColor import com.tangem.core.ui.utils.findActivity import com.tangem.tap.common.redux.AppState import com.tangem.tap.features.home.api.HomeComponent @@ -61,6 +65,17 @@ internal class DefaultHomeComponent @AssistedInject constructor( onShopButtonClick = model::onShopClick, onSearchTokensClick = model::onSearchClick, ) + + val rootBackgroundColor = LocalRootBackgroundColor.current + val previousColor = remember { rootBackgroundColor.value } + val storiesBackgroundColor = Color(color = 0xFF010101) + + DisposableEffect(Unit) { + rootBackgroundColor.value = storiesBackgroundColor + onDispose { + rootBackgroundColor.value = previousColor + } + } } override fun newState(state: HomeState) { diff --git a/app/src/main/java/com/tangem/tap/routing/RootContent.kt b/app/src/main/java/com/tangem/tap/routing/RootContent.kt index c4d8c96d97..b94e0d9aab 100644 --- a/app/src/main/java/com/tangem/tap/routing/RootContent.kt +++ b/app/src/main/java/com/tangem/tap/routing/RootContent.kt @@ -1,6 +1,7 @@ package com.tangem.tap.routing import android.app.Activity +import android.os.Build import android.os.Bundle import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box @@ -8,7 +9,6 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -17,25 +17,31 @@ import androidx.compose.ui.unit.dp import androidx.core.content.ContextCompat.startActivity import com.arkivanov.decompose.ExperimentalDecomposeApi import com.arkivanov.decompose.extensions.compose.stack.Children -import com.arkivanov.decompose.extensions.compose.stack.animation.fade +import com.arkivanov.decompose.extensions.compose.stack.animation.StackAnimation +import com.arkivanov.decompose.extensions.compose.stack.animation.predictiveback.androidPredictiveBackAnimatable +import com.arkivanov.decompose.extensions.compose.stack.animation.predictiveback.predictiveBackAnimation import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation import com.arkivanov.decompose.router.stack.ChildStack import com.arkivanov.decompose.value.Value +import com.arkivanov.essenty.backhandler.BackHandler import com.tangem.common.routing.AppRoute import com.tangem.core.ui.UiDependencies import com.tangem.core.ui.components.snackbar.TangemSnackbarHost import com.tangem.core.ui.message.EventMessageEffect -import com.tangem.core.ui.res.LocalIsNavigationRefactoringEnabled +import com.tangem.core.ui.res.LocalRootBackgroundColor import com.tangem.core.ui.res.LocalSnackbarHostState import com.tangem.core.ui.res.TangemTheme import com.tangem.tap.routing.component.RoutingComponent +import com.tangem.tap.routing.transitions.RoutingTransitionAnimationFactory @OptIn(ExperimentalDecomposeApi::class) @Composable internal fun RootContent( stack: Value>, - wcContent: @Composable (modifier: Modifier) -> Unit, + backHandler: BackHandler, uiDependencies: UiDependencies, + wcContent: @Composable (modifier: Modifier) -> Unit, + onBack: () -> Unit, modifier: Modifier = Modifier, ) { val context = LocalContext.current @@ -44,45 +50,62 @@ internal fun RootContent( activity = context as Activity, uiDependencies = uiDependencies, ) { - CompositionLocalProvider( - LocalIsNavigationRefactoringEnabled provides true, - ) { - val snackbarHostState = LocalSnackbarHostState.current + val snackbarHostState = LocalSnackbarHostState.current - Box(Modifier.background(TangemTheme.colors.background.primary)) { - Children( - modifier = modifier, - animation = stackAnimation(fade()), - stack = stack, - ) { child -> - when (val instance = child.instance) { - is RoutingComponent.Child.Initial -> Unit - is RoutingComponent.Child.ComposableComponent -> { - instance.component.Content(Modifier.fillMaxSize()) + Box(Modifier.background(LocalRootBackgroundColor.current.value)) { + Children( + modifier = modifier, + animation = childrenAnimation(backHandler = backHandler, onBack = onBack), + stack = stack, + ) { child -> + when (val instance = child.instance) { + is RoutingComponent.Child.Initial -> Unit + is RoutingComponent.Child.ComposableComponent -> { + instance.component.Content(Modifier.fillMaxSize()) + } + is RoutingComponent.Child.LegacyIntent -> { + // TODO: Remove and use it's own router: [REDACTED_JIRA] + LaunchedEffect(instance) { + startActivity(context, instance.intent, Bundle.EMPTY) } - is RoutingComponent.Child.LegacyIntent -> { - // TODO: Remove and use it's own router: [REDACTED_JIRA] - LaunchedEffect(instance) { - startActivity(context, instance.intent, Bundle.EMPTY) - } - } - is RoutingComponent.Child.LegacyFragment, - -> error("Unsupported child: $instance") } } - - wcContent(Modifier.fillMaxSize()) - - TangemSnackbarHost( - modifier = Modifier - .align(Alignment.BottomCenter) - .navigationBarsPadding() - .padding(all = 16.dp), - hostState = snackbarHostState, - ) } - } - EventMessageEffect() + wcContent(Modifier.fillMaxSize()) + + TangemSnackbarHost( + modifier = Modifier + .align(Alignment.BottomCenter) + .navigationBarsPadding() + .padding(all = 16.dp), + hostState = snackbarHostState, + ) + } + } + + EventMessageEffect() +} + +@OptIn(ExperimentalDecomposeApi::class) +private fun childrenAnimation( + backHandler: BackHandler, + onBack: () -> Unit, +): StackAnimation { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + predictiveBackAnimation( + backHandler = backHandler, + onBack = onBack, + selector = { backEvent, _, _ -> + androidPredictiveBackAnimatable(backEvent) + }, + fallbackAnimation = stackAnimation { + RoutingTransitionAnimationFactory.create(it.configuration) + }, + ) + } else { + stackAnimation { + RoutingTransitionAnimationFactory.create(it.configuration) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/routing/component/RoutingComponent.kt b/app/src/main/java/com/tangem/tap/routing/component/RoutingComponent.kt index 6cd367b582..9fdef856a9 100644 --- a/app/src/main/java/com/tangem/tap/routing/component/RoutingComponent.kt +++ b/app/src/main/java/com/tangem/tap/routing/component/RoutingComponent.kt @@ -2,11 +2,9 @@ package com.tangem.tap.routing.component import android.content.Intent import androidx.compose.runtime.Immutable -import androidx.fragment.app.Fragment import com.tangem.common.routing.AppRoute import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.ui.decompose.ComposableContentComponent -import com.tangem.utils.Provider internal interface RoutingComponent : ComposableContentComponent { @@ -16,11 +14,6 @@ internal interface RoutingComponent : ComposableContentComponent { // TODO: Remove and find initial intent in RoutingComponent: [REDACTED_JIRA] data object Initial : Child() - data class LegacyFragment( - val name: String, - val fragmentProvider: Provider, - ) : Child() - data class LegacyIntent(val intent: Intent) : Child() data class ComposableComponent( diff --git a/app/src/main/java/com/tangem/tap/routing/component/impl/DefaultRoutingComponent.kt b/app/src/main/java/com/tangem/tap/routing/component/impl/DefaultRoutingComponent.kt index b6cdb0f0be..92ac286e88 100644 --- a/app/src/main/java/com/tangem/tap/routing/component/impl/DefaultRoutingComponent.kt +++ b/app/src/main/java/com/tangem/tap/routing/component/impl/DefaultRoutingComponent.kt @@ -74,6 +74,8 @@ internal class DefaultRoutingComponent @AssistedInject constructor( stack = stack, uiDependencies = uiDependencies, wcContent = { wcRoutingComponent.Content(it) }, + backHandler = backHandler, + onBack = router::pop, ) } diff --git a/app/src/main/java/com/tangem/tap/routing/transitions/RoutingTransitionAnimationFactory.kt b/app/src/main/java/com/tangem/tap/routing/transitions/RoutingTransitionAnimationFactory.kt new file mode 100644 index 0000000000..b1332ff1c8 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/routing/transitions/RoutingTransitionAnimationFactory.kt @@ -0,0 +1,92 @@ +package com.tangem.tap.routing.transitions + +import androidx.compose.animation.core.CubicBezierEasing +import androidx.compose.animation.core.FiniteAnimationSpec +import androidx.compose.animation.core.tween +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.scale +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.layout.layout +import com.arkivanov.decompose.extensions.compose.stack.animation.* +import com.tangem.common.routing.AppRoute +import kotlin.compareTo +import kotlin.times + +object RoutingTransitionAnimationFactory { + + @Suppress("MagicNumber") + fun create(appRoute: AppRoute): StackAnimator { + return when (appRoute) { + is AppRoute.Onboarding, + is AppRoute.Welcome, + is AppRoute.Home, + -> fade(tween(400)).plus(scale(tween(400))) + is AppRoute.Wallet, + -> slideAndFade(directions = setOf(Direction.ENTER_BACK, Direction.EXIT_BACK)) + .plus( + scaleWithDirection( + directions = setOf(Direction.ENTER_FRONT, Direction.EXIT_FRONT), + animationSpec = tween(400), + ), + ) + else -> slideAndFade() + } + } + + private fun scaleWithDirection( + directions: Set, + animationSpec: FiniteAnimationSpec = tween(), + frontFactor: Float = 1.15F, + backFactor: Float = 0.95F, + ): StackAnimator = stackAnimator(animationSpec = animationSpec) { factor, direction, content -> + content( + Modifier.graphicsLayer { + val scaleFactor = if (directions.contains(direction)) { + if (factor >= 0F) { + factor * (frontFactor - 1F) + 1F + } else { + factor * (1F - backFactor) + 1F + } + } else { + 1f + } + scaleX = scaleFactor + scaleY = scaleFactor + }, + ) + } + + @Suppress("MagicNumber") + private fun slideAndFade(directions: Set? = null): StackAnimator { + val easing = CubicBezierEasing(0.55f, 0.0f, 0.0f, 1f) + + return stackAnimator( + animationSpec = tween(durationMillis = 400, easing = easing), + ) { factor, direction, content -> + content( + if (directions == null || directions.contains(direction)) { + Modifier.offsetXFactor(factor) + } else { + Modifier + }, + ) + }.plus( + fade( + animationSpec = tween( + delayMillis = 50, + durationMillis = 300, + easing = easing, + ), + ), + ) + } + + @Suppress("MagicNumber") + private fun Modifier.offsetXFactor(factor: Float): Modifier = layout { measurable, constraints -> + val placeable = measurable.measure(constraints) + + layout(placeable.width, placeable.height) { + placeable.placeRelative(x = (placeable.width.toFloat() * factor * 0.15f).toInt(), y = 0) + } + } +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemTheme.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemTheme.kt index fd2a88637f..c1df30c687 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemTheme.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemTheme.kt @@ -87,6 +87,8 @@ fun TangemTheme( DefaultHapticManager(view = view, vibratorHapticManager = vibratorHapticManager) } + val rootBackgroundColor = rememberedColors.background.secondary + MaterialTheme( colors = materialThemeColors(colors = themeColors, isDark = isDark), ) { @@ -105,6 +107,7 @@ fun TangemTheme( CompositionLocalProvider( LocalTangemShimmer provides TangemShimmer, LocalMainBottomSheetColor provides remember { mutableStateOf(Color.Unspecified) }, + LocalRootBackgroundColor provides remember { mutableStateOf(rootBackgroundColor) }, LocalTextSelectionColors provides TangemTextSelectionColors, ) { ProvideTextStyle( @@ -308,13 +311,12 @@ val LocalMainBottomSheetColor = staticCompositionLocalOf> { error("No MainBottomSheetColor provided") } -val LocalBladeAnimation = staticCompositionLocalOf { +val LocalRootBackgroundColor = staticCompositionLocalOf> { error("No MainBottomSheetColor provided") } -// TODO remove this after migration to new navigation -val LocalIsNavigationRefactoringEnabled = staticCompositionLocalOf { - false +val LocalBladeAnimation = staticCompositionLocalOf { + error("No MainBottomSheetColor provided") } /** diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/ui/DetailsScreen.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/ui/DetailsScreen.kt index cf172f3a90..0c4d3e218e 100644 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/ui/DetailsScreen.kt +++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/ui/DetailsScreen.kt @@ -1,7 +1,6 @@ package com.tangem.features.details.ui import android.content.res.Configuration -import androidx.activity.compose.BackHandler import androidx.compose.foundation.background import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.scrollable @@ -41,8 +40,6 @@ internal fun DetailsScreen( ) { val backgroundColor = TangemTheme.colors.background.secondary - BackHandler(onBack = state.popBack) - Scaffold( modifier = modifier, containerColor = backgroundColor, diff --git a/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/component/impl/DefaultDisclaimerComponent.kt b/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/component/impl/DefaultDisclaimerComponent.kt index 5fbd86f488..cd12445944 100644 --- a/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/component/impl/DefaultDisclaimerComponent.kt +++ b/features/disclaimer/impl/src/main/java/com/tangem/features/disclaimer/impl/component/impl/DefaultDisclaimerComponent.kt @@ -1,6 +1,5 @@ package com.tangem.features.disclaimer.impl.component.impl -import androidx.activity.compose.BackHandler import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier @@ -24,8 +23,6 @@ internal class DefaultDisclaimerComponent @AssistedInject constructor( @Composable override fun Content(modifier: Modifier) { val state by model.state.collectAsStateWithLifecycle() - - BackHandler(onBack = state.popBack) DisclaimerScreen(state = state) } diff --git a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/impl/DefaultManageTokensComponent.kt b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/impl/DefaultManageTokensComponent.kt index a66edf956d..76ae54a12d 100644 --- a/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/impl/DefaultManageTokensComponent.kt +++ b/features/manage-tokens/impl/src/main/kotlin/com/tangem/features/managetokens/component/impl/DefaultManageTokensComponent.kt @@ -1,6 +1,5 @@ package com.tangem.features.managetokens.component.impl -import androidx.activity.compose.BackHandler import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier @@ -42,8 +41,6 @@ internal class DefaultManageTokensComponent @AssistedInject constructor( val state by model.state.collectAsStateWithLifecycle() val bottomSheet by bottomSheetSlot.subscribeAsState() - BackHandler(onBack = state.popBack) - ManageTokensScreen( modifier = modifier, state = state, diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/DefaultMarketsTokenDetailsComponent.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/DefaultMarketsTokenDetailsComponent.kt index 3ee3a5edce..b24676e1f7 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/DefaultMarketsTokenDetailsComponent.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/details/impl/DefaultMarketsTokenDetailsComponent.kt @@ -124,10 +124,6 @@ internal class DefaultMarketsTokenDetailsComponent @AssistedInject constructor( @Composable override fun Content(modifier: Modifier) { - BackHandler { - navigateBack() - } - LifecycleStartEffect(Unit) { model.isVisibleOnScreen.value = true onStopOrDispose { diff --git a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/entry/impl/DefaultMarketsEntryComponent.kt b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/entry/impl/DefaultMarketsEntryComponent.kt index d175090f39..d2a5ed9d0b 100644 --- a/features/markets/impl/src/main/kotlin/com/tangem/features/markets/entry/impl/DefaultMarketsEntryComponent.kt +++ b/features/markets/impl/src/main/kotlin/com/tangem/features/markets/entry/impl/DefaultMarketsEntryComponent.kt @@ -40,7 +40,7 @@ internal class DefaultMarketsEntryComponent @AssistedInject constructor( source = stackNavigation, serializer = Child.serializer(), initialConfiguration = Child.TokenList, - handleBackButton = true, + handleBackButton = false, childFactory = { configuration, factoryContext -> marketsEntryChildFactory.createChild( child = configuration, diff --git a/features/referral/impl/src/main/java/com/tangem/feature/referral/ui/ReferralScreen.kt b/features/referral/impl/src/main/java/com/tangem/feature/referral/ui/ReferralScreen.kt index f5941be08d..5bf12360cb 100644 --- a/features/referral/impl/src/main/java/com/tangem/feature/referral/ui/ReferralScreen.kt +++ b/features/referral/impl/src/main/java/com/tangem/feature/referral/ui/ReferralScreen.kt @@ -2,7 +2,6 @@ package com.tangem.feature.referral.ui import android.content.res.Configuration import android.content.res.Resources -import androidx.activity.compose.BackHandler import androidx.annotation.DrawableRes import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -52,8 +51,6 @@ import kotlinx.coroutines.launch internal fun ReferralScreen(stateHolder: ReferralStateHolder) { val snackbarHostState = remember(::SnackbarHostState) - BackHandler(onBack = stateHolder.headerState.onBackClicked) - Scaffold( topBar = { AppBarWithBackButton( diff --git a/features/stories/impl/src/main/java/com/tangem/feature/stories/impl/DefaultStoriesComponent.kt b/features/stories/impl/src/main/java/com/tangem/feature/stories/impl/DefaultStoriesComponent.kt index 309d873b24..06f7b4b69f 100644 --- a/features/stories/impl/src/main/java/com/tangem/feature/stories/impl/DefaultStoriesComponent.kt +++ b/features/stories/impl/src/main/java/com/tangem/feature/stories/impl/DefaultStoriesComponent.kt @@ -1,12 +1,16 @@ package com.tangem.feature.stories.impl import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.Stable +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tangem.common.ui.swapStoriesScreen.SwapStoriesScreen import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.model.getOrCreateModel +import com.tangem.core.ui.res.LocalRootBackgroundColor +import com.tangem.core.ui.res.TangemColorPalette import com.tangem.feature.stories.api.StoriesComponent import com.tangem.feature.stories.impl.model.StoriesModel import dagger.assisted.Assisted @@ -25,6 +29,16 @@ internal class DefaultStoriesComponent @AssistedInject constructor( override fun Content(modifier: Modifier) { val state = model.state.collectAsStateWithLifecycle() SwapStoriesScreen(state.value) + + val rootBackgroundColor = LocalRootBackgroundColor.current + val previousColor = remember { rootBackgroundColor.value } + + DisposableEffect(Unit) { + rootBackgroundColor.value = TangemColorPalette.Black + onDispose { + rootBackgroundColor.value = previousColor + } + } } @AssistedFactory diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt index 03b631022d..93cf9bb969 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/ui/TokenDetailsScreen.kt @@ -1,7 +1,6 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.ui import android.content.res.Configuration -import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.* import androidx.compose.material3.Scaffold @@ -53,7 +52,6 @@ internal fun TokenDetailsScreen( tokenMarketBlockComponent: TokenMarketBlockComponent?, txHistoryComponent: TxHistoryComponent?, ) { - BackHandler(onBack = state.topAppBarConfig.onBackClick) val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() } Scaffold( diff --git a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/ui/WalletSettingsScreen.kt b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/ui/WalletSettingsScreen.kt index 675e86d521..165f5c1ca7 100644 --- a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/ui/WalletSettingsScreen.kt +++ b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/ui/WalletSettingsScreen.kt @@ -1,7 +1,6 @@ package com.tangem.feature.walletsettings.ui import android.content.res.Configuration -import androidx.activity.compose.BackHandler import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn @@ -37,8 +36,6 @@ internal fun WalletSettingsScreen( ) { val backgroundColor = TangemTheme.colors.background.secondary - BackHandler(onBack = state.popBack) - Scaffold( modifier = modifier, containerColor = backgroundColor, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/WalletComponent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/WalletComponent.kt index 7e8e90b260..21400eff17 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/WalletComponent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/WalletComponent.kt @@ -1,10 +1,8 @@ package com.tangem.feature.wallet.child.wallet -import androidx.activity.compose.BackHandler import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.arkivanov.decompose.extensions.compose.subscribeAsState import com.arkivanov.decompose.router.slot.childSlot @@ -16,7 +14,6 @@ import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.ui.decompose.ComposableBottomSheetComponent import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.core.ui.decompose.ComposableDialogComponent -import com.tangem.core.ui.utils.findActivity import com.tangem.feature.wallet.child.wallet.model.WalletModel import com.tangem.feature.wallet.navigation.WalletRoute import com.tangem.feature.wallet.presentation.wallet.state.model.WalletDialogConfig @@ -77,9 +74,6 @@ internal class WalletComponent @AssistedInject constructor( @Composable override fun Content(modifier: Modifier) { - val activity = LocalContext.current.findActivity() - BackHandler { activity.finish() } - val dialog by dialog.subscribeAsState() WalletScreen( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt index 01de3ee5da..7989bb0be0 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt @@ -8,6 +8,7 @@ import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.slideIn import androidx.compose.foundation.Canvas +import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.* @@ -24,7 +25,6 @@ import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.geometry.* import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Path -import androidx.compose.ui.graphics.luminance import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalSoftwareKeyboardController @@ -40,7 +40,6 @@ import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.paging.compose.collectAsLazyPagingItems -import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheet import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig import com.tangem.common.ui.expressStatus.expressTransactionsItems @@ -59,7 +58,6 @@ import com.tangem.core.ui.components.snackbar.TangemSnackbar import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.core.ui.event.StateEvent import com.tangem.core.ui.extensions.stringResourceSafe -import com.tangem.core.ui.res.LocalIsNavigationRefactoringEnabled import com.tangem.core.ui.res.LocalMainBottomSheetColor import com.tangem.core.ui.res.LocalWindowSize import com.tangem.core.ui.res.TangemTheme @@ -336,108 +334,121 @@ private inline fun BaseScaffoldWithMarkets( ) { val backgroundColor = LocalMainBottomSheetColor.current var isSearchFieldFocused by remember { mutableStateOf(false) } + val isNavBarVisible = remember { mutableStateOf(true) } BottomSheetStateEffects( bottomSheetState = bottomSheetState, alertConfig = alertConfig, onBottomSheetStateChange = onBottomSheetStateChange, + navigationBarVisible = isNavBarVisible, isSearchFieldFocused = isSearchFieldFocused, ) - TangemBottomSheetScaffold( - snackbarHost = { - WalletSnackbarHost( - snackbarHostState = it, - event = state.event, - modifier = Modifier - .padding(bottom = TangemTheme.dimens.spacing4) - .navigationBarsPadding(), - ) - }, - containerColor = TangemTheme.colors.background.secondary, - sheetContainerColor = backgroundColor.value, - scaffoldState = scaffoldState, - sheetPeekHeight = peekHeight, - sheetShape = TangemTheme.shapes.bottomSheetLarge, - sheetContent = { - // hide bottom sheet when back pressed - BackHandler( - isKeyboardVisible.not() && - bottomSheetState.currentValue == TangemSheetValue.Expanded, - ) { - coroutineScope.launch { bottomSheetState.partialExpand() } - } - - Column( - modifier = Modifier.sizeIn(maxHeight = maxHeight - statusBarHeight), - ) { - Hand(Modifier.drawBehind { drawRect(backgroundColor.value) }) - - Box( + Box { + TangemBottomSheetScaffold( + snackbarHost = { + WalletSnackbarHost( + snackbarHostState = it, + event = state.event, modifier = Modifier - // expand bottom sheet when clicked on the header - .clickable( - enabled = bottomSheetState.currentValue == TangemSheetValue.PartiallyExpanded, - indication = null, - interactionSource = null, - ) { - coroutineScope.launch { bottomSheetState.expand() } - } - .onFocusChanged { - isSearchFieldFocused = it.isFocused - }, - ) { - bottomSheetContent() - } - } - }, - content = { paddingValues -> - Box { - MarketsHint( - modifier = Modifier - .align(Alignment.BottomCenter) - .padding(bottom = peekHeight + 12.dp) - .fillMaxWidth(fraction = .4f), - isVisible = showMarketsHint, + .padding(bottom = TangemTheme.dimens.spacing4) + .navigationBarsPadding(), ) + }, + containerColor = TangemTheme.colors.background.secondary, + sheetContainerColor = backgroundColor.value, + scaffoldState = scaffoldState, + sheetPeekHeight = peekHeight, + sheetShape = TangemTheme.shapes.bottomSheetLarge, + sheetContent = { + // hide bottom sheet when back pressed + BackHandler( + isKeyboardVisible.not() && + bottomSheetState.currentValue == TangemSheetValue.Expanded, + ) { + coroutineScope.launch { bottomSheetState.partialExpand() } + } - Column { - WalletTopBar(config = state.topBarConfig) - TangemPullToRefreshContainer(config = selectedWallet.pullToRefreshConfig) { - content(paddingValues) + Column( + modifier = Modifier.sizeIn(maxHeight = maxHeight - statusBarHeight), + ) { + Hand(Modifier.drawBehind { drawRect(backgroundColor.value) }) + + Box( + modifier = Modifier + // expand bottom sheet when clicked on the header + .clickable( + enabled = bottomSheetState.currentValue == TangemSheetValue.PartiallyExpanded, + indication = null, + interactionSource = null, + ) { + coroutineScope.launch { bottomSheetState.expand() } + } + .onFocusChanged { + isSearchFieldFocused = it.isFocused + }, + ) { + bottomSheetContent() } } + }, + content = { paddingValues -> + Box { + MarketsHint( + modifier = Modifier + .align(Alignment.BottomCenter) + .padding(bottom = peekHeight + 12.dp) + .fillMaxWidth(fraction = .4f), + isVisible = showMarketsHint, + ) - BottomSheetScrim( - color = if (state.showMarketsOnboarding) { - Color.Black.copy(alpha = .65f) - } else { - BottomSheetDefaults.ScrimColor - }, - visible = bottomSheetState.targetValue == TangemSheetValue.Expanded || - state.showMarketsOnboarding, - onDismissRequest = { - coroutineScope.launch { bottomSheetState.partialExpand() } - state.onDismissMarketsOnboarding() - }, - ) + Column { + WalletTopBar(config = state.topBarConfig) + TangemPullToRefreshContainer(config = selectedWallet.pullToRefreshConfig) { + content(paddingValues) + } + } - MarketsTooltip( - modifier = Modifier - .align(Alignment.BottomCenter) - .padding(bottom = 24.dp) - .fillMaxWidth(fraction = 0.7f), - isVisible = state.showMarketsOnboarding, - availableHeight = if (LocalIsNavigationRefactoringEnabled.current) { - maxHeight - } else { - maxHeight - statusBarHeight - bottomBarHeight - }, - bottomSheetState = bottomSheetState, - ) - } - }, - ) + BottomSheetScrim( + color = if (state.showMarketsOnboarding) { + Color.Black.copy(alpha = .65f) + } else { + BottomSheetDefaults.ScrimColor + }, + visible = bottomSheetState.targetValue == TangemSheetValue.Expanded || + state.showMarketsOnboarding, + onDismissRequest = { + coroutineScope.launch { bottomSheetState.partialExpand() } + state.onDismissMarketsOnboarding() + }, + ) + + MarketsTooltip( + modifier = Modifier + .align(Alignment.BottomCenter) + .padding(bottom = 24.dp) + .fillMaxWidth(fraction = 0.7f), + isVisible = state.showMarketsOnboarding, + availableHeight = maxHeight, + bottomSheetState = bottomSheetState, + ) + } + }, + ) + + AnimatedVisibility( + modifier = Modifier.align(Alignment.BottomCenter), + visible = isNavBarVisible.value, + ) { + Box( + Modifier + .align(Alignment.BottomCenter) + .background(backgroundColor.value) + .height(bottomBarHeight) + .fillMaxWidth(), + ) + } + } LaunchedEffect(state.showMarketsOnboarding, bottomSheetState.targetValue) { if (state.showMarketsOnboarding && bottomSheetState.targetValue == TangemSheetValue.Expanded) { @@ -599,49 +610,17 @@ private fun BottomSheetScrim(color: Color, visible: Boolean, onDismissRequest: ( private fun BottomSheetStateEffects( bottomSheetState: TangemSheetState, alertConfig: WalletAlertState?, + navigationBarVisible: MutableState, onBottomSheetStateChange: (BottomSheetState) -> Unit, isSearchFieldFocused: Boolean, ) { - val systemUiController = rememberSystemUiController() - val navigationBarColor = TangemTheme.colors.background.primary - - LaunchedEffect(navigationBarColor) { - delay(timeMillis = 100) - when (bottomSheetState.currentValue) { - TangemSheetValue.Hidden, - TangemSheetValue.Expanded, - -> systemUiController.setNavigationBarColor( - color = Color.Transparent, - darkIcons = navigationBarColor.luminance() > 0.5f, - navigationBarContrastEnforced = true, - ) - TangemSheetValue.PartiallyExpanded, - -> systemUiController.setNavigationBarColor(navigationBarColor) - } - } - - LaunchedEffect(bottomSheetState.targetValue, navigationBarColor) { + LaunchedEffect(bottomSheetState.targetValue) { when (bottomSheetState.targetValue) { TangemSheetValue.Hidden, TangemSheetValue.Expanded, - -> systemUiController.setNavigationBarColor( - color = Color.Transparent, - darkIcons = navigationBarColor.luminance() > 0.5f, - navigationBarContrastEnforced = true, - ) + -> navigationBarVisible.value = false TangemSheetValue.PartiallyExpanded, - -> systemUiController.setNavigationBarColor(navigationBarColor) - } - } - - // make navigation bar transparent when leaving the screen - DisposableEffect(Unit) { - onDispose { - systemUiController.setNavigationBarColor( - color = Color.Transparent, - darkIcons = navigationBarColor.luminance() > 0.5f, - navigationBarContrastEnforced = false, - ) + -> navigationBarVisible.value = true } }