diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index 9f1970f662..2b66b30ca7 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -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() diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index 6992e82d7e..75211c56c5 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -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)) + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/di/UserWalletsListManagerProvider.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/di/UserWalletsListManagerProvider.kt index 2657240db8..719491a036 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/di/UserWalletsListManagerProvider.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/di/UserWalletsListManagerProvider.kt @@ -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, diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/DelegatedKeystoreManager.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/DelegatedKeystoreManager.kt new file mode 100644 index 0000000000..64e0f9a1ce --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/DelegatedKeystoreManager.kt @@ -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 { + + override suspend fun authenticateAndGetKey(keyAlias: String): SecretKey? { + return keystoreManagerProvider().authenticateAndGetKey(keyAlias) + } + + override suspend fun storeKey(keyAlias: String, key: SecretKey) { + keystoreManagerProvider().storeKey(keyAlias, key) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletsKeysStoreDecorator.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletsKeysStoreDecorator.kt index 662da27e35..49e87a395a 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletsKeysStoreDecorator.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletsKeysStoreDecorator.kt @@ -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 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) } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt index 9dd0c59cef..0190c0aa97 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsAction.kt @@ -74,8 +74,7 @@ sealed class DetailsAction : Action { } data class CheckBiometricsStatus( - val awaitStatusChange: Boolean, - val lifecycleCoroutineScope: LifecycleCoroutineScope, + val lifecycleScope: LifecycleCoroutineScope, ) : AppSettings() object EnrollBiometrics : AppSettings() diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt index 1c13b7ed80..bdf4867874 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt @@ -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) } } diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsFragment.kt index 974c0937d9..122e9437d1 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/appsettings/AppSettingsFragment.kt @@ -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 @@ -58,11 +57,6 @@ internal class AppSettingsFragment : ComposeFragment(), StoreSubscriber { diff --git a/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt index 5305d35dfb..29e8ad6a02 100644 --- a/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt @@ -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)) } diff --git a/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsCountUseCase.kt b/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsCountUseCase.kt index de458c7a51..6ca59cb013 100644 --- a/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsCountUseCase.kt +++ b/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsCountUseCase.kt @@ -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 { return either { catch( diff --git a/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsUseCase.kt b/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsUseCase.kt index 39865a24f5..0555f17187 100644 --- a/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsUseCase.kt +++ b/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsUseCase.kt @@ -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,