From 283bc49d3a0518b318eabc7ae915972682535121 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sun, 18 Feb 2024 14:37:31 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../java/com/tangem/tap/TapApplication.kt | 5 + .../tap/common/redux/global/GlobalReducer.kt | 4 +- .../domain/tasks/product/ScanProductTask.kt | 36 +++-- .../features/onboarding/OnboardingHelper.kt | 12 +- .../features/onboarding/OnboardingManager.kt | 29 ++-- .../note/redux/OnboardingNoteMiddleware.kt | 15 +- .../redux/OnboardingOtherCardsMiddleware.kt | 15 +- .../twins/redux/TwinCardsMiddleware.kt | 72 +++++---- .../redux/OnboardingWalletMiddleware.kt | 89 +++++------ .../tap/proxy/redux/DaggerGraphState.kt | 2 + .../di/AppPreferencesStoreModule.kt | 2 +- .../local/preferences/AppPreferencesStore.kt | 11 +- .../tangem/data/card/DefaultCardRepository.kt | 78 +++++++-- .../preferences/PreferencesDataSource.kt | 11 -- .../model/DataSourceUsedCardInfo.kt | 8 - .../model/DataSourceUsedCardInfoOld.kt | 7 - .../storage/UsedCardsPrefStorage.kt | 150 ------------------ .../domain/card/repository/CardRepository.kt | 15 ++ 18 files changed, 247 insertions(+), 314 deletions(-) delete mode 100644 data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/model/DataSourceUsedCardInfo.kt delete mode 100644 data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/model/DataSourceUsedCardInfoOld.kt delete mode 100644 data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/storage/UsedCardsPrefStorage.kt diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index 83452572ae..794eed1b33 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -30,6 +30,7 @@ import com.tangem.domain.apptheme.GetAppThemeModeUseCase import com.tangem.domain.apptheme.repository.AppThemeModeRepository import com.tangem.domain.balancehiding.repositories.BalanceHidingRepository import com.tangem.domain.card.ScanCardProcessor +import com.tangem.domain.card.repository.CardRepository import com.tangem.domain.common.LogConfig import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase @@ -170,6 +171,9 @@ internal class TapApplication : Application(), ImageLoaderFactory { @Inject lateinit var saveTwinsOnboardingShownUseCase: SaveTwinsOnboardingShownUseCase + + @Inject + lateinit var cardRepository: CardRepository // endregion Injected override fun onCreate() { @@ -256,6 +260,7 @@ internal class TapApplication : Application(), ImageLoaderFactory { generalUserWalletsListManager = generalUserWalletsListManager, wasTwinsOnboardingShownUseCase = wasTwinsOnboardingShownUseCase, saveTwinsOnboardingShownUseCase = saveTwinsOnboardingShownUseCase, + cardRepository = cardRepository, ), ), ) diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt index 778a37590c..fbe3e38b89 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt @@ -7,7 +7,6 @@ import com.tangem.tap.common.extensions.inject import com.tangem.tap.common.redux.AppState import com.tangem.tap.features.home.redux.HomeAction import com.tangem.tap.features.onboarding.OnboardingManager -import com.tangem.tap.preferencesStorage import com.tangem.tap.proxy.AppStateHolder import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.tap.store @@ -22,8 +21,7 @@ fun globalReducer(action: Action, state: AppState, appStateHolder: AppStateHolde return when (action) { is GlobalAction.Onboarding.Start -> { - val usedCardsPrefStorage = preferencesStorage.usedCardsPrefStorage - val onboardingManager = OnboardingManager(action.scanResponse, usedCardsPrefStorage) + val onboardingManager = OnboardingManager(action.scanResponse) globalState.copy(onboardingState = OnboardingState(true, onboardingManager)) } is GlobalAction.Onboarding.StartForUnfinishedBackup -> { diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt index 8d811541cf..8f065eda68 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt @@ -31,9 +31,12 @@ import com.tangem.operations.backup.StartPrimaryCardLinkingTask import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask import com.tangem.operations.files.ReadFilesTask import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand +import com.tangem.tap.common.extensions.inject import com.tangem.tap.domain.TapSdkError -import com.tangem.tap.preferencesStorage +import com.tangem.tap.mainScope +import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.tap.scope +import com.tangem.tap.store import kotlinx.coroutines.launch import kotlin.collections.set @@ -185,23 +188,28 @@ private class ScanWalletProcessor( session: CardSession, callback: (result: CompletionResult) -> Unit, ) { - val activationInProgress = preferencesStorage.usedCardsPrefStorage.isActivationInProgress(card.cardId) + mainScope.launch { + val activationInProgress = store.inject(DaggerGraphState::cardRepository) + .isActivationInProgress(card.cardId) - @Suppress("ComplexCondition") - if (card.backupStatus == CardDTO.BackupStatus.NoBackup && card.wallets.isNotEmpty() && activationInProgress) { - StartPrimaryCardLinkingTask().run(session) { linkingResult -> - when (linkingResult) { - is CompletionResult.Success -> { - primaryCard = linkingResult.data - deriveKeysIfNeeded(card, session, callback) - } - is CompletionResult.Failure -> { - deriveKeysIfNeeded(card, session, callback) + @Suppress("ComplexCondition") + if (card.backupStatus == CardDTO.BackupStatus.NoBackup && card.wallets.isNotEmpty() && + activationInProgress + ) { + StartPrimaryCardLinkingTask().run(session) { linkingResult -> + when (linkingResult) { + is CompletionResult.Success -> { + primaryCard = linkingResult.data + deriveKeysIfNeeded(card, session, callback) + } + is CompletionResult.Failure -> { + deriveKeysIfNeeded(card, session, callback) + } } } + } else { + deriveKeysIfNeeded(card, session, callback) } - } else { - deriveKeysIfNeeded(card, session, callback) } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt index fa2b2a0832..0d1788de88 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt @@ -29,26 +29,26 @@ import timber.log.Timber [REDACTED_AUTHOR] */ object OnboardingHelper { - fun isOnboardingCase(response: ScanResponse): Boolean { - val cardInfoStorage = preferencesStorage.usedCardsPrefStorage + suspend fun isOnboardingCase(response: ScanResponse): Boolean { + val onboardingManager = store.state.globalState.onboardingState.onboardingManager val cardId = response.card.cardId return when { response.cardTypesResolver.isTangemTwins() -> { if (!response.twinsIsTwinned()) { true } else { - cardInfoStorage.isActivationInProgress(cardId) + onboardingManager?.isActivationInProgress(cardId) ?: false } } response.cardTypesResolver.isWallet2() || response.cardTypesResolver.isShibaWallet() -> { val emptyWallets = response.card.wallets.isEmpty() - val activationInProgress = cardInfoStorage.isActivationInProgress(cardId) + val activationInProgress = onboardingManager?.isActivationInProgress(cardId) val backupNotActive = response.card.backupStatus?.isActive != true - emptyWallets || activationInProgress || backupNotActive + emptyWallets || activationInProgress == true || backupNotActive } - response.card.wallets.isNotEmpty() -> cardInfoStorage.isActivationInProgress(cardId) + response.card.wallets.isNotEmpty() -> onboardingManager?.isActivationInProgress(cardId) ?: false else -> true } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingManager.kt b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingManager.kt index 467d4d73a5..d37b7153d1 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingManager.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingManager.kt @@ -5,11 +5,12 @@ import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.WalletManager import com.tangem.common.extensions.isZero import com.tangem.common.services.Result -import com.tangem.data.source.preferences.storage.UsedCardsPrefStorage +import com.tangem.domain.card.repository.CardRepository import com.tangem.domain.models.scan.ScanResponse import com.tangem.operations.attestation.CardVerifyAndGetInfo import com.tangem.operations.attestation.OnlineCardVerifier import com.tangem.tap.common.entities.ProgressState +import com.tangem.tap.common.extensions.inject import com.tangem.tap.common.extensions.isPositive import com.tangem.tap.common.extensions.safeUpdate import com.tangem.tap.domain.TapError @@ -17,17 +18,17 @@ import com.tangem.tap.domain.extensions.getOrLoadCardArtworkUrl import com.tangem.tap.domain.model.Currency import com.tangem.tap.domain.model.hasPendingTransactions import com.tangem.tap.features.demo.isDemoCard +import com.tangem.tap.proxy.redux.DaggerGraphState +import com.tangem.tap.store import timber.log.Timber import java.math.BigDecimal /** [REDACTED_AUTHOR] */ -class OnboardingManager( - var scanResponse: ScanResponse, - private val usedCardsPrefStorage: UsedCardsPrefStorage, -) { +class OnboardingManager(var scanResponse: ScanResponse) { + private val cardRepository: CardRepository = store.inject(DaggerGraphState::cardRepository) private var cardInfo: Result? = null suspend fun loadArtworkUrl(): String { @@ -76,17 +77,23 @@ class OnboardingManager( ) } - fun activationStarted(cardId: String) { - usedCardsPrefStorage.activationStarted(cardId) + suspend fun startActivation(cardId: String) { + cardRepository.startCardActivation(cardId) } - fun activationFinished(cardId: String) { - usedCardsPrefStorage.activationFinished(cardId) + suspend fun finishActivation(cardId: String) { + cardRepository.finishCardActivation(cardId) } - fun isActivationStarted(cardId: String): Boolean { - return usedCardsPrefStorage.isActivationStarted(cardId) + suspend fun finishActivation(cardIds: List) { + cardRepository.finishCardsActivation(cardIds) } + + suspend fun isActivationStarted(cardId: String): Boolean = cardRepository.isActivationStarted(cardId) + + suspend fun isActivationFinished(cardId: String): Boolean = cardRepository.isActivationFinished(cardId) + + suspend fun isActivationInProgress(cardId: String): Boolean = cardRepository.isActivationInProgress(cardId) } data class OnboardingWalletBalance( diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt index cf01d56658..114dedc7df 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt @@ -20,6 +20,7 @@ import com.tangem.tap.features.demo.DemoHelper import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE import com.tangem.tap.features.onboarding.OnboardingDialog import com.tangem.tap.features.onboarding.OnboardingHelper +import com.tangem.tap.mainScope import com.tangem.tap.scope import com.tangem.tap.store import com.tangem.tap.tangemSdkManager @@ -57,8 +58,10 @@ private fun handleNoteAction(appState: () -> AppState?, action: Action, dispatch when (action) { is OnboardingNoteAction.Init -> { - if (!onboardingManager.isActivationStarted(card.cardId)) { - Analytics.send(Onboarding.Started()) + scope.launch { + if (!onboardingManager.isActivationStarted(card.cardId)) { + Analytics.send(Onboarding.Started()) + } } } is OnboardingNoteAction.LoadCardArtwork -> { @@ -86,8 +89,10 @@ private fun handleNoteAction(appState: () -> AppState?, action: Action, dispatch } OnboardingNoteStep.Done -> { Analytics.send(Onboarding.Finished()) - onboardingManager.activationFinished(card.cardId) - postUi(DELAY_SDK_DIALOG_CLOSE) { store.dispatch(OnboardingNoteAction.Confetti.Show) } + mainScope.launch { + onboardingManager.finishActivation(card.cardId) + postUi(DELAY_SDK_DIALOG_CLOSE) { store.dispatch(OnboardingNoteAction.Confetti.Show) } + } } else -> Unit } @@ -101,7 +106,7 @@ private fun handleNoteAction(appState: () -> AppState?, action: Action, dispatch Analytics.send(Onboarding.CreateWallet.WalletCreatedSuccessfully()) val updatedResponse = scanResponse.copy(card = result.data.card) onboardingManager.scanResponse = updatedResponse - onboardingManager.activationStarted(updatedResponse.card.cardId) + onboardingManager.startActivation(updatedResponse.card.cardId) store.dispatch(OnboardingNoteAction.SetStepOfScreen(OnboardingNoteStep.TopUpWallet)) } is CompletionResult.Failure -> Unit diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt index 243319a1c0..d5e8144485 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt @@ -11,6 +11,7 @@ import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.features.onboarding.OnboardingDialog import com.tangem.tap.features.onboarding.OnboardingHelper +import com.tangem.tap.mainScope import com.tangem.tap.scope import com.tangem.tap.store import com.tangem.tap.tangemSdkManager @@ -43,8 +44,10 @@ private fun handleOtherCardsAction(action: Action) { when (action) { is OnboardingOtherCardsAction.Init -> { - if (!onboardingManager.isActivationStarted(card.cardId)) { - Analytics.send(Onboarding.Started()) + scope.launch { + if (!onboardingManager.isActivationStarted(card.cardId)) { + Analytics.send(Onboarding.Started()) + } } } is OnboardingOtherCardsAction.LoadCardArtwork -> { @@ -67,8 +70,10 @@ private fun handleOtherCardsAction(action: Action) { } OnboardingOtherCardsStep.Done -> { Analytics.send(Onboarding.Finished()) - onboardingManager.activationFinished(card.cardId) - postUi(200) { store.dispatch(OnboardingOtherCardsAction.Confetti.Show) } + mainScope.launch { + onboardingManager.finishActivation(card.cardId) + postUi(200) { store.dispatch(OnboardingOtherCardsAction.Confetti.Show) } + } } else -> Unit } @@ -88,7 +93,7 @@ private fun handleOtherCardsAction(action: Action) { ) val updatedCard = updatedResponse.card onboardingManager.scanResponse = updatedResponse - onboardingManager.activationStarted(updatedCard.cardId) + onboardingManager.startActivation(updatedCard.cardId) delay(DELAY_SDK_DIALOG_CLOSE) store.dispatch(OnboardingOtherCardsAction.SetStepOfScreen(OnboardingOtherCardsStep.Done)) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt index 4a7906dfdb..90f14188f3 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt @@ -75,51 +75,61 @@ private fun handle(action: Action, dispatch: DispatchFunction) { fun startCardActivation(cardId: String) { if (twinCardsState.mode == CreateTwinWalletMode.CreateWallet) { - onboardingManager?.activationStarted(cardId) + scope.launch { + onboardingManager?.startActivation(cardId) + } } } fun finishCardActivation() { if (twinCardsState.mode == CreateTwinWalletMode.CreateWallet) { Analytics.send(Onboarding.Finished()) - onboardingManager?.activationFinished(getScanResponse().card.cardId) - twinCardsState.pairCardId?.let { onboardingManager?.activationFinished(it) } + + val cardIds = listOfNotNull(getScanResponse().card.cardId, twinCardsState.pairCardId) + + if (cardIds.isNotEmpty()) { + mainScope.launch { + onboardingManager?.finishActivation(cardIds) + } + } } } when (action) { is TwinCardsAction.Init -> { - if (twinCardsState.currentStep is TwinCardsStep.WelcomeOnly) return + mainScope.launch { + if (twinCardsState.currentStep is TwinCardsStep.WelcomeOnly) return@launch - val scanResponse = getScanResponse() - onboardingManager?.apply { - if (!isActivationStarted(scanResponse.card.cardId)) { - Analytics.send(Onboarding.Started()) - } - } - - when (twinCardsState.mode) { - CreateTwinWalletMode.CreateWallet -> { - mainScope.launch { - val wasTwinsOnboardingShown = store.inject(DaggerGraphState::wasTwinsOnboardingShownUseCase) - .invokeSync() - - val dispatchAction = if (wasTwinsOnboardingShown) { - val step = when { - !scanResponse.twinsIsTwinned() -> TwinCardsStep.CreateFirstWallet - twinCardsState.walletBalance.balanceIsToppedUp() -> TwinCardsStep.Done - else -> TwinCardsStep.TopUpWallet - } - TwinCardsAction.SetStepOfScreen(step) - } else { - TwinCardsAction.SetStepOfScreen(TwinCardsStep.Welcome) - } - - store.dispatch(dispatchAction) + val scanResponse = getScanResponse() + onboardingManager?.apply { + if (!isActivationStarted(scanResponse.card.cardId)) { + Analytics.send(Onboarding.Started()) } } - CreateTwinWalletMode.RecreateWallet -> { - store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.Warning)) + + when (twinCardsState.mode) { + CreateTwinWalletMode.CreateWallet -> { + mainScope.launch { + val wasTwinsOnboardingShown = store.inject(DaggerGraphState::wasTwinsOnboardingShownUseCase) + .invokeSync() + + val dispatchAction = if (wasTwinsOnboardingShown) { + val step = when { + !scanResponse.twinsIsTwinned() -> TwinCardsStep.CreateFirstWallet + twinCardsState.walletBalance.balanceIsToppedUp() -> TwinCardsStep.Done + else -> TwinCardsStep.TopUpWallet + } + TwinCardsAction.SetStepOfScreen(step) + } else { + TwinCardsAction.SetStepOfScreen(TwinCardsStep.Welcome) + } + + store.dispatch(dispatchAction) + } + } + CreateTwinWalletMode.RecreateWallet -> { + store.dispatch(TwinCardsAction.SetStepOfScreen(TwinCardsStep.Warning)) + } } } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt index 2a4ca101c4..ca0b30020d 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt @@ -27,6 +27,7 @@ import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.Onboarding import com.tangem.tap.common.extensions.dispatchDialogShow import com.tangem.tap.common.extensions.dispatchOnMain +import com.tangem.tap.common.extensions.dispatchWithMain import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.domain.tasks.product.CreateProductWalletTaskResponse @@ -72,8 +73,10 @@ private fun handleWalletAction(action: Action) { when (action) { OnboardingWalletAction.Init -> { ifNotNull(onboardingManager, card) { manager, notNullCard -> - if (!manager.isActivationStarted(notNullCard.cardId)) { - Analytics.send(Onboarding.Started()) + mainScope.launch { + if (!manager.isActivationStarted(notNullCard.cardId)) { + Analytics.send(Onboarding.Started()) + } } } when { @@ -144,8 +147,10 @@ private fun handleWalletAction(action: Action) { onboardingManager.scanResponse = updatedResponse store.dispatch(GlobalAction.Onboarding.ShouldResetCardOnCreate(false)) - startCardActivation(updatedResponse) - store.dispatch(OnboardingWalletAction.ResumeBackup) + mainScope.launch { + onboardingManager.startActivation(updatedResponse.card.cardId) + store.dispatchWithMain(OnboardingWalletAction.ResumeBackup) + } } is CompletionResult.Failure -> { if (result.error is TangemSdkError.WalletAlreadyCreated) { @@ -493,11 +498,13 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction) backupService.discardSavedBackup() } is BackupAction.DiscardSavedBackup -> { - backupService.primaryCardId?.let { - Analytics.send(Onboarding.Finished()) - finishCardsActivationForDiscardedUnfinishedBackup(it) + mainScope.launch { + backupService.primaryCardId?.let { + Analytics.send(Onboarding.Finished()) + store.state.globalState.onboardingState.onboardingManager?.finishActivation(it) + } + backupService.discardSavedBackup() } - backupService.discardSavedBackup() } is BackupAction.CheckForUnfinishedBackup -> { if (backupService.hasIncompletedBackup) { @@ -514,17 +521,21 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction) } is BackupAction.SkipBackup -> { Analytics.send(Onboarding.Backup.Skipped()) - Analytics.send(Onboarding.Finished()) - finishCardActivation(gatherCardIds(backupState, card)) + + scope.launch { + store.state.globalState.onboardingState.onboardingManager?.finishActivation( + cardIds = gatherCardIds(backupState, card), + ) + } } is BackupAction.FinishBackup -> { - if (action.withAnalytics) { - Analytics.send(Onboarding.Backup.Finished(backupState.backupCardsNumber)) - } + scope.launch { + if (action.withAnalytics) { + Analytics.send(Onboarding.Backup.Finished(backupState.backupCardsNumber)) + } - if (scanResponse != null) { - scope.launch { + if (scanResponse != null) { val userWallet = UserWalletBuilder(scanResponse) .backupCardsIds(backupState.backupCardIds.toSet()) .build() @@ -546,16 +557,22 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction) ) store.dispatchOnMain(GlobalAction.UpdateUserWalletsListManager(userWalletsListManager)) } + + val notActivatedCardIds = gatherCardIds(backupState, card).mapNotNull { + if (store.state.globalState.onboardingState.onboardingManager?.isActivationFinished(it) == true) { + null + } else { + it + } + } + + // All cardIds may already be activated if the backup was skipped before. + if (notActivatedCardIds.isEmpty()) return@launch + + Analytics.send(Onboarding.Finished()) + + store.state.globalState.onboardingState.onboardingManager?.finishActivation(notActivatedCardIds) } - - val notActivatedCardIds = gatherCardIds(backupState, card) - .mapNotNull { if (cardActivationIsFinished(it)) null else it } - - // All cardIds may already be activated if the backup was skipped before. - if (notActivatedCardIds.isEmpty()) return - - Analytics.send(Onboarding.Finished()) - finishCardActivation(notActivatedCardIds) } is BackupAction.ResetBackupCard -> { @@ -566,36 +583,12 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction) } } -private fun cardActivationIsFinished(cardId: String): Boolean { - return preferencesStorage.usedCardsPrefStorage.isActivationFinished(cardId) -} - -/** - * Standard Wallet cards start activation at OnboardingWalletAction.CreateWallet - */ -private fun startCardActivation(scanResponse: ScanResponse) { - preferencesStorage.usedCardsPrefStorage.activationStarted(scanResponse.card.cardId) -} - -/** - * Standard Wallet cards finish activation at BackupAction.SkipBackup and BackupAction.FinishBackup - */ -internal fun finishCardActivation(cardIds: List) { - cardIds.forEach { cardId -> - preferencesStorage.usedCardsPrefStorage.activationFinished(cardId) - } -} - internal fun gatherCardIds(backupState: BackupState, card: CardDTO?): List { return (listOf(backupState.primaryCardId, card?.cardId) + backupState.backupCardIds) .filterNotNull() .distinct() } -private fun finishCardsActivationForDiscardedUnfinishedBackup(cardId: String) { - preferencesStorage.usedCardsPrefStorage.activationFinished(cardId) -} - private fun handleOnBackPressed(state: OnboardingWalletState) { when (state.backupState.backupStep) { BackupStep.InitBackup, BackupStep.ScanOriginCard, BackupStep.AddBackupCards, BackupStep.EnterAccessCode, diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt index e552d90fd6..eef3877a77 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt @@ -8,6 +8,7 @@ import com.tangem.domain.apptheme.repository.AppThemeModeRepository import com.tangem.domain.balancehiding.repositories.BalanceHidingRepository import com.tangem.domain.card.ScanCardProcessor import com.tangem.domain.card.ScanCardUseCase +import com.tangem.domain.card.repository.CardRepository import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.domain.onboarding.SaveTwinsOnboardingShownUseCase import com.tangem.domain.onboarding.WasTwinsOnboardingShownUseCase @@ -63,6 +64,7 @@ data class DaggerGraphState( val generalUserWalletsListManager: UserWalletsListManager? = null, val wasTwinsOnboardingShownUseCase: WasTwinsOnboardingShownUseCase? = null, val saveTwinsOnboardingShownUseCase: SaveTwinsOnboardingShownUseCase? = null, + val cardRepository: CardRepository? = null, ) : StateType { inline fun get(getDependency: DaggerGraphState.() -> T?): T { diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/AppPreferencesStoreModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/AppPreferencesStoreModule.kt index e36b36096b..bc295ea874 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/di/AppPreferencesStoreModule.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/di/AppPreferencesStoreModule.kt @@ -21,7 +21,7 @@ internal object AppPreferencesStoreModule { fun provideAppPreferencesStore( @ApplicationContext appContext: Context, dispatchers: CoroutineDispatcherProvider, - @NetworkMoshi moshi: Moshi, + @SdkMoshi moshi: Moshi, ): AppPreferencesStore { return AppPreferencesStore( preferencesDataStore = PreferencesDataStore.getInstance(context = appContext, dispatcher = dispatchers.io), diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/AppPreferencesStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/AppPreferencesStore.kt index 0c38cd669b..810c38230d 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/preferences/AppPreferencesStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/preferences/AppPreferencesStore.kt @@ -45,7 +45,7 @@ class AppPreferencesStore( * @see getObjectList * */ inline fun MutablePreferences.getObject(key: Preferences.Key): T? { - val adapter = moshi.adapter(T::class.java) // TODO: Support parameterized types + val adapter = moshi.adapter(T::class.java) return this[key]?.let(adapter::fromJson) } @@ -55,6 +55,15 @@ class AppPreferencesStore( return this[key]?.let(adapter::fromJson) } + /** Get list of data [T] by string [key] or default */ + inline fun MutablePreferences.getObjectListOrDefault( + key: Preferences.Key, + default: List, + ): List { + val adapter = moshi.adapter>(Types.newParameterizedType(List::class.java, T::class.java)) + return this[key]?.let(adapter::fromJson) ?: default + } + /** Get map with [String] key and value [V] by string [key] from [MutablePreferences] */ inline fun MutablePreferences.getObjectMap(key: Preferences.Key): Map? { val type = Types.newParameterizedType(Map::class.java, String::class.java, V::class.java) diff --git a/data/card/src/main/java/com/tangem/data/card/DefaultCardRepository.kt b/data/card/src/main/java/com/tangem/data/card/DefaultCardRepository.kt index 84c73a8dc7..e8328a84ea 100644 --- a/data/card/src/main/java/com/tangem/data/card/DefaultCardRepository.kt +++ b/data/card/src/main/java/com/tangem/data/card/DefaultCardRepository.kt @@ -1,9 +1,11 @@ package com.tangem.data.card +import androidx.datastore.preferences.core.MutablePreferences import com.tangem.datasource.local.card.UsedCardInfo import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.datasource.local.preferences.PreferencesKeys import com.tangem.datasource.local.preferences.utils.getObjectList +import com.tangem.datasource.local.preferences.utils.getObjectListSync import com.tangem.domain.card.repository.CardRepository import com.tangem.utils.extensions.addOrReplace import kotlinx.coroutines.flow.Flow @@ -21,23 +23,73 @@ internal class DefaultCardRepository( } override suspend fun setCardWasScanned(cardId: String) { - appPreferencesStore.editData { mutablePreferences -> - val usedCards: List? = mutablePreferences.getObjectList( - key = PreferencesKeys.USED_CARDS_INFO_KEY, - ) + appPreferencesStore.editUsedCards(cardId) { it.copy(isScanned = true) } + } - val updatedUsedCards = usedCards?.updateCard(cardId) - ?: listOf(UsedCardInfo(cardId = cardId, isScanned = true)) + override suspend fun startCardActivation(cardId: String) { + appPreferencesStore.editUsedCards(cardId) { it.copy(isActivationStarted = true) } + } - mutablePreferences.setObjectList( - key = PreferencesKeys.USED_CARDS_INFO_KEY, - value = updatedUsedCards, - ) + override suspend fun finishCardActivation(cardId: String) { + appPreferencesStore.editUsedCards(cardId) { + it.copy(isActivationStarted = true, isActivationFinished = true) } } - private fun List.updateCard(cardId: String): List { - val card = find { it.cardId == cardId } ?: UsedCardInfo(cardId = cardId, isScanned = true) - return addOrReplace(item = card.copy(isScanned = true), predicate = { it.cardId == cardId }) + override suspend fun finishCardsActivation(cardIds: List) { + appPreferencesStore.editData { mutablePreferences -> + val usedCards = mutablePreferences.getUsedCards() + + val updatedUsedCards = cardIds.map { cardId -> + usedCards.updateCard(cardId) { + it.copy(isActivationStarted = true, isActivationFinished = true) + } + } + + mutablePreferences.setObjectList(key = PreferencesKeys.USED_CARDS_INFO_KEY, value = updatedUsedCards) + } + } + + override suspend fun isActivationStarted(cardId: String): Boolean { + return getUsedCardSync(cardId)?.isActivationStarted ?: false + } + + override suspend fun isActivationFinished(cardId: String): Boolean { + return getUsedCardSync(cardId)?.isActivationFinished ?: false + } + + override suspend fun isActivationInProgress(cardId: String): Boolean { + val card = getUsedCardSync(cardId) ?: return false + + return card.isActivationStarted && !card.isActivationFinished + } + + private suspend fun AppPreferencesStore.editUsedCards(cardId: String, update: (UsedCardInfo) -> UsedCardInfo) { + editData { mutablePreferences -> + val usedCards = mutablePreferences.getUsedCards() + + val updatedUsedCards = usedCards.updateCard(cardId = cardId, update = update) + + mutablePreferences.setObjectList(key = PreferencesKeys.USED_CARDS_INFO_KEY, value = updatedUsedCards) + } + } + + private fun MutablePreferences.getUsedCards(): List { + return with(appPreferencesStore) { + getObjectListOrDefault(key = PreferencesKeys.USED_CARDS_INFO_KEY, default = mutableListOf()) + } + } + + private fun List.updateCard( + cardId: String, + update: (UsedCardInfo) -> UsedCardInfo, + ): List { + val card = find { it.cardId == cardId } ?: UsedCardInfo(cardId = cardId) + return addOrReplace(item = update(card), predicate = { it.cardId == cardId }) + } + + private suspend fun getUsedCardSync(cardId: String): UsedCardInfo? { + return appPreferencesStore.getObjectListSync(PreferencesKeys.USED_CARDS_INFO_KEY) + .firstOrNull { it.cardId == cardId } } } \ No newline at end of file diff --git a/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/PreferencesDataSource.kt b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/PreferencesDataSource.kt index 91f774282a..4bcd434fe8 100644 --- a/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/PreferencesDataSource.kt +++ b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/PreferencesDataSource.kt @@ -3,10 +3,7 @@ package com.tangem.data.source.preferences import android.content.Context import android.content.SharedPreferences import androidx.core.content.edit -import com.tangem.common.json.MoshiJsonConverter -import com.tangem.data.source.preferences.adapters.BigDecimalAdapter import com.tangem.data.source.preferences.storage.DisclaimerPrefStorage -import com.tangem.data.source.preferences.storage.UsedCardsPrefStorage import javax.inject.Inject // 🔥FIXME: Only logic to work with preferences must be here, must be separated to repositories @@ -14,21 +11,13 @@ import javax.inject.Inject @Deprecated("Create repository instead") class PreferencesDataSource @Inject internal constructor(applicationContext: Context) { - val usedCardsPrefStorage: UsedCardsPrefStorage val disclaimerPrefStorage: DisclaimerPrefStorage private val preferences: SharedPreferences = applicationContext.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE) - private val moshiConverter = MoshiJsonConverter( - adapters = listOf(BigDecimalAdapter()) + MoshiJsonConverter.getTangemSdkAdapters(), - typedAdapters = MoshiJsonConverter.getTangemSdkTypedAdapters(), - ) - init { incrementLaunchCounter() - usedCardsPrefStorage = UsedCardsPrefStorage(preferences, moshiConverter) - usedCardsPrefStorage.migrate() disclaimerPrefStorage = DisclaimerPrefStorage(preferences) } diff --git a/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/model/DataSourceUsedCardInfo.kt b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/model/DataSourceUsedCardInfo.kt deleted file mode 100644 index d05eadc7ca..0000000000 --- a/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/model/DataSourceUsedCardInfo.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.tangem.data.source.preferences.model - -internal data class DataSourceUsedCardInfo( - val cardId: String, - val isScanned: Boolean = false, - val isActivationStarted: Boolean = false, - val isActivationFinished: Boolean = false, -) \ No newline at end of file diff --git a/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/model/DataSourceUsedCardInfoOld.kt b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/model/DataSourceUsedCardInfoOld.kt deleted file mode 100644 index ee01e6752a..0000000000 --- a/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/model/DataSourceUsedCardInfoOld.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.tangem.data.source.preferences.model - -internal data class DataSourceUsedCardInfoOld( - val cardId: String, - val isScanned: Boolean = false, - val isActivationStarted: Boolean = false, -) \ No newline at end of file diff --git a/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/storage/UsedCardsPrefStorage.kt b/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/storage/UsedCardsPrefStorage.kt deleted file mode 100644 index de987e84fc..0000000000 --- a/data/source/preferences/src/main/kotlin/com/tangem/data/source/preferences/storage/UsedCardsPrefStorage.kt +++ /dev/null @@ -1,150 +0,0 @@ -package com.tangem.data.source.preferences.storage - -import android.content.SharedPreferences -import androidx.core.content.edit -import com.tangem.common.json.MoshiJsonConverter -import com.tangem.data.source.preferences.model.DataSourceUsedCardInfo -import com.tangem.data.source.preferences.model.DataSourceUsedCardInfoOld - -/** -[REDACTED_AUTHOR] - */ -@Deprecated("Create repository instead") -class UsedCardsPrefStorage internal constructor( - private val preferences: SharedPreferences, - private val jsonConverter: MoshiJsonConverter, -) { - - private val migrationList = mutableListOf( - UserCardInfoToV2(this), - ) - - internal fun migrate() { - migrationList.forEach { it.migrate() } - migrationList.clear() - } - - fun scanned(cardId: String) { - val restoredList = restore() - val foundItem = findCardInfo(cardId, restoredList)?.copy(isScanned = true) - ?: DataSourceUsedCardInfo(cardId, true) - - save(foundItem, restoredList) - } - - fun wasScanned(cardId: String): Boolean { - return findCardInfo(cardId)?.isScanned ?: false - } - - fun activationStarted(cardId: String) { - val restoredList = restore() - val foundItem = findCardInfo(cardId, restoredList)?.copy(isActivationStarted = true) - ?: DataSourceUsedCardInfo(cardId, isActivationStarted = true) - - save(foundItem, restoredList) - } - - fun activationFinished(cardId: String) { - val restoredList = restore() - var foundItem = findCardInfo(cardId, restoredList) ?: DataSourceUsedCardInfo(cardId) - foundItem = foundItem.copy( - isActivationStarted = true, - isActivationFinished = true, - ) - - save(foundItem, restoredList) - } - - fun isActivationStarted(cardId: String): Boolean { - return findCardInfo(cardId)?.isActivationStarted ?: false - } - - fun isActivationFinished(cardId: String): Boolean { - return findCardInfo(cardId)?.isActivationFinished ?: false - } - - fun isActivationInProgress(cardId: String): Boolean { - val cardInfo = findCardInfo(cardId) ?: return false - return cardInfo.isActivationStarted && !cardInfo.isActivationFinished - } - - fun hadFinishedActivation(): Boolean { - return restore().any { it.isActivationFinished } - } - - private fun findCardInfo( - cardId: String, - list: MutableList? = null, - ): DataSourceUsedCardInfo? { - val findInList = list ?: restore() - return findInList.firstOrNull { it.cardId == cardId } - } - - private fun save(usedCardInfo: DataSourceUsedCardInfo?, usedCardsInfo: MutableList) { - val info = usedCardInfo ?: return - - with(usedCardsInfo) { - val index = indexOfFirst { it.cardId == info.cardId } - if (index == -1) { - add(info) - } else { - set(index, info) - } - } - - save(usedCardsInfo) - } - - private fun save(list: MutableList) { - val json = jsonConverter.toJson(list) - preferences.edit { putString(USED_CARDS_INFO_V2, json) } - } - - private fun restore(): MutableList { - val json = preferences.getString(USED_CARDS_INFO_V2, null) ?: return mutableListOf() - return try { - jsonConverter.fromJson(json, jsonConverter.typedList(DataSourceUsedCardInfo::class.java))!! - } catch (ex: Exception) { - preferences.edit(true) { remove(USED_CARDS_INFO_V2) } - mutableListOf() - } - } - - companion object { - private const val USED_CARDS_INFO_V2 = "usedCardsInfo_v2" - private const val USED_CARDS_INFO = "usedCardsInfo" - } - - private class UserCardInfoToV2( - private val storage: UsedCardsPrefStorage, - ) : Migration { - override fun migrate() { - val restoredCardsInfo = restore() - if (restoredCardsInfo.isEmpty()) return - - val newCardsInfo = restoredCardsInfo.map { cardInfo -> - DataSourceUsedCardInfo( - cardId = cardInfo.cardId, - isScanned = cardInfo.isScanned, - isActivationStarted = true, - isActivationFinished = !cardInfo.isActivationStarted, - ) - }.toMutableList() - storage.save(newCardsInfo) - } - - private fun restore(): MutableList { - val json = storage.preferences.getString(USED_CARDS_INFO, null) ?: return mutableListOf() - return try { - storage.jsonConverter.fromJson( - json, - storage.jsonConverter.typedList(DataSourceUsedCardInfoOld::class.java), - )!! - } catch (ex: Exception) { - mutableListOf() - } finally { - storage.preferences.edit(true) { remove(USED_CARDS_INFO) } - } - } - } -} \ No newline at end of file diff --git a/domain/card/src/main/kotlin/com/tangem/domain/card/repository/CardRepository.kt b/domain/card/src/main/kotlin/com/tangem/domain/card/repository/CardRepository.kt index 27bb1657ab..97a087c85e 100644 --- a/domain/card/src/main/kotlin/com/tangem/domain/card/repository/CardRepository.kt +++ b/domain/card/src/main/kotlin/com/tangem/domain/card/repository/CardRepository.kt @@ -7,4 +7,19 @@ interface CardRepository { fun wasCardScanned(cardId: String): Flow suspend fun setCardWasScanned(cardId: String) + + suspend fun startCardActivation(cardId: String) + + suspend fun finishCardActivation(cardId: String) + + suspend fun finishCardsActivation(cardIds: List) + + @Throws + suspend fun isActivationStarted(cardId: String): Boolean + + @Throws + suspend fun isActivationFinished(cardId: String): Boolean + + @Throws + suspend fun isActivationInProgress(cardId: String): Boolean } \ No newline at end of file