Updated on 2026-08-14
This commit is contained in:
parent
c6578fcad5
commit
6e642be3ca
19 changed files with 478 additions and 50 deletions
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.tap.domain.userWalletList
|
||||
|
||||
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManagerFeatureToggles
|
||||
|
||||
internal class DefaultUserWalletsListManagerFeatureToggles(
|
||||
private val featureTogglesManager: FeatureTogglesManager,
|
||||
) : UserWalletsListManagerFeatureToggles {
|
||||
|
||||
override val isGeneralManagerEnabled: Boolean
|
||||
get() = featureTogglesManager.isFeatureEnabled(name = "GENERAL_USER_WALLETS_LIST_MANAGER_ENABLED")
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package com.tangem.tap.domain.userWalletList.di
|
||||
|
||||
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManagerFeatureToggles
|
||||
import com.tangem.tap.domain.userWalletList.DefaultUserWalletsListManagerFeatureToggles
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal object UserWalletsListManagerFeatureTogglesModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideUserWalletsListManagerFeatureToggles(
|
||||
featureTogglesManager: FeatureTogglesManager,
|
||||
): UserWalletsListManagerFeatureToggles {
|
||||
return DefaultUserWalletsListManagerFeatureToggles(featureTogglesManager)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
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.authentication.AuthenticatedStorage
|
||||
import com.tangem.common.json.TangemSdkAdapter
|
||||
import com.tangem.common.services.secure.SecureStorage
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.sdk.storage.AndroidSecureStorage
|
||||
import com.tangem.sdk.storage.createEncryptedSharedPreferences
|
||||
import com.tangem.tap.domain.userWalletList.implementation.BiometricUserWalletsListManager
|
||||
import com.tangem.tap.domain.userWalletList.implementation.GeneralUserWalletsListManager
|
||||
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
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal object UserWalletsListManagerModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGeneralUserWalletsListManager(
|
||||
@ApplicationContext applicationContext: Context,
|
||||
appPreferencesStore: AppPreferencesStore,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): UserWalletsListManager {
|
||||
return GeneralUserWalletsListManager(
|
||||
runtimeUserWalletsListManager = RuntimeUserWalletsListManager(),
|
||||
biometricUserWalletsListManager = createBiometricUserWalletsListManager(applicationContext),
|
||||
appPreferencesStore = appPreferencesStore,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createBiometricUserWalletsListManager(applicationContext: Context): UserWalletsListManager {
|
||||
val moshi = Moshi.Builder()
|
||||
.add(WalletDerivedKeysMapAdapter())
|
||||
.add(ScanResponseDerivedKeysMapAdapter())
|
||||
.add(ByteArrayKeyAdapter())
|
||||
.add(ExtendedPublicKeysMapAdapter())
|
||||
.add(CardBackupStatusAdapter())
|
||||
.add(DerivationPathAdapterWithMigration())
|
||||
.add(TangemSdkAdapter.DateAdapter())
|
||||
.add(TangemSdkAdapter.DerivationNodeAdapter())
|
||||
.add(TangemSdkAdapter.FirmwareVersionAdapter()) // For PrimaryCard model
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.build()
|
||||
|
||||
val secureStorage = AndroidSecureStorage(
|
||||
preferences = SecureStorage.createEncryptedSharedPreferences(
|
||||
context = applicationContext,
|
||||
storageName = USER_WALLETS_STORAGE_NAME,
|
||||
),
|
||||
)
|
||||
|
||||
val authenticatedStorage = AuthenticatedStorage(
|
||||
secureStorage = UserWalletsKeysStoreDecorator(
|
||||
featureStorage = secureStorage,
|
||||
cardSdkStorageProvider = Provider { tangemSdkManager.secureStorage },
|
||||
),
|
||||
keystoreManager = DelegatedKeystoreManager(
|
||||
keystoreManagerProvider = Provider { tangemSdkManager.keystoreManager },
|
||||
),
|
||||
)
|
||||
|
||||
val keysRepository = BiometricUserWalletsKeysRepository(
|
||||
moshi = moshi,
|
||||
secureStorage = secureStorage,
|
||||
authenticatedStorage = authenticatedStorage,
|
||||
)
|
||||
|
||||
val publicInformationRepository = DefaultUserWalletsPublicInformationRepository(
|
||||
moshi = moshi,
|
||||
secureStorage = secureStorage,
|
||||
)
|
||||
|
||||
val sensitiveInformationRepository = DefaultUserWalletsSensitiveInformationRepository(
|
||||
moshi = moshi,
|
||||
secureStorage = secureStorage,
|
||||
)
|
||||
|
||||
val selectedUserWalletRepository = DefaultSelectedUserWalletRepository(secureStorage = secureStorage)
|
||||
|
||||
return BiometricUserWalletsListManager(
|
||||
keysRepository = keysRepository,
|
||||
publicInformationRepository = publicInformationRepository,
|
||||
sensitiveInformationRepository = sensitiveInformationRepository,
|
||||
selectedUserWalletRepository = selectedUserWalletRepository,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -21,7 +21,7 @@ import com.tangem.tap.domain.userWalletList.utils.json.*
|
|||
import com.tangem.tap.tangemSdkManager
|
||||
import com.tangem.utils.Provider
|
||||
|
||||
private const val USER_WALLETS_STORAGE_NAME = "user_wallets_storage"
|
||||
internal const val USER_WALLETS_STORAGE_NAME = "user_wallets_storage"
|
||||
|
||||
fun UserWalletsListManager.Companion.provideBiometricImplementation(
|
||||
applicationContext: Context,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,146 @@
|
|||
package com.tangem.tap.domain.userWalletList.implementation
|
||||
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.preferences.PreferencesKeys
|
||||
import com.tangem.datasource.local.preferences.utils.get
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.*
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* General implementation of [UserWalletsListManager] that helps to switch between Runtime and Biometric
|
||||
* implementations.
|
||||
*
|
||||
* @property runtimeUserWalletsListManager runtime user wallets list manager
|
||||
* @property biometricUserWalletsListManager biometric user wallets list manager
|
||||
* @property appPreferencesStore app preferences store
|
||||
* @property dispatchers coroutine dispatcher provider
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
internal class GeneralUserWalletsListManager(
|
||||
private val runtimeUserWalletsListManager: UserWalletsListManager,
|
||||
private val biometricUserWalletsListManager: UserWalletsListManager,
|
||||
private val appPreferencesStore: AppPreferencesStore,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : UserWalletsListManager.Lockable {
|
||||
|
||||
private val applicationScope = CoroutineScope(dispatchers.io)
|
||||
private val implementation = MutableStateFlow(runtimeUserWalletsListManager)
|
||||
|
||||
init {
|
||||
subscribeOnCurrentManager()
|
||||
}
|
||||
|
||||
override val userWallets: Flow<List<UserWallet>>
|
||||
get() = implementation.flatMapLatest { it.userWallets }
|
||||
|
||||
override val selectedUserWallet: Flow<UserWallet>
|
||||
get() = implementation.flatMapLatest { it.selectedUserWallet }
|
||||
|
||||
override val selectedUserWalletSync: UserWallet?
|
||||
get() = implementation.value.selectedUserWalletSync
|
||||
|
||||
override val hasUserWallets: Boolean
|
||||
get() = implementation.value.hasUserWallets
|
||||
|
||||
override val walletsCount: Int
|
||||
get() = implementation.value.walletsCount
|
||||
|
||||
override val isLocked: Flow<Boolean>
|
||||
get() = implementation.flatMapLatest {
|
||||
if (it is UserWalletsListManager.Lockable) {
|
||||
it.isLocked
|
||||
} else {
|
||||
error("RuntimeUserWalletsListManager is not lockable")
|
||||
}
|
||||
}
|
||||
|
||||
override val isLockedSync: Boolean
|
||||
get() {
|
||||
val implementation = implementation.value
|
||||
return if (implementation is UserWalletsListManager.Lockable) {
|
||||
implementation.isLockedSync
|
||||
} else {
|
||||
error("RuntimeUserWalletsListManager is not lockable")
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun select(userWalletId: UserWalletId): CompletionResult<UserWallet> {
|
||||
return implementation.value.select(userWalletId)
|
||||
}
|
||||
|
||||
override suspend fun save(userWallet: UserWallet, canOverride: Boolean): CompletionResult<Unit> {
|
||||
return implementation.value.save(userWallet, canOverride)
|
||||
}
|
||||
|
||||
override suspend fun update(
|
||||
userWalletId: UserWalletId,
|
||||
update: suspend (UserWallet) -> UserWallet,
|
||||
): CompletionResult<UserWallet> {
|
||||
return implementation.value.update(userWalletId, update)
|
||||
}
|
||||
|
||||
override suspend fun delete(userWalletIds: List<UserWalletId>): CompletionResult<Unit> {
|
||||
return implementation.value.delete(userWalletIds)
|
||||
}
|
||||
|
||||
override suspend fun clear(): CompletionResult<Unit> {
|
||||
return implementation.value.clear()
|
||||
}
|
||||
|
||||
override suspend fun get(userWalletId: UserWalletId): CompletionResult<UserWallet> {
|
||||
return implementation.value.get(userWalletId)
|
||||
}
|
||||
|
||||
override suspend fun unlock(throwIfNotAllWalletsUnlocked: Boolean): CompletionResult<UserWallet> {
|
||||
val implementation = implementation.value
|
||||
return if (implementation is UserWalletsListManager.Lockable) {
|
||||
implementation.unlock()
|
||||
} else {
|
||||
error("RuntimeUserWalletsListManager is not lockable")
|
||||
}
|
||||
}
|
||||
|
||||
override fun lock() {
|
||||
val implementation = implementation.value
|
||||
return if (implementation is UserWalletsListManager.Lockable) {
|
||||
implementation.lock()
|
||||
} else {
|
||||
error("RuntimeUserWalletsListManager is not lockable")
|
||||
}
|
||||
}
|
||||
|
||||
private fun subscribeOnCurrentManager() {
|
||||
appPreferencesStore.get(key = PreferencesKeys.SAVE_USER_WALLETS_KEY, default = false)
|
||||
.distinctUntilChanged()
|
||||
.onEach { shouldSaveUserWallets ->
|
||||
val manager = if (shouldSaveUserWallets) {
|
||||
biometricUserWalletsListManager.copyFrom(runtimeUserWalletsListManager)
|
||||
} else {
|
||||
runtimeUserWalletsListManager.copyFrom(biometricUserWalletsListManager)
|
||||
}
|
||||
|
||||
Timber.d("Switch to ${manager::class.java.simpleName}")
|
||||
|
||||
implementation.value = manager
|
||||
}
|
||||
.flowOn(dispatchers.io)
|
||||
.launchIn(applicationScope)
|
||||
}
|
||||
|
||||
/** Copy data from [old] manager and clean it */
|
||||
private suspend fun UserWalletsListManager.copyFrom(old: UserWalletsListManager): UserWalletsListManager {
|
||||
old.selectedUserWalletSync?.let { this.save(it) }
|
||||
old.clear()
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue