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 c9d4a5e39c..3dcacf1eef 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -31,6 +31,7 @@ import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask import com.tangem.operations.pins.CheckUserCodesCommand import com.tangem.operations.pins.CheckUserCodesResponse import com.tangem.operations.pins.SetUserCodeCommand +import com.tangem.operations.usersetttings.SetUserCodeRecoveryAllowedTask import com.tangem.tap.common.analytics.events.Basic import com.tangem.tap.domain.tasks.CreateWalletAndRescanTask import com.tangem.tap.domain.tasks.product.CreateProductWalletTask @@ -173,6 +174,14 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co ) } + suspend fun setAccessCodeRecoveryEnabled(cardId: String?, enabled: Boolean): CompletionResult { + return runTaskAsyncReturnOnMain( + SetUserCodeRecoveryAllowedTask(enabled), + cardId, + initialMessage = Message(context.getString(R.string.initial_message_tap_header)), + ) + } + suspend fun scanCard( cardId: String? = null, allowRequestAccessCodeFromRepository: Boolean = false, 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 6fe638daca..d7bcd69528 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 @@ -29,6 +29,15 @@ sealed class DetailsAction : Action { data class PrepareCardSettingsData(val card: CardDTO, val cardTypesResolver: CardTypesResolver) : DetailsAction() object ResetCardSettingsData : DetailsAction() + sealed class AccessCodeRecovery : DetailsAction() { + object Open : AccessCodeRecovery() + data class SaveChanges(val enabled: Boolean) : AccessCodeRecovery() { + data class Success(val enabled: Boolean) : AccessCodeRecovery() + } + + data class SelectOption(val enabled: Boolean) : AccessCodeRecovery() + } + sealed class ManageSecurity : DetailsAction() { object OpenSecurity : ManageSecurity() data class SelectOption(val option: SecurityOption) : ManageSecurity() 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 e7eab8cc49..de6be315af 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 @@ -51,6 +51,7 @@ class DetailsMiddleware { private val eraseWalletMiddleware = EraseWalletMiddleware() private val manageSecurityMiddleware = ManageSecurityMiddleware() private val managePrivacyMiddleware = ManagePrivacyMiddleware() + private val accessCodeRecoveryMiddleware = AccessCodeRecoveryMiddleware() val detailsMiddleware: Middleware = { _, stateProvider -> { next -> { action -> @@ -74,6 +75,7 @@ class DetailsMiddleware { store.dispatch(TwinCardsAction.SetMode(CreateTwinWalletMode.RecreateWallet)) store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingTwins)) } + is DetailsAction.AccessCodeRecovery -> accessCodeRecoveryMiddleware.handle(state, action) DetailsAction.ScanCard -> { scope.launch { tangemSdkManager.scanProduct( @@ -418,4 +420,28 @@ class DetailsMiddleware { ) } } + + class AccessCodeRecoveryMiddleware { + fun handle(state: DetailsState, action: DetailsAction.AccessCodeRecovery) { + when (action) { + is DetailsAction.AccessCodeRecovery.Open -> { + // store.dispatch(NavigationAction.NavigateTo(AppScreen.AccessCodeRecovery)) Todo: next PR + } + is DetailsAction.AccessCodeRecovery.SaveChanges -> { + scope.launch { + tangemSdkManager + .setAccessCodeRecoveryEnabled(state.cardSettingsState?.card?.cardId, action.enabled) + .doOnSuccess { + store.dispatchOnMain(NavigationAction.PopBackTo()) + store.dispatchOnMain( + DetailsAction.AccessCodeRecovery.SaveChanges.Success(action.enabled), + ) + } + } + } + is DetailsAction.AccessCodeRecovery.SelectOption -> Unit + is DetailsAction.AccessCodeRecovery.SaveChanges.Success -> Unit + } + } + } } \ No newline at end of file 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 380d7be88b..b72be6d5d8 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 @@ -40,6 +40,7 @@ private fun internalReduce(action: Action, state: AppState): DetailsState { } is DetailsAction.ChangeAppCurrency -> detailsState.copy(appCurrency = action.fiatCurrency) + is DetailsAction.AccessCodeRecovery -> handleAccessCodeRecoveryAction(action, detailsState) else -> detailsState } } @@ -68,6 +69,15 @@ private fun handlePrepareCardSettingsScreen( manageSecurityState = prepareSecurityOptions(card, cardTypesResolver), card = card, resetCardAllowed = isResetToFactoryAllowedByCard(card, cardTypesResolver), + accessCodeRecovery = if (cardTypesResolver.isWallet2()) { + val enabled = card.userSettings?.isUserCodeRecoveryAllowed ?: false + AccessCodeRecoveryState( + enabledOnCard = enabled, + enabledSelection = enabled, + ) + } else { + null + }, ) return state.copy(cardSettingsState = cardSettingsState) } @@ -189,6 +199,34 @@ private fun handlePrivacyAction(action: DetailsAction.AppSettings, state: Detail } } +private fun handleAccessCodeRecoveryAction( + action: DetailsAction.AccessCodeRecovery, + state: DetailsState, +): DetailsState { + return when (action) { + DetailsAction.AccessCodeRecovery.Open -> { + val accessCodeRecovery = state.cardSettingsState?.accessCodeRecovery?.copy( + enabledSelection = state.cardSettingsState.accessCodeRecovery.enabledOnCard, + ) + state.copy(cardSettingsState = state.cardSettingsState?.copy(accessCodeRecovery = accessCodeRecovery)) + } + is DetailsAction.AccessCodeRecovery.SaveChanges -> state + is DetailsAction.AccessCodeRecovery.SelectOption -> { + val accessCodeRecovery = state.cardSettingsState?.accessCodeRecovery?.copy( + enabledSelection = action.enabled, + ) + state.copy(cardSettingsState = state.cardSettingsState?.copy(accessCodeRecovery = accessCodeRecovery)) + } + is DetailsAction.AccessCodeRecovery.SaveChanges.Success -> { + val accessCodeRecovery = state.cardSettingsState?.accessCodeRecovery?.copy( + enabledOnCard = action.enabled, + enabledSelection = action.enabled, + ) + state.copy(cardSettingsState = state.cardSettingsState?.copy(accessCodeRecovery = accessCodeRecovery)) + } + } +} + private fun prepareAllowedSecurityOptions( cardTypesResolver: CardTypesResolver, currentSecurityOption: SecurityOption?, 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 62179b433d..0fc60cafcc 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 @@ -26,12 +26,22 @@ data class CardInfo( val hasBackup: Boolean, ) +/** + * @property enabledOnCard whether access code recovery is enabled on card + * @property enabledSelection current selected option in app (not saved on card yet) + */ +data class AccessCodeRecoveryState( + val enabledOnCard: Boolean, + val enabledSelection: Boolean, +) + data class CardSettingsState( val cardInfo: CardInfo, val card: CardDTO, val manageSecurityState: ManageSecurityState?, val resetCardAllowed: Boolean, val resetConfirmed: Boolean = false, + val accessCodeRecovery: AccessCodeRecoveryState? = null, ) data class ManageSecurityState( diff --git a/domain/src/main/java/com/tangem/domain/common/CardDTO.kt b/domain/src/main/java/com/tangem/domain/common/CardDTO.kt index ffe33e01e2..38494e9294 100644 --- a/domain/src/main/java/com/tangem/domain/common/CardDTO.kt +++ b/domain/src/main/java/com/tangem/domain/common/CardDTO.kt @@ -23,6 +23,7 @@ data class CardDTO( val manufacturer: Manufacturer, val issuer: Issuer, val settings: Settings, + val userSettings: UserSettings?, val linkedTerminalStatus: LinkedTerminalStatus, val isAccessCodeSet: Boolean, val isPasscodeSet: Boolean?, @@ -39,6 +40,7 @@ data class CardDTO( manufacturer = Manufacturer(card.manufacturer), issuer = Issuer(card.issuer), settings = Settings(card.settings), + userSettings = UserSettings(card.userSettings), linkedTerminalStatus = LinkedTerminalStatus.fromSdkStatus(card.linkedTerminalStatus), isAccessCodeSet = card.isAccessCodeSet, isPasscodeSet = card.isPasscodeSet, @@ -60,6 +62,7 @@ data class CardDTO( if (manufacturer != other.manufacturer) return false if (issuer != other.issuer) return false if (settings != other.settings) return false + if (userSettings != other.userSettings) return false if (linkedTerminalStatus != other.linkedTerminalStatus) return false if (isAccessCodeSet != other.isAccessCodeSet) return false if (isPasscodeSet != other.isPasscodeSet) return false @@ -78,6 +81,7 @@ data class CardDTO( result = 31 * result + manufacturer.hashCode() result = 31 * result + issuer.hashCode() result = 31 * result + settings.hashCode() + result = 31 * result + userSettings.hashCode() result = 31 * result + linkedTerminalStatus.hashCode() result = 31 * result + isAccessCodeSet.hashCode() result = 31 * result + (isPasscodeSet?.hashCode() ?: 0) @@ -114,6 +118,15 @@ data class CardDTO( ) } + @JsonClass(generateAdapter = true) + data class UserSettings( + val isUserCodeRecoveryAllowed: Boolean, + ) { + constructor(userSettings: com.tangem.common.card.UserSettings) : this( + userSettings.isUserCodeRecoveryAllowed, + ) + } + @JsonClass(generateAdapter = true) data class FirmwareVersion( val major: Int, diff --git a/domain/src/main/java/com/tangem/domain/common/CardTypesResolver.kt b/domain/src/main/java/com/tangem/domain/common/CardTypesResolver.kt index a938ae53b0..0d91e233df 100644 --- a/domain/src/main/java/com/tangem/domain/common/CardTypesResolver.kt +++ b/domain/src/main/java/com/tangem/domain/common/CardTypesResolver.kt @@ -6,6 +6,7 @@ import com.tangem.blockchain.common.Token interface CardTypesResolver { fun isTangemNote(): Boolean fun isTangemWallet(): Boolean + fun isWallet2(): Boolean fun isSaltPay(): Boolean fun isSaltPayVisa(): Boolean fun isSaltPayWallet(): Boolean diff --git a/domain/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt b/domain/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt index 5d9f6fafa7..50850def2c 100644 --- a/domain/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt +++ b/domain/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt @@ -24,6 +24,7 @@ class TangemCardTypesResolver( card.firmwareVersion >= FirmwareVersion.MultiWalletAvailable && !card.isSaltPay + override fun isWallet2(): Boolean = card.firmwareVersion >= FirmwareVersion.KeysImportAvailable override fun isSaltPay(): Boolean = productType == ProductType.SaltPay override fun isSaltPayVisa(): Boolean = card.isSaltPayVisa override fun isSaltPayWallet(): Boolean = card.isSaltPayWallet