Updated on 2026-08-14

This commit is contained in:
Tangem 2025-09-22 14:24:06 +05:00
parent 3306749ae5
commit b98b66ff9f
6 changed files with 84 additions and 46 deletions

View file

@ -31,6 +31,7 @@ import com.tangem.core.ui.message.EventMessageEffect
import com.tangem.core.ui.res.LocalRootBackgroundColor
import com.tangem.core.ui.res.LocalSnackbarHostState
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.security.ProvideSecureFlagController
import com.tangem.tap.routing.component.RoutingComponent
import com.tangem.tap.routing.transitions.RoutingTransitionAnimationFactory
@ -52,39 +53,41 @@ internal fun RootContent(
activity = context as Activity,
uiDependencies = uiDependencies,
) {
val snackbarHostState = LocalSnackbarHostState.current
ProvideSecureFlagController {
val snackbarHostState = LocalSnackbarHostState.current
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)
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)
}
}
}
}
wcContent(Modifier.fillMaxSize())
hotAccessCodeContent(Modifier.fillMaxSize())
TangemSnackbarHost(
modifier = Modifier
.align(Alignment.BottomCenter)
.navigationBarsPadding()
.padding(all = 16.dp),
hostState = snackbarHostState,
)
}
wcContent(Modifier.fillMaxSize())
hotAccessCodeContent(Modifier.fillMaxSize())
TangemSnackbarHost(
modifier = Modifier
.align(Alignment.BottomCenter)
.navigationBarsPadding()
.padding(all = 16.dp),
hostState = snackbarHostState,
)
}
EventMessageEffect()
}

View file

@ -1,11 +1,53 @@
package com.tangem.core.ui.security
import android.app.Activity
import android.view.WindowManager
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.platform.LocalContext
import com.tangem.core.ui.utils.findActivity
import timber.log.Timber
private val LocalSecureFlagController = staticCompositionLocalOf<SecureFlagController> {
error("No SecureFlagController provided")
}
private class SecureFlagController(private val activity: Activity) {
private var count by mutableIntStateOf(0)
fun enable() {
if (count == 0) {
activity.window.setFlags(
WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE,
)
}
count++
}
fun disable() {
count--
if (count == 0) {
activity.window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
}
}
}
@Composable
fun ProvideSecureFlagController(content: @Composable () -> Unit) {
val activity = LocalContext.current.findActivity()
val controller = remember(activity) { SecureFlagController(activity) }
CompositionLocalProvider(
LocalSecureFlagController provides controller,
content = content,
)
}
/**
* Disables screenshots for the current composition.
@ -14,18 +56,10 @@ import timber.log.Timber
*/
@Composable
fun DisableScreenshotsDisposableEffect() {
val activity = LocalContext.current.findActivity()
val secureFlagController = LocalSecureFlagController.current
DisposableEffect(activity) {
Timber.d("Security mode: enabled")
activity.window.setFlags(
WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE,
)
onDispose {
Timber.d("Security mode: disabled")
activity.window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
}
DisposableEffect(secureFlagController) {
secureFlagController.enable()
onDispose { secureFlagController.disable() }
}
}

View file

@ -25,10 +25,7 @@ internal class AccessCodeComponent @AssistedInject constructor(
override fun Content(modifier: Modifier) {
val state by model.uiState.collectAsStateWithLifecycle()
if (!state.isConfirmMode) {
DisableScreenshotsDisposableEffect()
}
DisableScreenshotsDisposableEffect()
AccessCode(
modifier = modifier,
state = state,

View file

@ -7,6 +7,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.security.DisableScreenshotsDisposableEffect
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.features.hotwallet.addexistingwallet.im.port.model.AddExistingWalletImportModel
import com.tangem.features.hotwallet.addexistingwallet.im.port.ui.AddExistingWalletImportContent
@ -22,6 +23,7 @@ internal class AddExistingWalletImportComponent @AssistedInject constructor(
@Composable
override fun Content(modifier: Modifier) {
val state by model.uiState.collectAsStateWithLifecycle()
DisableScreenshotsDisposableEffect()
AddExistingWalletImportContent(
state = state,
modifier = modifier,

View file

@ -7,6 +7,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.security.DisableScreenshotsDisposableEffect
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.features.hotwallet.manualbackup.check.model.ManualBackupCheckModel
import com.tangem.features.hotwallet.manualbackup.check.ui.ManualBackupCheckContent
@ -22,6 +23,7 @@ internal class ManualBackupCheckComponent @AssistedInject constructor(
@Composable
override fun Content(modifier: Modifier) {
val state by model.uiState.collectAsStateWithLifecycle()
DisableScreenshotsDisposableEffect()
ManualBackupCheckContent(
state = state,
modifier = modifier,

View file

@ -63,7 +63,7 @@ internal class WalletActivationModel @Inject constructor(
is WalletActivationRoute.ManualBackupCheck -> stackNavigation.pop()
is WalletActivationRoute.ManualBackupCompleted -> Unit
is WalletActivationRoute.SetAccessCode -> Unit
is WalletActivationRoute.ConfirmAccessCode -> Unit
is WalletActivationRoute.ConfirmAccessCode -> stackNavigation.pop()
is WalletActivationRoute.PushNotifications -> Unit
is WalletActivationRoute.SetupFinished -> Unit
}