Updated on 2026-08-14
This commit is contained in:
parent
5bbbd9e945
commit
f74f4e7083
12 changed files with 76 additions and 89 deletions
|
|
@ -35,13 +35,10 @@ import com.tangem.tap.common.OnActivityResultCallback
|
|||
import com.tangem.tap.common.SnackbarHandler
|
||||
import com.tangem.tap.common.apptheme.MutableAppThemeModeHolder
|
||||
import com.tangem.tap.common.redux.NotificationsHandler
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.common.shop.googlepay.GooglePayService
|
||||
import com.tangem.tap.common.shop.googlepay.GooglePayService.Companion.LOAD_PAYMENT_DATA_REQUEST_CODE
|
||||
import com.tangem.tap.common.shop.googlepay.GooglePayUtil.createPaymentsClient
|
||||
import com.tangem.tap.domain.TangemSdkManager
|
||||
import com.tangem.tap.domain.userWalletList.di.provideBiometricImplementation
|
||||
import com.tangem.tap.domain.userWalletList.di.provideRuntimeImplementation
|
||||
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor
|
||||
import com.tangem.tap.features.intentHandler.IntentProcessor
|
||||
import com.tangem.tap.features.intentHandler.handlers.BackgroundScanIntentHandler
|
||||
|
|
@ -149,7 +146,6 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
backupService = BackupService.init(cardSdkConfigRepository.sdk, this)
|
||||
lockUserWalletsTimer = LockUserWalletsTimer(owner = this)
|
||||
|
||||
initUserWalletsListManager()
|
||||
initIntentHandlers()
|
||||
|
||||
store.dispatch(
|
||||
|
|
@ -251,18 +247,6 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
intentProcessor.addHandler(SellCurrencyIntentHandler())
|
||||
}
|
||||
|
||||
private fun initUserWalletsListManager() {
|
||||
val manager = if (preferencesStorage.shouldSaveUserWallets) {
|
||||
UserWalletsListManager.provideBiometricImplementation(
|
||||
context = applicationContext,
|
||||
tangemSdkManager = tangemSdkManager,
|
||||
)
|
||||
} else {
|
||||
UserWalletsListManager.provideRuntimeImplementation()
|
||||
}
|
||||
store.dispatch(GlobalAction.UpdateUserWalletsListManager(manager))
|
||||
}
|
||||
|
||||
private fun updateAppTheme(appThemeMode: AppThemeMode) {
|
||||
MutableAppThemeModeHolder.value = appThemeMode
|
||||
MutableAppThemeModeHolder.isDarkThemeActive = isDarkTheme()
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import com.tangem.domain.common.LogConfig
|
|||
import com.tangem.domain.settings.repositories.AppRatingRepository
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.wallets.legacy.WalletManagersRepository
|
||||
import com.tangem.features.tokendetails.featuretoggles.TokenDetailsFeatureToggles
|
||||
import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles
|
||||
|
|
@ -58,6 +59,8 @@ import com.tangem.tap.domain.tokens.UserTokensRepository
|
|||
import com.tangem.tap.domain.tokens.UserTokensStorageService
|
||||
import com.tangem.tap.domain.totalBalance.TotalFiatBalanceCalculator
|
||||
import com.tangem.tap.domain.totalBalance.di.provideDefaultImplementation
|
||||
import com.tangem.tap.domain.userWalletList.di.provideBiometricImplementation
|
||||
import com.tangem.tap.domain.userWalletList.di.provideRuntimeImplementation
|
||||
import com.tangem.tap.domain.walletCurrencies.WalletCurrenciesManager
|
||||
import com.tangem.tap.domain.walletCurrencies.di.provideDefaultImplementation
|
||||
import com.tangem.tap.domain.walletStores.WalletStoresManager
|
||||
|
|
@ -254,6 +257,7 @@ internal class TapApplication : Application(), ImageLoaderFactory {
|
|||
val configLoader = FeaturesLocalLoader(assetReader, MoshiConverter.sdkMoshi, BuildConfig.ENVIRONMENT)
|
||||
initConfigManager(configLoader, ::initWithConfigDependency)
|
||||
initWarningMessagesManager()
|
||||
initUserWalletsListManager()
|
||||
|
||||
loadNativeLibraries()
|
||||
|
||||
|
|
@ -391,4 +395,14 @@ internal class TapApplication : Application(), ImageLoaderFactory {
|
|||
private fun initWarningMessagesManager() {
|
||||
store.dispatch(GlobalAction.SetWarningManager(WarningMessagesManager()))
|
||||
}
|
||||
|
||||
private fun initUserWalletsListManager() {
|
||||
val manager = if (preferencesStorage.shouldSaveUserWallets) {
|
||||
UserWalletsListManager.provideBiometricImplementation(applicationContext)
|
||||
} else {
|
||||
UserWalletsListManager.provideRuntimeImplementation()
|
||||
}
|
||||
|
||||
store.dispatch(GlobalAction.UpdateUserWalletsListManager(manager))
|
||||
}
|
||||
}
|
||||
|
|
@ -3,27 +3,28 @@ package com.tangem.tap.domain.userWalletList.di
|
|||
import android.content.Context
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import com.tangem.common.Provider
|
||||
import com.tangem.common.authentication.AuthenticatedStorage
|
||||
import com.tangem.common.json.TangemSdkAdapter
|
||||
import com.tangem.common.services.secure.SecureStorage
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.sdk.storage.AndroidSecureStorage
|
||||
import com.tangem.sdk.storage.createEncryptedSharedPreferences
|
||||
import com.tangem.tap.domain.TangemSdkManager
|
||||
import com.tangem.tap.domain.userWalletList.implementation.BiometricUserWalletsListManager
|
||||
import com.tangem.tap.domain.userWalletList.implementation.RuntimeUserWalletsListManager
|
||||
import com.tangem.tap.domain.userWalletList.repository.DelegatedKeystoreManager
|
||||
import com.tangem.tap.domain.userWalletList.repository.UserWalletsKeysStoreDecorator
|
||||
import com.tangem.tap.domain.userWalletList.repository.implementation.BiometricUserWalletsKeysRepository
|
||||
import com.tangem.tap.domain.userWalletList.repository.implementation.DefaultSelectedUserWalletRepository
|
||||
import com.tangem.tap.domain.userWalletList.repository.implementation.DefaultUserWalletsPublicInformationRepository
|
||||
import com.tangem.tap.domain.userWalletList.repository.implementation.DefaultUserWalletsSensitiveInformationRepository
|
||||
import com.tangem.tap.domain.userWalletList.utils.json.*
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
|
||||
private const val USER_WALLETS_STORAGE_NAME = "user_wallets_storage"
|
||||
|
||||
fun UserWalletsListManager.Companion.provideBiometricImplementation(
|
||||
context: Context,
|
||||
tangemSdkManager: TangemSdkManager,
|
||||
applicationContext: Context,
|
||||
): UserWalletsListManager {
|
||||
val moshi = Moshi.Builder()
|
||||
.add(WalletDerivedKeysMapAdapter())
|
||||
|
|
@ -40,7 +41,7 @@ fun UserWalletsListManager.Companion.provideBiometricImplementation(
|
|||
|
||||
val secureStorage = AndroidSecureStorage(
|
||||
preferences = SecureStorage.createEncryptedSharedPreferences(
|
||||
context = context,
|
||||
context = applicationContext,
|
||||
storageName = USER_WALLETS_STORAGE_NAME,
|
||||
),
|
||||
)
|
||||
|
|
@ -48,16 +49,17 @@ fun UserWalletsListManager.Companion.provideBiometricImplementation(
|
|||
val authenticatedStorage = AuthenticatedStorage(
|
||||
secureStorage = UserWalletsKeysStoreDecorator(
|
||||
featureStorage = secureStorage,
|
||||
cardSdkStorage = tangemSdkManager.secureStorage,
|
||||
cardSdkStorageProvider = Provider { tangemSdkManager.secureStorage },
|
||||
),
|
||||
keystoreManager = DelegatedKeystoreManager(
|
||||
keystoreManagerProvider = Provider { tangemSdkManager.keystoreManager },
|
||||
),
|
||||
keystoreManager = tangemSdkManager.keystoreManager,
|
||||
)
|
||||
|
||||
val keysRepository = BiometricUserWalletsKeysRepository(
|
||||
moshi = moshi,
|
||||
secureStorage = secureStorage,
|
||||
authenticatedStorage = authenticatedStorage,
|
||||
|
||||
)
|
||||
val publicInformationRepository = DefaultUserWalletsPublicInformationRepository(
|
||||
moshi = moshi,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
package com.tangem.tap.domain.userWalletList.repository
|
||||
|
||||
import com.tangem.common.Provider
|
||||
import com.tangem.common.authentication.KeystoreManager
|
||||
import javax.crypto.SecretKey
|
||||
|
||||
internal class DelegatedKeystoreManager(
|
||||
private val keystoreManagerProvider: Provider<KeystoreManager>,
|
||||
) : KeystoreManager {
|
||||
|
||||
override suspend fun authenticateAndGetKey(keyAlias: String): SecretKey? {
|
||||
return keystoreManagerProvider().authenticateAndGetKey(keyAlias)
|
||||
}
|
||||
|
||||
override suspend fun storeKey(keyAlias: String, key: SecretKey) {
|
||||
keystoreManagerProvider().storeKey(keyAlias, key)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +1,29 @@
|
|||
package com.tangem.tap.domain.userWalletList.repository
|
||||
|
||||
import com.tangem.common.Provider
|
||||
import com.tangem.common.services.secure.SecureStorage
|
||||
|
||||
/**
|
||||
* A decorator for [SecureStorage] that facilitates data migration between two storages.
|
||||
*
|
||||
* @property featureStorage The primary storage, which will eventually contain all user data.
|
||||
* @property cardSdkStorage The SDK's storage where user data might have been previously stored.
|
||||
* @property cardSdkStorageProvider The SDK's storage where user data might have been previously stored.
|
||||
*/
|
||||
internal class UserWalletsKeysStoreDecorator(
|
||||
private val featureStorage: SecureStorage,
|
||||
private val cardSdkStorage: SecureStorage,
|
||||
private val cardSdkStorageProvider: Provider<SecureStorage>,
|
||||
) : SecureStorage by featureStorage {
|
||||
|
||||
override fun delete(account: String) {
|
||||
featureStorage.delete(account)
|
||||
cardSdkStorage.delete(account)
|
||||
cardSdkStorageProvider().delete(account)
|
||||
}
|
||||
|
||||
override fun get(account: String): ByteArray? {
|
||||
var data = featureStorage.get(account)
|
||||
|
||||
if (data == null) {
|
||||
data = cardSdkStorage.get(account) ?: return null
|
||||
data = cardSdkStorageProvider().get(account) ?: return null
|
||||
featureStorage.store(data, account)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -74,8 +74,7 @@ sealed class DetailsAction : Action {
|
|||
}
|
||||
|
||||
data class CheckBiometricsStatus(
|
||||
val awaitStatusChange: Boolean,
|
||||
val lifecycleCoroutineScope: LifecycleCoroutineScope,
|
||||
val lifecycleScope: LifecycleCoroutineScope,
|
||||
) : AppSettings()
|
||||
|
||||
object EnrollBiometrics : AppSettings()
|
||||
|
|
|
|||
|
|
@ -40,7 +40,8 @@ import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
|
|||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.walletSelector.redux.WalletSelectorAction
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
import com.tangem.utils.coroutines.JobHolder
|
||||
import com.tangem.utils.coroutines.saveIn
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
|
|
@ -209,6 +210,9 @@ class DetailsMiddleware {
|
|||
}
|
||||
|
||||
class AppSettingsMiddleware {
|
||||
|
||||
private val checkBiometricsStatusJobHolder = JobHolder()
|
||||
|
||||
fun handle(state: DetailsState, action: DetailsAction.AppSettings) {
|
||||
when (action) {
|
||||
is DetailsAction.AppSettings.SwitchPrivacySetting -> {
|
||||
|
|
@ -218,11 +222,7 @@ class DetailsMiddleware {
|
|||
}
|
||||
}
|
||||
is DetailsAction.AppSettings.CheckBiometricsStatus -> {
|
||||
checkBiometricsStatus(
|
||||
awaitStatusChange = action.awaitStatusChange,
|
||||
state = state,
|
||||
lifecycleScope = action.lifecycleCoroutineScope,
|
||||
)
|
||||
observeBiometricsStatusChanges(state, action.lifecycleScope)
|
||||
}
|
||||
is DetailsAction.AppSettings.EnrollBiometrics -> {
|
||||
enrollBiometrics()
|
||||
|
|
@ -245,27 +245,20 @@ class DetailsMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param awaitStatusChange If true then start a new coroutine and check the biometric status every 100
|
||||
* milliseconds until it changes
|
||||
* */
|
||||
private fun checkBiometricsStatus(
|
||||
awaitStatusChange: Boolean,
|
||||
state: DetailsState,
|
||||
lifecycleScope: LifecycleCoroutineScope,
|
||||
) {
|
||||
lifecycleScope.launch {
|
||||
if (awaitStatusChange) {
|
||||
while (state.appSettingsState.needEnrollBiometrics == tangemSdkManager.needEnrollBiometrics) {
|
||||
delay(timeMillis = 100)
|
||||
private fun observeBiometricsStatusChanges(state: DetailsState, lifecycleScope: LifecycleCoroutineScope) {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
do {
|
||||
val needEnrollBiometrics = runCatching(tangemSdkManager::needEnrollBiometrics).getOrNull()
|
||||
|
||||
if (needEnrollBiometrics != null &&
|
||||
needEnrollBiometrics != state.appSettingsState.needEnrollBiometrics
|
||||
) {
|
||||
store.dispatchWithMain(DetailsAction.AppSettings.BiometricsStatusChanged(needEnrollBiometrics))
|
||||
}
|
||||
}
|
||||
store.dispatchWithMain(
|
||||
DetailsAction.AppSettings.BiometricsStatusChanged(
|
||||
needEnrollBiometrics = tangemSdkManager.needEnrollBiometrics,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
delay(timeMillis = 500)
|
||||
} while (true)
|
||||
}.saveIn(checkBiometricsStatusJobHolder)
|
||||
}
|
||||
|
||||
private fun enrollBiometrics() {
|
||||
|
|
@ -454,10 +447,7 @@ class DetailsMiddleware {
|
|||
return null
|
||||
}
|
||||
|
||||
return UserWalletsListManager.provideBiometricImplementation(
|
||||
context = context,
|
||||
tangemSdkManager = tangemSdkManager,
|
||||
)
|
||||
return UserWalletsListManager.provideBiometricImplementation(context)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.tangem.tap.features.details.ui.appsettings
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
|
|
@ -32,11 +31,6 @@ internal class AppSettingsFragment : ComposeFragment(), StoreSubscriber<DetailsS
|
|||
AppSettingsViewModel(store, detailsFeatureToggles, appCurrencyRepository)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
viewModel.checkBiometricsStatus(lifecycleScope)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun ScreenContent(modifier: Modifier) {
|
||||
AppSettingsScreen(
|
||||
|
|
@ -49,6 +43,11 @@ internal class AppSettingsFragment : ComposeFragment(), StoreSubscriber<DetailsS
|
|||
)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
viewModel.checkBiometricsStatus(lifecycleScope)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
store.subscribe(this) { state ->
|
||||
|
|
@ -58,11 +57,6 @@ internal class AppSettingsFragment : ComposeFragment(), StoreSubscriber<DetailsS
|
|||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
viewModel.refreshBiometricsStatus(lifecycleScope)
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
store.unsubscribe(this)
|
||||
|
|
|
|||
|
|
@ -55,21 +55,7 @@ internal class AppSettingsViewModel(
|
|||
}
|
||||
|
||||
fun checkBiometricsStatus(lifecycleScope: LifecycleCoroutineScope) {
|
||||
store.dispatch(
|
||||
DetailsAction.AppSettings.CheckBiometricsStatus(
|
||||
awaitStatusChange = false,
|
||||
lifecycleCoroutineScope = lifecycleScope,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun refreshBiometricsStatus(lifecycleScope: LifecycleCoroutineScope) {
|
||||
store.dispatch(
|
||||
DetailsAction.AppSettings.CheckBiometricsStatus(
|
||||
awaitStatusChange = true,
|
||||
lifecycleCoroutineScope = lifecycleScope,
|
||||
),
|
||||
)
|
||||
store.dispatch(DetailsAction.AppSettings.CheckBiometricsStatus(lifecycleScope))
|
||||
}
|
||||
|
||||
private fun buildItems(state: AppSettingsState): ImmutableList<AppSettingsScreenState.Item> {
|
||||
|
|
|
|||
|
|
@ -140,10 +140,7 @@ internal class SaveWalletMiddleware {
|
|||
store.dispatchWithMain(SaveWalletAction.Save.Error(TangemSdkError.ExceptionError(error)))
|
||||
return
|
||||
}
|
||||
val manager = UserWalletsListManager.provideBiometricImplementation(
|
||||
context = context,
|
||||
tangemSdkManager = tangemSdkManager,
|
||||
)
|
||||
val manager = UserWalletsListManager.provideBiometricImplementation(context)
|
||||
|
||||
store.dispatchWithMain(GlobalAction.UpdateUserWalletsListManager(manager))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import com.tangem.domain.txhistory.repository.TxHistoryRepository
|
|||
|
||||
class GetTxHistoryItemsCountUseCase(private val repository: TxHistoryRepository) {
|
||||
|
||||
// FIXME: Provide UserWalletId
|
||||
suspend operator fun invoke(network: Network): Either<TxHistoryStateError, Int> {
|
||||
return either {
|
||||
catch(
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ private const val DEFAULT_PAGE_SIZE = 50
|
|||
|
||||
class GetTxHistoryItemsUseCase(private val repository: TxHistoryRepository) {
|
||||
|
||||
// FIXME: Provide UserWalletId
|
||||
operator fun invoke(
|
||||
currency: CryptoCurrency,
|
||||
pageSize: Int = DEFAULT_PAGE_SIZE,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue