From 713cad5fd79480b2e85331e972230194fca727d5 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 17 Sep 2025 15:16:22 +0500 Subject: [PATCH] Updated on 2026-08-14 --- .../main/java/com/tangem/tap/MainActivity.kt | 164 ++---------------- .../common/pushes/PushNotificationDelegate.kt | 4 +- .../com/tangem/tap/di/IntentHandlingModule.kt | 12 -- .../features/intentHandler/IntentHandler.kt | 2 +- .../features/intentHandler/IntentProcessor.kt | 27 --- .../handlers/OnPushClickedIntentHandler.kt | 24 --- .../tangem/tap/features/main/MainViewModel.kt | 17 +- .../products/wallet/redux/BackupMiddleware.kt | 2 +- .../welcome/component/WelcomeComponent.kt | 2 - .../features/welcome/model/WelcomeModel.kt | 4 +- .../features/welcome/redux/WelcomeAction.kt | 5 +- .../welcome/redux/WelcomeMiddleware.kt | 23 --- .../features/welcome/redux/WelcomeReducer.kt | 1 - .../features/welcome/redux/WelcomeState.kt | 2 - .../tap/routing/component/RoutingComponent.kt | 7 +- .../component/impl/DefaultRoutingComponent.kt | 63 ++++++- .../routing/configurator/AppRouterConfig.kt | 2 + .../configurator/MutableAppRouterConfig.kt | 3 +- .../tangem/tap/routing/utils/ChildFactory.kt | 1 - .../com/tangem/common/routing/AppRoute.kt | 4 - .../routing/entity/SerializableBundle.kt | 24 --- .../routing/entity/SerializableIntent.kt | 52 ------ .../accesscode/AccessCodeComponent.kt | 4 +- .../entry/AddExistingWalletModel.kt | 4 +- .../phrase/ManualBackupPhraseComponent.kt | 2 + .../entry/WalletActivationModel.kt | 4 +- 26 files changed, 100 insertions(+), 359 deletions(-) delete mode 100644 app/src/main/java/com/tangem/tap/features/intentHandler/IntentProcessor.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/intentHandler/handlers/OnPushClickedIntentHandler.kt delete mode 100644 common/routing/src/main/kotlin/com/tangem/common/routing/entity/SerializableBundle.kt delete mode 100644 common/routing/src/main/kotlin/com/tangem/common/routing/entity/SerializableIntent.kt diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index 287b4a7688..c047e66353 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -26,10 +26,8 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope import arrow.core.getOrElse -import com.tangem.common.routing.AppRoute import com.tangem.common.routing.deeplink.DeeplinkConst.WEBLINK_KEY import com.tangem.common.routing.deeplink.PayloadToDeeplinkConverter -import com.tangem.common.routing.entity.SerializableIntent import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.di.RootAppComponentContext @@ -41,7 +39,6 @@ import com.tangem.domain.card.ScanCardUseCase import com.tangem.domain.card.repository.CardRepository import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.domain.core.wallets.UserWalletsListRepository -import com.tangem.domain.models.wallet.isLocked import com.tangem.domain.settings.SetGooglePayAvailabilityUseCase import com.tangem.domain.settings.SetGoogleServicesAvailabilityUseCase import com.tangem.domain.settings.ShouldInitiallyAskPermissionUseCase @@ -62,11 +59,9 @@ import com.tangem.sdk.api.TangemSdkManager import com.tangem.tap.common.ActivityResultCallbackHolder import com.tangem.tap.common.DialogManager import com.tangem.tap.common.OnActivityResultCallback +import com.tangem.tap.common.analytics.events.Push import com.tangem.tap.common.apptheme.MutableAppThemeModeHolder -import com.tangem.tap.common.extensions.dispatchNavigationAction -import com.tangem.tap.features.intentHandler.IntentProcessor import com.tangem.tap.features.intentHandler.handlers.BackgroundScanIntentHandler -import com.tangem.tap.features.intentHandler.handlers.OnPushClickedIntentHandler import com.tangem.tap.features.main.MainViewModel import com.tangem.tap.proxy.redux.DaggerGraphAction import com.tangem.tap.routing.component.RoutingComponent @@ -172,12 +167,6 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder { @Inject internal lateinit var testerMenuLauncher: TesterMenuLauncher - @Inject - internal lateinit var intentProcessor: IntentProcessor - - @Inject - internal lateinit var onPushClickedIntentHandler: OnPushClickedIntentHandler - @Inject internal lateinit var backgroundScanIntentHandler: BackgroundScanIntentHandler @@ -248,6 +237,10 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder { if (BuildConfig.TESTER_MENU_ENABLED) { lifecycle.addObserver(testerMenuLauncher.launchOnKeyEventObserver) } + + if (intent != null) { + handleDeepLink(intent = intent, isFromOnNewIntent = false) + } } private fun setRootContent() { @@ -257,6 +250,7 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder { val routingComponent = routingComponentFactory.create( context = rootComponentContext, initialStack = appRouterConfig.stack, + launchMode = backgroundScanIntentHandler.getInitScreenLaunchMode(intent), ) setContent { @@ -281,8 +275,6 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder { clearAllHotWalletContextualUnlockUseCase = clearAllHotWalletContextualUnlockUseCase, ) - initIntentHandlers() - store.dispatch( DaggerGraphAction.SetActivityDependencies( scanCardUseCase = scanCardUseCase, @@ -339,18 +331,12 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder { dialogManager.onStart(this) } - override fun onResume() { - super.onResume() - navigateToInitialScreenIfNeeded(intent) - } - override fun onStop() { dialogManager.onStop() super.onStop() } override fun onDestroy() { - intentProcessor.removeAll() // workaround: kill process when activity destroy to avoid state when lock() wallets // and navigation to unlock screen was skipped because system kills activity but not process if (BuildConfig.BUILD_TYPE != MOCKED_BUILD_TYPE) { @@ -359,10 +345,6 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder { super.onDestroy() } - private fun initIntentHandlers() { - intentProcessor.addHandler(onPushClickedIntentHandler) - } - private fun updateAppTheme(appThemeMode: AppThemeMode) { val mode = when (appThemeMode) { AppThemeMode.FORCE_DARK -> AppCompatDelegate.MODE_NIGHT_YES @@ -388,8 +370,9 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder { override fun onNewIntent(intent: Intent?) { super.onNewIntent(intent) - lifecycleScope.launch { - intentProcessor.handleIntent(intent = intent, isFromForeground = true) + val fromPush = intent?.extras?.containsKey(OPENED_FROM_GCM_PUSH) ?: false + if (fromPush) { + analyticsEventsHandler.send(Push.PushNotificationOpened) } if (intent != null) { @@ -431,134 +414,6 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder { } } - private fun navigateToInitialScreenIfNeeded(intentWhichStartedActivity: Intent?) { - // TODO refactor this method to return a route instead of navigating directly - if (hotWalletFeatureToggles.isHotWalletEnabled) { - navigateToInitialScreenIfNeededNew(intentWhichStartedActivity) - return - } - - val backStack = appRouterConfig.stack ?: emptyList() - // TODO move inital navigation to navigation component ([REDACTED_JIRA]) - val isOnlyInitialRoute = backStack.all { it is AppRoute.Initial } - val isOnInitialScreen = backStack.all { it is AppRoute.Welcome || it is AppRoute.Home } - val isNotScannedBefore = store.state.globalState.scanResponse == null - val isOnboardingServiceNotActive = !store.state.globalState.onboardingState.onboardingStarted - - when { - !isOnInitialScreen && isNotScannedBefore && isOnboardingServiceNotActive -> { - navigateToInitialScreen(intentWhichStartedActivity) - } - backStack.isEmpty() -> { - navigateToInitialScreen(intentWhichStartedActivity) - } - isOnlyInitialRoute -> navigateToInitialScreen(intentWhichStartedActivity) - else -> Unit - } - } - - @Deprecated("Refactor this method to return a route instead of navigating directly") - private fun navigateToInitialScreenIfNeededNew(intentWhichStartedActivity: Intent?) { - lifecycleScope.launch { - val userWallets = userWalletsListRepository.userWalletsSync() - val launchMode = backgroundScanIntentHandler.getInitScreenLaunchMode(intentWhichStartedActivity) - if (userWallets.isEmpty()) { - val shouldShowTos = !cardRepository.isTangemTOSAccepted() - - val route = if (shouldShowTos) { - AppRoute.Disclaimer(isTosAccepted = false) - } else { - AppRoute.Home(launchMode = launchMode) - } - - store.dispatchNavigationAction { replaceAll(route) } - intentProcessor.handleIntent( - intent = intentWhichStartedActivity, - isFromForeground = false, - skipNavigationHandlers = false, - ) - } else { - if (userWallets.any { it.isLocked }) { - store.dispatchNavigationAction { - replaceAll( - AppRoute.Welcome( - launchMode = launchMode, - intent = intentWhichStartedActivity?.let(::SerializableIntent), - ), - ) - } - } else { - store.dispatchNavigationAction { - replaceAll(AppRoute.Wallet) - } - } - - intentProcessor.handleIntent( - intent = intentWhichStartedActivity, - isFromForeground = false, - skipNavigationHandlers = true, - ) - } - - if (intent != null) { - handleDeepLink(intent = intent, isFromOnNewIntent = false) - } - - viewModel.checkForUnfinishedBackup() - } - } - - private fun navigateToInitialScreen(intentWhichStartedActivity: Intent?) { - val launchMode = backgroundScanIntentHandler.getInitScreenLaunchMode(intentWhichStartedActivity) - - // Workaround to navigate to TangemPayDetails screen. Will be deleted in next PRs - if (tangemPayFeatureToggles.isTangemPayEnabled) { - lifecycleScope.launch { - val selectedUserWalledId = userWalletsListRepository.selectedUserWalletSync()?.walletId - store.dispatchNavigationAction { - replaceAll(AppRoute.TangemPayDetails(requireNotNull(selectedUserWalledId))) - } - } - } else if (userWalletsListManager.isLockable && userWalletsListManager.hasUserWallets) { - store.dispatchNavigationAction { - replaceAll( - AppRoute.Welcome( - launchMode = launchMode, - intent = intentWhichStartedActivity?.let(::SerializableIntent), - ), - ) - } - intentProcessor.handleIntent( - intent = intentWhichStartedActivity, - isFromForeground = false, - skipNavigationHandlers = true, - ) - } else { - lifecycleScope.launch { - val shouldShowTos = !cardRepository.isTangemTOSAccepted() - - val route = if (shouldShowTos) { - AppRoute.Disclaimer(isTosAccepted = false) - } else { - AppRoute.Home(launchMode = launchMode) - } - - store.dispatchNavigationAction { replaceAll(route) } - intentProcessor.handleIntent( - intent = intentWhichStartedActivity, - isFromForeground = false, - skipNavigationHandlers = false, - ) - } - } - - if (intent != null) { - handleDeepLink(intent = intent, isFromOnNewIntent = false) - } - - viewModel.checkForUnfinishedBackup() - } - private fun handleDeepLink(intent: Intent, isFromOnNewIntent: Boolean) { val deepLinkExtras = PayloadToDeeplinkConverter.convertBundle(intent.extras)?.toUri() val webLink = intent.getStringExtra(WEBLINK_KEY) @@ -627,5 +482,6 @@ class MainActivity : AppCompatActivity(), ActivityResultCallbackHolder { companion object { private const val APP_THEME_LOAD_TIMEOUT = 2 private const val MOCKED_BUILD_TYPE = "mocked" + private const val OPENED_FROM_GCM_PUSH = "google.sent_time" // every bundle from FCM contains this key } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/pushes/PushNotificationDelegate.kt b/app/src/main/java/com/tangem/tap/common/pushes/PushNotificationDelegate.kt index 844eec86ff..a04f41db40 100644 --- a/app/src/main/java/com/tangem/tap/common/pushes/PushNotificationDelegate.kt +++ b/app/src/main/java/com/tangem/tap/common/pushes/PushNotificationDelegate.kt @@ -16,7 +16,6 @@ import coil.request.ImageRequest import com.tangem.domain.common.LogConfig import com.tangem.tap.MainActivity import com.tangem.tap.common.images.createCoilImageLoader -import com.tangem.tap.features.intentHandler.handlers.OnPushClickedIntentHandler import com.tangem.wallet.R class PushNotificationDelegate(private val context: Context) { @@ -35,7 +34,7 @@ class PushNotificationDelegate(private val context: Context) { dataMap.forEach { (key, value) -> putExtra(key, value) } - putExtra(OnPushClickedIntentHandler.OPENED_FROM_GCM_PUSH, true) + putExtra(OPENED_FROM_GCM_PUSH, true) addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) } @@ -98,5 +97,6 @@ class PushNotificationDelegate(private val context: Context) { private companion object { const val PUSH_NOTIFICATION_REQUEST_CODE = 123 + private const val OPENED_FROM_GCM_PUSH = "google.sent_time" // every bundle from FCM contains this key } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/di/IntentHandlingModule.kt b/app/src/main/java/com/tangem/tap/di/IntentHandlingModule.kt index b84f990fa6..64f87e9dcb 100644 --- a/app/src/main/java/com/tangem/tap/di/IntentHandlingModule.kt +++ b/app/src/main/java/com/tangem/tap/di/IntentHandlingModule.kt @@ -1,9 +1,6 @@ package com.tangem.tap.di -import com.tangem.core.analytics.api.AnalyticsEventHandler -import com.tangem.tap.features.intentHandler.IntentProcessor import com.tangem.tap.features.intentHandler.handlers.BackgroundScanIntentHandler -import com.tangem.tap.features.intentHandler.handlers.OnPushClickedIntentHandler import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -17,13 +14,4 @@ internal object IntentHandlingModule { @Provides @Singleton fun provideBackgroundScanIntentHandler(): BackgroundScanIntentHandler = BackgroundScanIntentHandler() - - @Provides - @Singleton - fun provideOnPushClickedIntentHandler(analyticsEventHandler: AnalyticsEventHandler): OnPushClickedIntentHandler = - OnPushClickedIntentHandler(analyticsEventHandler) - - @Provides - @Singleton - fun provideIntentProcessor(): IntentProcessor = IntentProcessor() } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/intentHandler/IntentHandler.kt b/app/src/main/java/com/tangem/tap/features/intentHandler/IntentHandler.kt index c420052753..7458f759ba 100644 --- a/app/src/main/java/com/tangem/tap/features/intentHandler/IntentHandler.kt +++ b/app/src/main/java/com/tangem/tap/features/intentHandler/IntentHandler.kt @@ -7,5 +7,5 @@ import android.content.Intent */ interface IntentHandler { - fun handleIntent(intent: Intent?, isFromForeground: Boolean): Boolean + fun handleIntent(intent: Intent?): Boolean } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/intentHandler/IntentProcessor.kt b/app/src/main/java/com/tangem/tap/features/intentHandler/IntentProcessor.kt deleted file mode 100644 index 6883c38214..0000000000 --- a/app/src/main/java/com/tangem/tap/features/intentHandler/IntentProcessor.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.tangem.tap.features.intentHandler - -import android.content.Intent -import java.util.concurrent.CopyOnWriteArrayList - -/** -[REDACTED_AUTHOR] - */ -// TODO: fixme: close it with the combined interfaces IntentHandler and IntentHandlerHolder -class IntentProcessor { - - private val intentHandlers = CopyOnWriteArrayList() - - fun addHandler(handler: IntentHandler) { - intentHandlers.add(handler) - } - - fun removeAll() { - intentHandlers.clear() - } - - fun handleIntent(intent: Intent?, isFromForeground: Boolean, skipNavigationHandlers: Boolean = false) { - intentHandlers - .filterNot { handler -> skipNavigationHandlers && handler is AffectsNavigation } - .forEach { handler -> handler.handleIntent(intent, isFromForeground) } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/OnPushClickedIntentHandler.kt b/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/OnPushClickedIntentHandler.kt deleted file mode 100644 index e6b36001d8..0000000000 --- a/app/src/main/java/com/tangem/tap/features/intentHandler/handlers/OnPushClickedIntentHandler.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.tangem.tap.features.intentHandler.handlers - -import android.content.Intent -import com.tangem.core.analytics.api.AnalyticsEventHandler -import com.tangem.tap.common.analytics.events.Push -import com.tangem.tap.features.intentHandler.IntentHandler - -internal class OnPushClickedIntentHandler(val analyticsEventHandler: AnalyticsEventHandler) : IntentHandler { - - override fun handleIntent(intent: Intent?, isFromForeground: Boolean): Boolean { - val fromPush = intent?.extras?.containsKey(OPENED_FROM_GCM_PUSH) ?: false - - return if (fromPush) { - analyticsEventHandler.send(Push.PushNotificationOpened) - true - } else { - false - } - } - - companion object { - const val OPENED_FROM_GCM_PUSH = "google.sent_time" // every bundle from FCM contains this key - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt b/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt index 5fba945d69..1734b45276 100644 --- a/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt @@ -28,7 +28,6 @@ import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.notifications.GetApplicationIdUseCase import com.tangem.domain.notifications.SendPushTokenUseCase import com.tangem.domain.notifications.models.ApplicationId -import com.tangem.domain.onboarding.repository.OnboardingRepository import com.tangem.domain.onramp.FetchHotCryptoUseCase import com.tangem.domain.promo.GetStoryContentUseCase import com.tangem.domain.promo.models.StoryContentIds @@ -43,12 +42,10 @@ import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase import com.tangem.domain.wallets.usecase.UpdateRemoteWalletsInfoUseCase import com.tangem.feature.swap.analytics.StoriesEvents import com.tangem.tap.common.extensions.setContext -import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.features.onboarding.products.wallet.redux.BackupDialog import com.tangem.tap.network.exchangeServices.ExchangeService import com.tangem.tap.network.exchangeServices.moonpay.MoonPayService import com.tangem.tap.proxy.AppStateHolder -import com.tangem.tap.store +import com.tangem.tap.routing.configurator.AppRouterConfig import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.wallet.BuildConfig import dagger.hilt.android.lifecycle.HiltViewModel @@ -78,7 +75,6 @@ internal class MainViewModel @Inject constructor( private val getStoryContentUseCase: GetStoryContentUseCase, private val imagePreloader: ImagePreloader, private val fetchHotCryptoUseCase: FetchHotCryptoUseCase, - private val onboardingRepository: OnboardingRepository, private val getApplicationIdUseCase: GetApplicationIdUseCase, private val subscribeOnWalletsUseCase: GetSavedWalletsCountUseCase, private val associateWalletsWithApplicationIdUseCase: AssociateWalletsWithApplicationIdUseCase, @@ -89,6 +85,7 @@ internal class MainViewModel @Inject constructor( private val appStateHolder: AppStateHolder, private val environmentConfigStorage: EnvironmentConfigStorage, private val getSelectedWalletUseCase: GetSelectedWalletUseCase, + private val appRouterConfig: AppRouterConfig, getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase, ) : ViewModel() { @@ -139,13 +136,6 @@ internal class MainViewModel @Inject constructor( multiQuoteUpdater.unsubscribe() } - fun checkForUnfinishedBackup() { - viewModelScope.launch(dispatchers.main) { - val onboardingScanResponse = onboardingRepository.getUnfinishedFinalizeOnboarding() ?: return@launch - store.dispatch(GlobalAction.ShowDialog(BackupDialog.UnfinishedBackupFound(onboardingScanResponse))) - } - } - /** Loading the resources needed to run the application */ private fun loadApplicationResources() { viewModelScope.launch { @@ -159,6 +149,9 @@ internal class MainViewModel @Inject constructor( prepareSelectedWalletFeedback() + // await while initial route stack is initialized + appRouterConfig.isInitialized.first { it } + isSplashScreenShown = false } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/BackupMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/BackupMiddleware.kt index 546cf5fd21..2433af1e95 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/BackupMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/BackupMiddleware.kt @@ -49,7 +49,7 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction) is BackupAction.ResumeFoundUnfinishedBackup -> { if (action.unfinishedBackupScanResponse != null) { store.dispatchNavigationAction { - push( + replaceAll( AppRoute.Onboarding( scanResponse = action.unfinishedBackupScanResponse, mode = AppRoute.Onboarding.Mode.ContinueFinalize, diff --git a/app/src/main/java/com/tangem/tap/features/welcome/component/WelcomeComponent.kt b/app/src/main/java/com/tangem/tap/features/welcome/component/WelcomeComponent.kt index 215560a1ad..120b0b5484 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/component/WelcomeComponent.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/component/WelcomeComponent.kt @@ -1,7 +1,6 @@ package com.tangem.tap.features.welcome.component import com.tangem.common.routing.entity.InitScreenLaunchMode -import com.tangem.common.routing.entity.SerializableIntent import com.tangem.core.decompose.factory.ComponentFactory import com.tangem.core.ui.decompose.ComposableContentComponent @@ -9,7 +8,6 @@ interface WelcomeComponent : ComposableContentComponent { data class Params( val launchMode: InitScreenLaunchMode, - val intent: SerializableIntent?, ) interface Factory : ComponentFactory diff --git a/app/src/main/java/com/tangem/tap/features/welcome/model/WelcomeModel.kt b/app/src/main/java/com/tangem/tap/features/welcome/model/WelcomeModel.kt index 5965f011f3..ae5afc8d33 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/model/WelcomeModel.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/model/WelcomeModel.kt @@ -47,7 +47,7 @@ internal class WelcomeModel @Inject constructor( val welcomeAction = when (params.launchMode) { is InitScreenLaunchMode.WithCardScan -> WelcomeAction.ProceedWithCard - is InitScreenLaunchMode.Standard -> WelcomeAction.ProceedWithBiometrics(params.intent?.toIntent()) + is InitScreenLaunchMode.Standard -> WelcomeAction.ProceedWithBiometrics } store.dispatch(welcomeAction) @@ -55,7 +55,7 @@ internal class WelcomeModel @Inject constructor( private fun unlockWallets() { Analytics.send(SignIn.ButtonBiometricSignIn()) - store.dispatch(WelcomeAction.ProceedWithBiometrics()) + store.dispatch(WelcomeAction.ProceedWithBiometrics) } private fun scanCard() { diff --git a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeAction.kt b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeAction.kt index bd88693c6b..78fb0451e0 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeAction.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeAction.kt @@ -1,12 +1,11 @@ package com.tangem.tap.features.welcome.redux -import android.content.Intent import com.tangem.common.core.TangemError import org.rekotlin.Action internal sealed interface WelcomeAction : Action { - data class ProceedWithBiometrics(val afterUnlockIntent: Intent? = null) : WelcomeAction { + data object ProceedWithBiometrics : WelcomeAction { object Success : WelcomeAction data class Error(val error: TangemError) : WelcomeAction } @@ -17,8 +16,6 @@ internal sealed interface WelcomeAction : Action { data class ChangeProgress(val showProgress: Boolean) : WelcomeAction } - data class ProceedWithIntent(val intent: Intent) : WelcomeAction - object CloseError : WelcomeAction object ClearUserWallets : WelcomeAction diff --git a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt index d107bc7422..af60602c2b 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt @@ -1,6 +1,5 @@ package com.tangem.tap.features.welcome.redux -import android.content.Intent import com.tangem.common.core.TangemSdkError import com.tangem.common.doOnFailure import com.tangem.common.doOnResult @@ -41,7 +40,6 @@ internal class WelcomeMiddleware { private fun handleAction(action: WelcomeAction) { mainScope.launch { when (action) { - is WelcomeAction.ProceedWithIntent -> proceedWithIntent(action.intent) is WelcomeAction.ProceedWithBiometrics -> proceedWithBiometrics() is WelcomeAction.ProceedWithCard -> proceedWithCard() is WelcomeAction.ClearUserWallets -> disableUserWalletsSaving() @@ -50,26 +48,7 @@ internal class WelcomeMiddleware { } } - private suspend fun proceedWithIntent(initialIntent: Intent) { - Timber.d( - """ - Proceeding with intent - |- Intent: $initialIntent - """.trimIndent(), - ) - - val hasUncompletedBackup = backupService.hasIncompletedBackup - - if (!hasUncompletedBackup) { - store.dispatchWithMain(WelcomeAction.ProceedWithBiometrics(initialIntent)) - } else { - store.dispatchWithMain(WelcomeAction.ProceedWithCard) - } - } - private suspend fun proceedWithBiometrics() { - Timber.d("Proceeding with biometry") - val userWalletsListManager = store.inject(DaggerGraphState::generalUserWalletsListManager) userWalletsListManager.unlockIfLockable(type = UnlockType.ANY) .doOnFailure { error -> @@ -89,8 +68,6 @@ internal class WelcomeMiddleware { } private suspend fun proceedWithCard() { - Timber.d("Proceeding with card") - scanCardInternal { scanResponse -> val userWalletBuilder = store.inject(DaggerGraphState::coldUserWalletBuilderFactory).create(scanResponse) diff --git a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeReducer.kt b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeReducer.kt index 1f475f9a80..0d9645dea6 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeReducer.kt @@ -14,7 +14,6 @@ internal object WelcomeReducer { private fun internalReduce(action: WelcomeAction, state: WelcomeState): WelcomeState { return when (action) { - is WelcomeAction.ProceedWithIntent -> state.copy(intent = action.intent) is WelcomeAction.ProceedWithBiometrics -> state.copy(isUnlockWithBiometricsInProgress = true) is WelcomeAction.ProceedWithCard -> state.copy(isUnlockWithCardInProgress = true) is WelcomeAction.ProceedWithBiometrics.Error -> state.copy( diff --git a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeState.kt b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeState.kt index a8a55d3984..a4fce23f93 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeState.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeState.kt @@ -1,12 +1,10 @@ package com.tangem.tap.features.welcome.redux -import android.content.Intent import com.tangem.common.core.TangemError import org.rekotlin.StateType data class WelcomeState( val isUnlockWithBiometricsInProgress: Boolean = false, val isUnlockWithCardInProgress: Boolean = false, - val intent: Intent? = null, val error: TangemError? = null, ) : StateType \ 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 9fdef856a9..22bb2b0b4a 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 @@ -3,6 +3,7 @@ package com.tangem.tap.routing.component import android.content.Intent import androidx.compose.runtime.Immutable import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.entity.InitScreenLaunchMode import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.ui.decompose.ComposableContentComponent @@ -22,6 +23,10 @@ internal interface RoutingComponent : ComposableContentComponent { } interface Factory { - fun create(context: AppComponentContext, initialStack: List?): RoutingComponent + fun create( + context: AppComponentContext, + initialStack: List?, + launchMode: InitScreenLaunchMode, + ): RoutingComponent } } \ No newline at end of file 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 86b7acd8d4..8030c44f97 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 @@ -9,6 +9,7 @@ import com.arkivanov.decompose.value.subscribe import com.arkivanov.essenty.lifecycle.subscribe import com.google.android.material.snackbar.Snackbar import com.tangem.common.routing.AppRoute +import com.tangem.common.routing.entity.InitScreenLaunchMode import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.child import com.tangem.core.decompose.context.childByContext @@ -17,27 +18,36 @@ import com.tangem.core.ui.UiDependencies import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.message.SnackbarMessage +import com.tangem.domain.card.repository.CardRepository +import com.tangem.domain.core.wallets.UserWalletsListRepository +import com.tangem.domain.models.wallet.isLocked +import com.tangem.domain.onboarding.repository.OnboardingRepository import com.tangem.features.hotwallet.HotAccessCodeRequestComponent import com.tangem.features.hotwallet.accesscoderequest.proxy.HotWalletPasswordRequesterProxy import com.tangem.features.walletconnect.components.WcRoutingComponent import com.tangem.hot.sdk.TangemHotSdk import com.tangem.hot.sdk.android.create import com.tangem.tap.common.SnackbarHandler +import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.features.hot.TangemHotSDKProxy +import com.tangem.tap.features.onboarding.products.wallet.redux.BackupDialog import com.tangem.tap.routing.RootContent import com.tangem.tap.routing.component.RoutingComponent import com.tangem.tap.routing.component.RoutingComponent.Child import com.tangem.tap.routing.configurator.AppRouterConfig import com.tangem.tap.routing.utils.ChildFactory import com.tangem.tap.routing.utils.DeepLinkFactory +import com.tangem.tap.store import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import kotlinx.coroutines.launch @Suppress("LongParameterList") internal class DefaultRoutingComponent @AssistedInject constructor( @Assisted context: AppComponentContext, @Assisted val initialStack: List?, + @Assisted val launchMode: InitScreenLaunchMode, private val childFactory: ChildFactory, private val appRouterConfig: AppRouterConfig, private val uiDependencies: UiDependencies, @@ -46,6 +56,9 @@ internal class DefaultRoutingComponent @AssistedInject constructor( private val tangemHotSDKProxy: TangemHotSDKProxy, private val hotAccessCodeRequestComponentFactory: HotAccessCodeRequestComponent.Factory, private val hotAccessCodeRequesterProxy: HotWalletPasswordRequesterProxy, + private val userWalletsListRepository: UserWalletsListRepository, + private val cardRepository: CardRepository, + private val onboardingRepository: OnboardingRepository, ) : RoutingComponent, AppComponentContext by context, SnackbarHandler { @@ -87,6 +100,42 @@ internal class DefaultRoutingComponent @AssistedInject constructor( } configureProxies() + initializeInitialNavigation() + } + + private fun initializeInitialNavigation() { + if (initialStack.isNullOrEmpty()) { + componentScope.launch { + val initialRoute = resolveInitialRoute() + router.replaceAll(initialRoute) + } + } + } + + private suspend fun resolveInitialRoute(): AppRoute { + val userWallets = userWalletsListRepository.userWalletsSync() + + return when { + userWallets.isEmpty() -> { + val shouldShowTos = !cardRepository.isTangemTOSAccepted() + if (shouldShowTos) { + AppRoute.Disclaimer(isTosAccepted = false) + } else { + AppRoute.Home(launchMode = launchMode) + } + } + userWallets.any { it.isLocked } -> { + AppRoute.Welcome( + launchMode = launchMode, + ) + } + else -> { + AppRoute.Wallet + } + }.also { + appRouterConfig.isInitialized.value = true + checkForUnfinishedBackup() + } } @Composable @@ -131,7 +180,6 @@ internal class DefaultRoutingComponent @AssistedInject constructor( messageSender.send(SnackbarMessage(message = TextReference.EMPTY)) } - // TODO: Find correct initial route here: [REDACTED_JIRA] private fun getInitialStackOrInit(): List = if (initialStack.isNullOrEmpty()) { listOf(AppRoute.Initial) } else { @@ -153,6 +201,17 @@ internal class DefaultRoutingComponent @AssistedInject constructor( @AssistedFactory interface Factory : RoutingComponent.Factory { - override fun create(context: AppComponentContext, initialStack: List?): DefaultRoutingComponent + override fun create( + context: AppComponentContext, + initialStack: List?, + launchMode: InitScreenLaunchMode, + ): DefaultRoutingComponent + } + + private fun checkForUnfinishedBackup() { + componentScope.launch(dispatchers.main) { + val onboardingScanResponse = onboardingRepository.getUnfinishedFinalizeOnboarding() ?: return@launch + store.dispatch(GlobalAction.ShowDialog(BackupDialog.UnfinishedBackupFound(onboardingScanResponse))) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/routing/configurator/AppRouterConfig.kt b/app/src/main/java/com/tangem/tap/routing/configurator/AppRouterConfig.kt index 5c2c9b3843..393ab3a7df 100644 --- a/app/src/main/java/com/tangem/tap/routing/configurator/AppRouterConfig.kt +++ b/app/src/main/java/com/tangem/tap/routing/configurator/AppRouterConfig.kt @@ -4,12 +4,14 @@ import com.tangem.common.routing.AppRoute import com.tangem.core.decompose.navigation.Router import com.tangem.tap.common.SnackbarHandler import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.MutableStateFlow internal interface AppRouterConfig { var routerScope: CoroutineScope? var componentRouter: Router? var stack: List? + val isInitialized: MutableStateFlow // TODO: Replace with UI message handler: [REDACTED_JIRA] var snackbarHandler: SnackbarHandler? diff --git a/app/src/main/java/com/tangem/tap/routing/configurator/MutableAppRouterConfig.kt b/app/src/main/java/com/tangem/tap/routing/configurator/MutableAppRouterConfig.kt index d1b1e93f7c..776802e429 100644 --- a/app/src/main/java/com/tangem/tap/routing/configurator/MutableAppRouterConfig.kt +++ b/app/src/main/java/com/tangem/tap/routing/configurator/MutableAppRouterConfig.kt @@ -4,11 +4,12 @@ import com.tangem.common.routing.AppRoute import com.tangem.core.decompose.navigation.Router import com.tangem.tap.common.SnackbarHandler import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.MutableStateFlow internal class MutableAppRouterConfig : AppRouterConfig { - override var routerScope: CoroutineScope? = null override var componentRouter: Router? = null override var stack: List? = null override var snackbarHandler: SnackbarHandler? = null + override val isInitialized: MutableStateFlow = MutableStateFlow(false) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt index fb0bfb3588..4f4eca31b6 100644 --- a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt +++ b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt @@ -155,7 +155,6 @@ internal class ChildFactory @Inject constructor( context = context, params = WelcomeComponent.Params( launchMode = route.launchMode, - intent = route.intent, ), componentFactory = welcomeComponentFactory, ) diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt index 448f369ac0..716edd6750 100644 --- a/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt +++ b/common/routing/src/main/kotlin/com/tangem/common/routing/AppRoute.kt @@ -4,7 +4,6 @@ import android.os.Bundle import com.tangem.common.routing.bundle.RouteBundleParams import com.tangem.common.routing.bundle.bundle import com.tangem.common.routing.entity.InitScreenLaunchMode -import com.tangem.common.routing.entity.SerializableIntent import com.tangem.core.decompose.navigation.Route import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.feedback.models.WalletMetaInfo @@ -32,9 +31,6 @@ sealed class AppRoute(val path: String) : Route { data class Welcome( @Deprecated("No longer used, will be removed in future releases") val launchMode: InitScreenLaunchMode = InitScreenLaunchMode.Standard, - // we still have this param to be handled by WalletConnectLinkIntentHandler in WelcomeMiddleware - @Deprecated("No longer used, will be removed in future releases") - val intent: SerializableIntent? = null, ) : AppRoute(path = "/welcome"), RouteBundleParams { override fun getBundle(): Bundle = bundle(serializer()) diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/entity/SerializableBundle.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/entity/SerializableBundle.kt deleted file mode 100644 index fcac1b0422..0000000000 --- a/common/routing/src/main/kotlin/com/tangem/common/routing/entity/SerializableBundle.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.tangem.common.routing.entity - -import android.os.Bundle -import kotlinx.serialization.Serializable - -@Serializable -data class SerializableBundle( - val map: Map, -) { - - constructor(bundle: Bundle) : this( - map = bundle.keySet().mapNotNull { key -> - bundle.getString(key)?.let { key to it } - }.toMap(), - ) - - fun toBundle(): Bundle { - return Bundle().apply { - map.forEach { (key, value) -> - putString(key, value) - } - } - } -} \ No newline at end of file diff --git a/common/routing/src/main/kotlin/com/tangem/common/routing/entity/SerializableIntent.kt b/common/routing/src/main/kotlin/com/tangem/common/routing/entity/SerializableIntent.kt deleted file mode 100644 index cb4f2b871a..0000000000 --- a/common/routing/src/main/kotlin/com/tangem/common/routing/entity/SerializableIntent.kt +++ /dev/null @@ -1,52 +0,0 @@ -package com.tangem.common.routing.entity - -import android.content.ComponentName -import android.content.Intent -import android.net.Uri -import kotlinx.serialization.Serializable - -@Serializable -data class SerializableIntent( - val action: String?, - val dataString: String?, - val categories: Set?, - val type: String?, - val packageValue: String?, - val component: String?, - val flags: Int, - // CAUTION: works wrong with SerializableBundle constructor(bundle: Bundle), need to be removed - val extras: SerializableBundle?, -) { - - constructor(intent: Intent) : this( - action = intent.action, - dataString = intent.dataString, - categories = intent.categories, - type = intent.type, - packageValue = intent.`package`, - component = intent.component?.flattenToString(), - flags = intent.flags, - extras = intent.extras?.let(::SerializableBundle), - ) - - fun toIntent(): Intent { - val intent = Intent() - - intent.action = action - intent.setDataAndType( - dataString?.let { Uri.parse(it) }, - type, - ) - categories?.let { categories -> - for (category in categories) { - intent.addCategory(category) - } - } - intent.`package` = packageValue - intent.component = component?.let { ComponentName.unflattenFromString(it) } - intent.flags = flags - extras?.let { intent.putExtras(it.toBundle()) } - - return intent - } -} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/AccessCodeComponent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/AccessCodeComponent.kt index 160d1a228f..62fdd324b1 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/AccessCodeComponent.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/accesscode/AccessCodeComponent.kt @@ -25,7 +25,9 @@ internal class AccessCodeComponent @AssistedInject constructor( override fun Content(modifier: Modifier) { val state by model.uiState.collectAsStateWithLifecycle() - DisableScreenshotsDisposableEffect() + if (!state.isConfirmMode) { + DisableScreenshotsDisposableEffect() + } AccessCode( modifier = modifier, diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletModel.kt index b218fa6aea..423b382025 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/addexistingwallet/entry/AddExistingWalletModel.kt @@ -63,9 +63,7 @@ internal class AddExistingWalletModel @Inject constructor( modelScope.launch { val shouldRequestPush = shouldAskPermissionUseCase(PUSH_PERMISSION) if (shouldRequestPush) { - // is yet blocked by [REDACTED_TASK_KEY] - // stackNavigation.replaceAll(AddExistingWalletRoute.PushNotifications) - stackNavigation.replaceAll(AddExistingWalletRoute.SetupFinished) + stackNavigation.replaceAll(AddExistingWalletRoute.PushNotifications) } else { stackNavigation.replaceAll(AddExistingWalletRoute.SetupFinished) } diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/phrase/ManualBackupPhraseComponent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/phrase/ManualBackupPhraseComponent.kt index 62d7b79ed4..b66835823a 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/phrase/ManualBackupPhraseComponent.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/manualbackup/phrase/ManualBackupPhraseComponent.kt @@ -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.phrase.model.ManualBackupPhraseModel import com.tangem.features.hotwallet.manualbackup.phrase.ui.ManualBackupPhraseContent @@ -22,6 +23,7 @@ internal class ManualBackupPhraseComponent @AssistedInject constructor( @Composable override fun Content(modifier: Modifier) { val state by model.uiState.collectAsStateWithLifecycle() + DisableScreenshotsDisposableEffect() ManualBackupPhraseContent( state = state, modifier = modifier, diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/WalletActivationModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/WalletActivationModel.kt index cfd50e4c72..e5cd76ac2f 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/WalletActivationModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/walletactivation/entry/WalletActivationModel.kt @@ -73,9 +73,7 @@ internal class WalletActivationModel @Inject constructor( modelScope.launch { val shouldRequestPush = shouldAskPermissionUseCase(PUSH_PERMISSION) if (shouldRequestPush) { - // is yet blocked by [REDACTED_TASK_KEY] - // stackNavigation.replaceAll(AddExistingWalletRoute.PushNotifications) - stackNavigation.replaceAll(WalletActivationRoute.SetupFinished) + stackNavigation.replaceAll(WalletActivationRoute.PushNotifications) } else { stackNavigation.replaceAll(WalletActivationRoute.SetupFinished) }