From 0dd4d45b62b375e05e7df52169a85d56d7eb722d Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 22 Sep 2023 11:36:12 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../com/tangem/tap/domain/TangemSdkManager.kt | 20 ++-- .../features/details/redux/DetailsAction.kt | 3 +- .../details/redux/DetailsMiddleware.kt | 30 ++++- .../features/details/redux/DetailsReducer.kt | 24 +++- .../features/details/redux/DetailsState.kt | 4 +- .../details/ui/resetcard/ResetCardScreen.kt | 110 +++++++++++------- .../ui/resetcard/ResetCardScreenState.kt | 5 +- .../ui/resetcard/ResetCardViewModel.kt | 7 +- core/res/src/main/res/values-ru/strings.xml | 1 - .../src/main/res/values-zh-rTW/strings.xml | 1 - core/res/src/main/res/values/strings.xml | 3 +- 11 files changed, 141 insertions(+), 67 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt index d692f0504a..bbf617b817 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -61,6 +61,9 @@ class TangemSdkManager( val biometricManager: BiometricManager get() = tangemSdk.biometricManager + val userCodeRequestPolicy: UserCodeRequestPolicy + get() = tangemSdk.config.userCodeRequestPolicy + suspend fun scanProduct( cardId: String? = null, messageRes: Int? = null, @@ -138,7 +141,9 @@ class TangemSdkManager( allowsRequestAccessCodeFromRepository: Boolean, ): CompletionResult { return runTaskAsyncReturnOnMain( - runnable = ResetToFactorySettingsTask(allowsRequestAccessCodeFromRepository), + runnable = ResetToFactorySettingsTask( + allowsRequestAccessCodeFromRepository = allowsRequestAccessCodeFromRepository, + ), cardId = cardId, initialMessage = Message(resources.getString(R.string.card_settings_reset_card_to_factory)), ) @@ -243,17 +248,8 @@ class TangemSdkManager( return resources.getString(stringResId, *formatArgs) } - fun setAccessCodeRequestPolicy(useBiometricsForAccessCode: Boolean) { - tangemSdk.config.userCodeRequestPolicy = if (useBiometricsForAccessCode) { - UserCodeRequestPolicy.AlwaysWithBiometrics(codeType = UserCodeType.AccessCode) - } else { - UserCodeRequestPolicy.Default - } - } - - fun useBiometricsForAccessCode(): Boolean { - val policy = tangemSdk.config.userCodeRequestPolicy - return policy is UserCodeRequestPolicy.AlwaysWithBiometrics && policy.codeType == UserCodeType.AccessCode + fun setUserCodeRequestPolicy(policy: UserCodeRequestPolicy) { + tangemSdk.config.userCodeRequestPolicy = policy } companion object { diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt index 4bef087c68..9dd0c59cef 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt @@ -21,7 +21,8 @@ sealed class DetailsAction : Action { sealed class ResetToFactory : DetailsAction() { object Start : ResetToFactory() object Proceed : ResetToFactory() - data class Confirm(val confirmed: Boolean) : ResetToFactory() + data class AcceptCondition1(val accepted: Boolean) : ResetToFactory() + data class AcceptCondition2(val accepted: Boolean) : ResetToFactory() object Failure : ResetToFactory() object Success : ResetToFactory() } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt index 1d0b13e4e3..1c13b7ed80 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt @@ -1,13 +1,11 @@ package com.tangem.tap.features.details.redux import androidx.lifecycle.LifecycleCoroutineScope -import com.tangem.common.CompletionResult +import com.tangem.common.* import com.tangem.common.core.TangemError import com.tangem.common.core.TangemSdkError -import com.tangem.common.doOnFailure -import com.tangem.common.doOnSuccess +import com.tangem.common.core.UserCodeRequestPolicy import com.tangem.common.extensions.guard -import com.tangem.common.flatMap import com.tangem.core.analytics.Analytics import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction @@ -103,6 +101,27 @@ class DetailsMiddleware { scope.launch { val userWalletId = UserWalletIdBuilder.card(card).build() + // we must require a password regardless of biometric settings + val policy = tangemSdkManager.userCodeRequestPolicy + val doBeforeErase = { + val type = if (card.isAccessCodeSet) { + UserCodeType.AccessCode + } else if (card.isPasscodeSet == true) { + UserCodeType.Passcode + } else { + null + } + + type?.let { + tangemSdkManager.setUserCodeRequestPolicy(UserCodeRequestPolicy.Always(type)) + } + } + + val doAfterErase = { + tangemSdkManager.setUserCodeRequestPolicy(policy) + } + + doBeforeErase() tangemSdkManager.resetToFactorySettings(card.cardId, true) .flatMap { userWalletsListManager.delete(listOfNotNull(userWalletId)) } .flatMap { tangemSdkManager.deleteSavedUserCodes(setOf(card.cardId)) } @@ -126,6 +145,9 @@ class DetailsMiddleware { Analytics.send(Settings.CardSettings.FactoryResetFinished(error)) } } + .doOnResult { + doAfterErase() + } } } else -> Unit diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt index 6c464d6275..e861e52c60 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt @@ -93,6 +93,9 @@ private fun handlePrepareCardSettingsScreen( manageSecurityState = prepareSecurityOptions(card, cardTypesResolver), card = card, resetCardAllowed = isResetToFactoryAllowedByCard(card, cardTypesResolver), + resetButtonEnabled = false, + condition1Checked = false, + condition2Checked = false, accessCodeRecovery = if (cardTypesResolver.isWallet2()) { val enabled = card.userSettings?.isUserCodeRecoveryAllowed ?: false AccessCodeRecoveryState( @@ -145,8 +148,25 @@ private fun isResetToFactoryAllowedByCard(card: CardDTO, cardTypesResolver: Card private fun handleEraseWallet(action: DetailsAction.ResetToFactory, state: DetailsState): DetailsState { return when (action) { - is DetailsAction.ResetToFactory.Confirm -> - state.copy(cardSettingsState = state.cardSettingsState?.copy(resetConfirmed = action.confirmed)) + is DetailsAction.ResetToFactory.AcceptCondition1 -> { + val warning1Checked = action.accepted + state.copy( + cardSettingsState = state.cardSettingsState?.copy( + condition1Checked = action.accepted, + resetButtonEnabled = warning1Checked && state.cardSettingsState.condition2Checked, + ), + ) + } + is DetailsAction.ResetToFactory.AcceptCondition2 -> { + val warning2Checked = action.accepted + state.copy( + cardSettingsState = state.cardSettingsState?.copy( + condition2Checked = action.accepted, + resetButtonEnabled = warning2Checked && state.cardSettingsState.condition1Checked, + ), + ) + } + else -> state } } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt index 68c663534e..3e49b20905 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsState.kt @@ -41,7 +41,9 @@ data class CardSettingsState( val card: CardDTO, val manageSecurityState: ManageSecurityState?, val resetCardAllowed: Boolean, - val resetConfirmed: Boolean = false, + val resetButtonEnabled: Boolean, + val condition1Checked: Boolean, + val condition2Checked: Boolean, val accessCodeRecovery: AccessCodeRecoveryState? = null, ) diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreen.kt b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreen.kt index d7b85a7202..aa76d59a80 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreen.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreen.kt @@ -1,5 +1,7 @@ package com.tangem.tap.features.details.ui.resetcard +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* @@ -33,6 +35,7 @@ internal fun ResetCardScreen(state: ResetCardScreenState, onBackClick: () -> Uni ) } +@OptIn(ExperimentalAnimationApi::class) @Suppress("LongMethod", "MagicNumber") @Composable private fun ResetCardView(state: ResetCardScreenState) { @@ -76,54 +79,78 @@ private fun ResetCardView(state: ResetCardScreenState) { ) Spacer(modifier = Modifier.size(28.dp)) - Row( - modifier = Modifier - .fillMaxWidth() - .clickable( - onClick = { state.onAcceptWarningToggleClick(!state.accepted) }, - ) - .padding(top = 16.dp, bottom = 16.dp), - ) { - IconToggleButton( - checked = state.accepted, - onCheckedChange = state.onAcceptWarningToggleClick, - modifier = Modifier.padding(start = 20.dp, end = 20.dp), - ) { - Icon( - painter = painterResource( - if (state.accepted) { - R.drawable.ic_accepted - } else { - R.drawable.ic_unticked - }, - ), - contentDescription = null, - tint = if (state.accepted) { - TangemTheme.colors.icon.accent - } else { - TangemTheme.colors.icon.secondary - }, - ) - } - Text( - text = stringResource(id = R.string.reset_card_to_factory_warning_message), - style = TangemTheme.typography.body2, - color = TangemTheme.colors.text.secondary, - modifier = Modifier.padding(end = 20.dp), - ) - } + + ConditionCheckBox( + checkedState = state.acceptCondition1Checked, + onCheckedChange = state.onAcceptCondition1ToggleClick, + description = TextReference.Res(R.string.reset_card_to_factory_condition_1), + ) + + ConditionCheckBox( + checkedState = state.acceptCondition2Checked, + onCheckedChange = state.onAcceptCondition2ToggleClick, + description = TextReference.Res(R.string.reset_card_to_factory_condition_2), + ) Spacer(modifier = Modifier.size(16.dp)) Box( modifier = Modifier.padding(start = 16.dp, end = 16.dp, bottom = 32.dp), ) { - DetailsMainButton( - title = stringResource(id = R.string.reset_card_to_factory_button_title), - onClick = state.onResetButtonClick, - enabled = state.resetButtonEnabled, + AnimatedContent( + targetState = state.resetButtonEnabled, + label = "Update checked state", + ) { buttonEnabled -> + DetailsMainButton( + title = stringResource(id = R.string.reset_card_to_factory_button_title), + onClick = state.onResetButtonClick, + enabled = buttonEnabled, + ) + } + } + } + } +} + +@OptIn(ExperimentalAnimationApi::class) +@Composable +private fun ConditionCheckBox(checkedState: Boolean, onCheckedChange: (Boolean) -> Unit, description: TextReference) { + Row( + modifier = Modifier + .fillMaxWidth() + .clickable( + onClick = { onCheckedChange.invoke(!checkedState) }, + ) + .padding(top = TangemTheme.dimens.size16, bottom = TangemTheme.dimens.size16), + ) { + IconToggleButton( + checked = checkedState, + onCheckedChange = onCheckedChange, + modifier = Modifier.padding(start = TangemTheme.dimens.size20, end = TangemTheme.dimens.size20), + ) { + AnimatedContent(targetState = checkedState, label = "Update checked state") { checked -> + Icon( + painter = painterResource( + if (checked) { + R.drawable.ic_accepted + } else { + R.drawable.ic_unticked + }, + ), + contentDescription = null, + tint = if (checked) { + TangemTheme.colors.icon.accent + } else { + TangemTheme.colors.icon.secondary + }, ) } } + Text( + text = description.resolveReference(), + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.secondary, + modifier = Modifier.padding(end = TangemTheme.dimens.size20), + ) } } @@ -138,7 +165,8 @@ private fun ResetCardScreenSample(modifier: Modifier = Modifier) { state = ResetCardScreenState( accepted = true, descriptionText = TextReference.Res(R.string.reset_card_with_backup_to_factory_message), - onAcceptWarningToggleClick = {}, + onAcceptCondition1ToggleClick = {}, + onAcceptCondition2ToggleClick = {}, onResetButtonClick = {}, ), onBackClick = {}, diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreenState.kt b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreenState.kt index b4f297d11c..97f7a9776e 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreenState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreenState.kt @@ -5,7 +5,10 @@ import com.tangem.tap.features.details.ui.cardsettings.TextReference internal data class ResetCardScreenState( val accepted: Boolean = false, val descriptionText: TextReference, - val onAcceptWarningToggleClick: (Boolean) -> Unit, + val acceptCondition1Checked: Boolean = false, + val acceptCondition2Checked: Boolean = false, + val onAcceptCondition1ToggleClick: (Boolean) -> Unit, + val onAcceptCondition2ToggleClick: (Boolean) -> Unit, val onResetButtonClick: () -> Unit, ) { val resetButtonEnabled: Boolean diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardViewModel.kt b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardViewModel.kt index b694554734..00d9f515e2 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardViewModel.kt @@ -15,9 +15,12 @@ internal class ResetCardViewModel(private val store: Store) { ?: TextReference.Str(value = "") return ResetCardScreenState( - accepted = state?.resetConfirmed ?: false, + accepted = state?.resetButtonEnabled ?: false, descriptionText = descriptionText, - onAcceptWarningToggleClick = { store.dispatch(DetailsAction.ResetToFactory.Confirm(it)) }, + acceptCondition1Checked = state?.condition1Checked ?: false, + acceptCondition2Checked = state?.condition2Checked ?: false, + onAcceptCondition1ToggleClick = { store.dispatch(DetailsAction.ResetToFactory.AcceptCondition1(it)) }, + onAcceptCondition2ToggleClick = { store.dispatch(DetailsAction.ResetToFactory.AcceptCondition2(it)) }, onResetButtonClick = { store.dispatch(DetailsAction.ResetToFactory.Proceed) }, ) } diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 510b31b595..2a4a4d51d7 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -351,7 +351,6 @@ Пожалуйста, удерживайте карту до завершения операции Сбросить карту - Я понимаю, что после выполнения этого действия у меня больше не будет доступа к текущему кошельку Сброс к заводским настройкам приведет к полному удалению кошелька с выбранной карты. Вы не сможете восстановить текущий кошелек или использовать данную карту для восстановления кода доступа. Сброс к заводским настройкам приведет к полному удалению кошелька с выбранной карты. Вы не сможете восстановить текущий кошелек. У вас есть карта банка другой страны, а также вид на жительство или регистрация вне РФ? diff --git a/core/res/src/main/res/values-zh-rTW/strings.xml b/core/res/src/main/res/values-zh-rTW/strings.xml index 545a1272b2..01a2c72b3e 100644 --- a/core/res/src/main/res/values-zh-rTW/strings.xml +++ b/core/res/src/main/res/values-zh-rTW/strings.xml @@ -277,7 +277,6 @@ 請持有卡片直至操作完成 重置卡片 - 我了解執行此操作後,我將無法再訪問當前錢包 恢復原廠設置將從所選卡中完全刪除錢包。您將無法恢復當前錢包或使用卡恢復訪問密碼 恢復原廠設置將從所選卡中完全刪除錢包並將其從應用程序中刪除。您將無法恢復當前錢包 目前不接受俄羅斯銀行卡 diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 660803875f..1788e649be 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -371,7 +371,8 @@ Please hold the card until the operation complete Reset the Card - I understand that after performing this action, I will no longer have access to the current wallet + I realize that I\'ll lose access to my funds on this card after this action. + I realize that I can\'t use this card to recover my access code on the other cards. Factory Reset will completely delete the wallet from the selected card. You will not be able to restore the current wallet or use the card to recover the access code. Factory Reset will completely delete the wallet from the selected card and remove it from the app. You will not be able to restore the current wallet. Do you have a bank card from another country and a residence permit or registration outside the Russian Federation?