Updated on 2026-08-14
This commit is contained in:
parent
56956a0c2b
commit
b73d93928b
22 changed files with 292 additions and 264 deletions
|
|
@ -1,18 +1,20 @@
|
|||
package com.tangem.tap
|
||||
|
||||
import android.app.Application
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.lifecycle.flowWithLifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import arrow.core.getOrElse
|
||||
import by.kirich1409.viewbindingdelegate.viewBinding
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.tangem.core.navigation.AppScreen
|
||||
|
|
@ -32,7 +34,6 @@ import com.tangem.tap.common.DialogManager
|
|||
import com.tangem.tap.common.OnActivityResultCallback
|
||||
import com.tangem.tap.common.SnackbarHandler
|
||||
import com.tangem.tap.common.apptheme.MutableAppThemeModeHolder
|
||||
import com.tangem.tap.common.extensions.dispatchOnMain
|
||||
import com.tangem.tap.common.redux.NotificationsHandler
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.common.shop.googlepay.GooglePayService
|
||||
|
|
@ -49,19 +50,15 @@ import com.tangem.tap.features.intentHandler.handlers.SellCurrencyIntentHandler
|
|||
import com.tangem.tap.features.intentHandler.handlers.WalletConnectLinkIntentHandler
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction
|
||||
import com.tangem.tap.features.shop.redux.ShopAction
|
||||
import com.tangem.tap.features.welcome.redux.WelcomeAction
|
||||
import com.tangem.tap.features.welcome.ui.WelcomeFragment
|
||||
import com.tangem.tap.proxy.AppStateHolder
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphAction
|
||||
import com.tangem.utils.coroutines.FeatureCoroutineExceptionHandler
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.wallet.databinding.ActivityMainBinding
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import java.lang.ref.WeakReference
|
||||
import javax.inject.Inject
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
|
@ -117,8 +114,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
@Inject
|
||||
lateinit var walletConnectInteractor: WalletConnectInteractor
|
||||
|
||||
private val viewModel: MainViewModel by viewModels()
|
||||
private var isInitializing: Boolean = true
|
||||
private lateinit var appThemeModeFlow: SharedFlow<AppThemeMode?>
|
||||
|
||||
// TODO: fixme: inject through DI
|
||||
private val intentProcessor: IntentProcessor = IntentProcessor()
|
||||
|
|
@ -130,25 +126,24 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
private val onActivityResultCallbacks = mutableListOf<OnActivityResultCallback>()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
val splashScreen = installSplashScreen()
|
||||
|
||||
if (!isDarkThemeFeatureEnabled(application)) {
|
||||
setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
|
||||
}
|
||||
installSplashScreen()
|
||||
installAppTheme() // We need to call it before onCreate to prevent unnecessary activity recreation
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
cardSdkLifecycleObserver.onCreate(context = this)
|
||||
|
||||
bootstrapMainStateUpdates(application)
|
||||
|
||||
splashScreen.setKeepOnScreenCondition { isInitializing }
|
||||
installActivityDependencies()
|
||||
observeAppThemeModeUpdates()
|
||||
|
||||
setContentView(R.layout.activity_main)
|
||||
systemActions()
|
||||
initContent()
|
||||
|
||||
checkGooglePayAvailability()
|
||||
}
|
||||
|
||||
private fun installActivityDependencies() {
|
||||
store.dispatch(NavigationAction.ActivityCreated(WeakReference(this)))
|
||||
|
||||
cardSdkLifecycleObserver.onCreate(context = this)
|
||||
tangemSdkManager = injectedTangemSdkManager
|
||||
appStateHolder.tangemSdkManager = tangemSdkManager
|
||||
backupService = BackupService.init(cardSdkConfigRepository.sdk, this)
|
||||
|
|
@ -157,11 +152,6 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
initUserWalletsListManager()
|
||||
initIntentHandlers()
|
||||
|
||||
store.dispatch(
|
||||
ShopAction.CheckIfGooglePayAvailable(
|
||||
GooglePayService(createPaymentsClient(this), this),
|
||||
),
|
||||
)
|
||||
store.dispatch(
|
||||
DaggerGraphAction.SetActivityDependencies(
|
||||
testerRouter = testerRouter,
|
||||
|
|
@ -174,9 +164,57 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
)
|
||||
}
|
||||
|
||||
private fun isDarkThemeFeatureEnabled(application: Application): Boolean {
|
||||
val featureToggle = (application as TapApplication).darkThemeFeatureToggle
|
||||
return featureToggle.isDarkThemeEnabled
|
||||
private fun installAppTheme() {
|
||||
appThemeModeFlow = createAppThemeModeFlow()
|
||||
val mode = runBlocking { appThemeModeFlow.filterNotNull().first() }
|
||||
|
||||
updateAppTheme(mode)
|
||||
}
|
||||
|
||||
private fun observeAppThemeModeUpdates() {
|
||||
appThemeModeFlow
|
||||
.filterNotNull()
|
||||
.flowWithLifecycle(lifecycle)
|
||||
.onEach(::updateAppTheme)
|
||||
.launchIn(lifecycleScope)
|
||||
}
|
||||
|
||||
@SuppressLint("SourceLockedOrientationActivity")
|
||||
private fun initContent() {
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
|
||||
supportFragmentManager.registerFragmentLifecycleCallbacks(
|
||||
NavBarInsetsFragmentLifecycleCallback(),
|
||||
true,
|
||||
)
|
||||
|
||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||
}
|
||||
|
||||
private fun checkGooglePayAvailability() {
|
||||
store.dispatch(
|
||||
ShopAction.CheckIfGooglePayAvailable(
|
||||
GooglePayService(createPaymentsClient(this), this),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createAppThemeModeFlow(): SharedFlow<AppThemeMode?> {
|
||||
val tapApplication = application as TapApplication
|
||||
val featureToggle = tapApplication.darkThemeFeatureToggle
|
||||
|
||||
return if (featureToggle.isDarkThemeEnabled) {
|
||||
tapApplication.getAppThemeModeUseCase()
|
||||
.map { maybeMode ->
|
||||
maybeMode.getOrElse { AppThemeMode.DEFAULT }
|
||||
}
|
||||
.shareIn(
|
||||
scope = lifecycleScope + Dispatchers.IO,
|
||||
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = 5_000),
|
||||
)
|
||||
} else {
|
||||
MutableStateFlow(AppThemeMode.FORCE_LIGHT)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
|
|
@ -189,7 +227,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
// TODO: RESEARCH! NotificationsHandler is created in onResume and destroyed in onStop
|
||||
notificationsHandler = NotificationsHandler(binding.fragmentContainer)
|
||||
|
||||
navigateToInitialScreenIfNeededOnResume(intent)
|
||||
navigateToInitialScreenIfNeeded(intent)
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
|
|
@ -207,12 +245,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
|
||||
private fun initIntentHandlers() {
|
||||
val hasSavedWalletsProvider = { store.state.globalState.userWalletsListManager?.hasUserWallets == true }
|
||||
intentProcessor.addHandler(
|
||||
BackgroundScanIntentHandler(
|
||||
hasSavedWalletsProvider,
|
||||
lifecycleScope,
|
||||
),
|
||||
)
|
||||
intentProcessor.addHandler(BackgroundScanIntentHandler(hasSavedWalletsProvider, lifecycleScope))
|
||||
intentProcessor.addHandler(WalletConnectLinkIntentHandler())
|
||||
intentProcessor.addHandler(BuyCurrencyIntentHandler())
|
||||
intentProcessor.addHandler(SellCurrencyIntentHandler())
|
||||
|
|
@ -230,38 +263,21 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
store.dispatch(GlobalAction.UpdateUserWalletsListManager(manager))
|
||||
}
|
||||
|
||||
private fun bootstrapMainStateUpdates(application: Application) {
|
||||
viewModel.state
|
||||
.onEach { state ->
|
||||
isInitializing = state is GlobalSettingsState.Loading
|
||||
private fun updateAppTheme(appThemeMode: AppThemeMode) {
|
||||
MutableAppThemeModeHolder.value = appThemeMode
|
||||
MutableAppThemeModeHolder.isDarkThemeActive = isDarkTheme()
|
||||
|
||||
when (state) {
|
||||
is GlobalSettingsState.Content -> {
|
||||
if (isDarkThemeFeatureEnabled(application)) {
|
||||
MutableAppThemeModeHolder.value = state.appThemeMode
|
||||
MutableAppThemeModeHolder.isDarkThemeActive = isDarkTheme()
|
||||
val mode = when (appThemeMode) {
|
||||
AppThemeMode.FORCE_DARK -> AppCompatDelegate.MODE_NIGHT_YES
|
||||
AppThemeMode.FORCE_LIGHT -> AppCompatDelegate.MODE_NIGHT_NO
|
||||
AppThemeMode.FOLLOW_SYSTEM -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||
}
|
||||
|
||||
val mode = when (state.appThemeMode) {
|
||||
AppThemeMode.FORCE_DARK -> AppCompatDelegate.MODE_NIGHT_YES
|
||||
AppThemeMode.FORCE_LIGHT -> AppCompatDelegate.MODE_NIGHT_NO
|
||||
AppThemeMode.FOLLOW_SYSTEM -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||
}
|
||||
setDefaultNightMode(mode)
|
||||
} else {
|
||||
MutableAppThemeModeHolder.value = AppThemeMode.FORCE_LIGHT
|
||||
}
|
||||
}
|
||||
is GlobalSettingsState.Loading -> Unit
|
||||
}
|
||||
}
|
||||
.launchIn(lifecycleScope)
|
||||
setDefaultNightMode(mode)
|
||||
}
|
||||
|
||||
private fun isDarkTheme(): Boolean {
|
||||
return when (
|
||||
resources.configuration.uiMode and
|
||||
Configuration.UI_MODE_NIGHT_MASK
|
||||
) {
|
||||
return when (resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
|
||||
Configuration.UI_MODE_NIGHT_YES -> true
|
||||
Configuration.UI_MODE_NIGHT_NO -> false
|
||||
Configuration.UI_MODE_NIGHT_UNDEFINED -> false
|
||||
|
|
@ -269,17 +285,6 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
}
|
||||
}
|
||||
|
||||
private fun systemActions() {
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
|
||||
supportFragmentManager.registerFragmentLifecycleCallbacks(
|
||||
NavBarInsetsFragmentLifecycleCallback(),
|
||||
true,
|
||||
)
|
||||
|
||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent?) {
|
||||
super.onNewIntent(intent)
|
||||
|
||||
|
|
@ -341,43 +346,40 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
lockUserWalletsTimer?.restart()
|
||||
}
|
||||
|
||||
private fun navigateToInitialScreenIfNeededOnResume(intentWhichStartedActivity: Intent?) {
|
||||
private fun navigateToInitialScreenIfNeeded(intentWhichStartedActivity: Intent?) {
|
||||
val backStackIsEmpty = supportFragmentManager.backStackEntryCount == 0
|
||||
val isNotScannedBefore = store.state.globalState.scanResponse == null
|
||||
val isOnboardingServiceNotActive = store.state.globalState.onboardingState.onboardingStarted
|
||||
val isShopNotOpened = store.state.shopState.total != null
|
||||
|
||||
when {
|
||||
!backStackIsEmpty && isNotScannedBefore && isOnboardingServiceNotActive && isShopNotOpened -> {
|
||||
navigateToInitialScreenOnResume(intentWhichStartedActivity)
|
||||
navigateToInitialScreen(intentWhichStartedActivity)
|
||||
}
|
||||
backStackIsEmpty -> {
|
||||
navigateToInitialScreenOnResume(intentWhichStartedActivity)
|
||||
navigateToInitialScreen(intentWhichStartedActivity)
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
private fun navigateToInitialScreenOnResume(intentWhichStartedActivity: Intent?) {
|
||||
private fun navigateToInitialScreen(intentWhichStartedActivity: Intent?) {
|
||||
if (store.state.globalState.userWalletsListManager?.hasUserWallets == true) {
|
||||
store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Welcome))
|
||||
store.dispatchOnMain(WelcomeAction.SetInitialIntent(intentWhichStartedActivity))
|
||||
lifecycleScope.launch {
|
||||
val handler = BackgroundScanIntentHandler(
|
||||
hasSavedUserWalletsProvider = { true },
|
||||
lifecycleCoroutineScope = lifecycleScope,
|
||||
)
|
||||
val isBackgroundScanHandled = handler.handleIntent(intentWhichStartedActivity)
|
||||
val hasNotIncompletedBackup = !backupService.hasIncompletedBackup
|
||||
if (!isBackgroundScanHandled && hasNotIncompletedBackup) {
|
||||
store.dispatchOnMain(WelcomeAction.ProceedWithBiometrics)
|
||||
}
|
||||
}
|
||||
store.dispatch(
|
||||
NavigationAction.NavigateTo(
|
||||
screen = AppScreen.Welcome,
|
||||
bundle = intentWhichStartedActivity?.let {
|
||||
bundleOf(WelcomeFragment.INITIAL_INTENT_KEY to it)
|
||||
},
|
||||
),
|
||||
)
|
||||
} else {
|
||||
store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Home))
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Home))
|
||||
lifecycleScope.launch {
|
||||
intentProcessor.handleIntent(intentWhichStartedActivity)
|
||||
}
|
||||
}
|
||||
|
||||
store.dispatch(BackupAction.CheckForUnfinishedBackup)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue