Updated on 2026-08-14

This commit is contained in:
Tangem 2025-12-08 16:51:34 +03:00
parent 133c89dd7e
commit dffa8b2658
12 changed files with 109 additions and 1 deletions

View file

@ -1,6 +1,7 @@
package com.tangem.tap.di.domain
import com.tangem.domain.hotwallet.GetAccessCodeSkippedUseCase
import com.tangem.domain.hotwallet.IsHotWalletCreationSupported
import com.tangem.domain.hotwallet.SetAccessCodeSkippedUseCase
import com.tangem.domain.hotwallet.repository.HotWalletRepository
import dagger.Module
@ -24,4 +25,12 @@ internal object HotWalletDomainModule {
fun provideSetAccessCodeSkippedUseCase(hotWalletRepository: HotWalletRepository): SetAccessCodeSkippedUseCase {
return SetAccessCodeSkippedUseCase(hotWalletRepository)
}
@Provides
@Singleton
fun provideIsWalletCreationSupportedUseCase(
hotWalletRepository: HotWalletRepository,
): IsHotWalletCreationSupported {
return IsHotWalletCreationSupported(hotWalletRepository)
}
}

View file

@ -39,6 +39,30 @@ object Dialogs {
)
}
/**
* Hot wallet creation not supported dialog
*
* @param leastSupportedVersion least supported OS version name ex. "Android 10"
* @param onDismiss lambda be invoked when dialog is dismissed
*/
fun hotWalletCreationNotSupportedDialog(leastSupportedVersion: String, onDismiss: () -> Unit = {}): DialogMessage {
return DialogMessage(
title = resourceReference(
id = R.string.mobile_wallet_requires_min_os_warning_title,
formatArgs = wrappedList(leastSupportedVersion),
),
message = resourceReference(
id = R.string.mobile_wallet_requires_min_os_warning_body,
formatArgs = wrappedList(leastSupportedVersion),
),
firstAction = EventMessageAction(
title = resourceReference(R.string.common_got_it),
onClick = {},
),
onDismissRequest = onDismiss,
)
}
/**
* Universal error dialog
*/

View file

@ -1,5 +1,7 @@
package com.tangem.data.hotwallet
import android.os.Build
import androidx.annotation.ChecksSdkIntAtLeast
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.datasource.local.preferences.PreferencesKeys
import com.tangem.datasource.local.preferences.utils.getObjectMap
@ -12,6 +14,13 @@ internal class DefaultHotWalletRepository(
private val appPreferencesStore: AppPreferencesStore,
) : HotWalletRepository {
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.Q)
override fun isWalletCreationSupported(): Boolean {
return BuildConfig.DEBUG || Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
}
override fun getLeastSupportedAndroidVersionName(): String = "Android 10"
override fun accessCodeSkipped(userWalletId: UserWalletId): Flow<Boolean> = appPreferencesStore
.getObjectMap<Boolean>(PreferencesKeys.ACCESS_CODE_SKIPPED_STATES_KEY)
.map { it[userWalletId.stringValue] == true }

View file

@ -0,0 +1,9 @@
package com.tangem.domain.hotwallet
import com.tangem.domain.hotwallet.repository.HotWalletRepository
class IsHotWalletCreationSupported(private val hotWalletRepository: HotWalletRepository) {
operator fun invoke(): Boolean = hotWalletRepository.isWalletCreationSupported()
fun getLeastVersionName(): String = hotWalletRepository.getLeastSupportedAndroidVersionName()
}

View file

@ -5,6 +5,10 @@ import kotlinx.coroutines.flow.Flow
interface HotWalletRepository {
fun isWalletCreationSupported(): Boolean
fun getLeastSupportedAndroidVersionName(): String
fun accessCodeSkipped(userWalletId: UserWalletId): Flow<Boolean>
suspend fun setAccessCodeSkipped(userWalletId: UserWalletId, skipped: Boolean)

View file

@ -32,6 +32,7 @@ dependencies {
implementation(projects.domain.wallets.models)
implementation(projects.domain.notifications.models)
implementation(projects.domain.demo.models)
implementation(projects.domain.hotWallet)
// endregion
// region Tangem libraries

View file

@ -3,6 +3,7 @@ package com.tangem.domain.wallets.builder
import com.tangem.blockchain.blockchains.cardano.CardanoUtils
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.derivation.DerivationStyle
import com.tangem.domain.hotwallet.IsHotWalletCreationSupported
import com.tangem.domain.models.MobileWallet
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase
@ -22,9 +23,12 @@ class HotUserWalletBuilder @AssistedInject constructor(
private val hotSdk: TangemHotSdk,
private val generateWalletNameUseCase: GenerateWalletNameUseCase,
private val dispatcherProvider: CoroutineDispatcherProvider,
private val isHotWalletCreationSupported: IsHotWalletCreationSupported,
) {
suspend fun build(): UserWallet.Hot = withContext(dispatcherProvider.default) {
checkHotWalletCreationSupported()
val allNetworks = Blockchain.entries.filter { it.isTestnet().not() }
val curves = allNetworks.map { it.getSupportedCurves() }.flatten().toSet()
val requests = curves.sortedBy { it.ordinal }.map { curve ->
@ -73,6 +77,12 @@ class HotUserWalletBuilder @AssistedInject constructor(
)
}
fun checkHotWalletCreationSupported() {
require(isHotWalletCreationSupported()) {
"Hot wallet creation is supported only from ${isHotWalletCreationSupported.getLeastVersionName()}"
}
}
@AssistedFactory
interface Factory {
fun create(hotWalletId: HotWalletId): HotUserWalletBuilder

View file

@ -23,6 +23,7 @@ dependencies {
implementation(projects.domain.settings)
implementation(projects.domain.wallets)
implementation(projects.domain.models)
implementation(projects.domain.hotWallet)
/** Core modules */
implementation(projects.core.configToggles)

View file

@ -7,10 +7,13 @@ import com.tangem.core.analytics.models.Basic
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.components.label.entity.LabelStyle
import com.tangem.core.ui.components.label.entity.LabelUM
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.message.dialog.Dialogs.hotWalletCreationNotSupportedDialog
import com.tangem.domain.hotwallet.IsHotWalletCreationSupported
import com.tangem.domain.wallets.usecase.GenerateBuyTangemCardLinkUseCase
import com.tangem.features.createwalletselection.entity.CreateWalletSelectionUM
import com.tangem.features.createwalletselection.impl.R
@ -31,6 +34,8 @@ internal class CreateWalletSelectionModel @Inject constructor(
private val analyticsEventHandler: AnalyticsEventHandler,
private val generateBuyTangemCardLinkUseCase: GenerateBuyTangemCardLinkUseCase,
private val urlOpener: UrlOpener,
private val isHotWalletCreationSupported: IsHotWalletCreationSupported,
private val uiMessageSender: UiMessageSender,
) : Model() {
internal val uiState: StateFlow<CreateWalletSelectionUM>
@ -93,6 +98,13 @@ internal class CreateWalletSelectionModel @Inject constructor(
}
private fun onMobileWalletClick() {
if (!isHotWalletCreationSupported()) {
uiMessageSender.send(
hotWalletCreationNotSupportedDialog(isHotWalletCreationSupported.getLeastVersionName()),
)
return
}
router.push(AppRoute.CreateMobileWallet)
}

View file

@ -20,6 +20,7 @@ dependencies {
implementation(projects.domain.settings)
implementation(projects.domain.wallets)
implementation(projects.domain.models)
implementation(projects.domain.hotWallet)
/** Core modules */
implementation(projects.core.configToggles)

View file

@ -17,11 +17,13 @@ import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.R
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.message.DialogMessage
import com.tangem.core.ui.message.dialog.Dialogs.hotWalletCreationNotSupportedDialog
import com.tangem.domain.card.ScanCardProcessor
import com.tangem.domain.card.analytics.IntroductionProcess
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.common.wallets.error.SaveWalletError
import com.tangem.domain.hotwallet.IsHotWalletCreationSupported
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.settings.repositories.SettingsRepository
import com.tangem.domain.wallets.builder.ColdUserWalletBuilder
@ -52,6 +54,7 @@ internal class CreateWalletStartModel @Inject constructor(
private val appRouter: AppRouter,
private val coldUserWalletBuilderFactory: ColdUserWalletBuilder.Factory,
private val saveWalletUseCase: SaveWalletUseCase,
private val isHotWalletCreationSupported: IsHotWalletCreationSupported,
private val userWalletsListRepository: UserWalletsListRepository,
@GlobalUiMessageSender private val uiMessageSender: UiMessageSender,
private val generateBuyTangemCardLinkUseCase: GenerateBuyTangemCardLinkUseCase,
@ -131,6 +134,13 @@ internal class CreateWalletStartModel @Inject constructor(
}
private fun onStartWithMobileWalletClick() {
if (!isHotWalletCreationSupported()) {
uiMessageSender.send(
hotWalletCreationNotSupportedDialog(isHotWalletCreationSupported.getLeastVersionName()),
)
return
}
router.push(AppRoute.CreateMobileWallet)
}

View file

@ -5,6 +5,9 @@ import com.tangem.core.analytics.utils.TrackingContextProxy
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.message.dialog.Dialogs.hotWalletCreationNotSupportedDialog
import com.tangem.domain.hotwallet.IsHotWalletCreationSupported
import com.tangem.domain.wallets.builder.HotUserWalletBuilder
import com.tangem.domain.wallets.usecase.SaveWalletUseCase
import com.tangem.domain.wallets.usecase.SyncWalletWithRemoteUseCase
@ -32,6 +35,8 @@ internal class CreateMobileWalletModel @Inject constructor(
private val router: Router,
private val tangemHotSdk: TangemHotSdk,
private val trackingContextProxy: TrackingContextProxy,
private val isHotWalletCreationSupported: IsHotWalletCreationSupported,
private val uiMessageSender: UiMessageSender,
) : Model() {
internal val uiState: StateFlow<CreateMobileWalletUM>
@ -54,10 +59,13 @@ internal class CreateMobileWalletModel @Inject constructor(
}
private fun onImportClick() {
checkHotWalletCreationSupported(notSupported = { return })
router.push(AppRoute.AddExistingWallet)
}
private fun onCreateClick() {
checkHotWalletCreationSupported(notSupported = { return })
modelScope.launch {
uiState.update {
it.copy(createButtonLoading = true)
@ -70,9 +78,10 @@ internal class CreateMobileWalletModel @Inject constructor(
saveUserWalletUseCase(userWallet)
launch(NonCancellable) {
launch(dispatchers.main + NonCancellable) {
syncWalletWithRemoteUseCase(userWalletId = userWallet.walletId)
}
router.replaceAll(AppRoute.Wallet)
}.onFailure { throwable ->
Timber.e(throwable)
@ -81,4 +90,13 @@ internal class CreateMobileWalletModel @Inject constructor(
}
}
}
private inline fun checkHotWalletCreationSupported(notSupported: () -> Unit) {
if (!isHotWalletCreationSupported()) {
uiMessageSender.send(
hotWalletCreationNotSupportedDialog(isHotWalletCreationSupported.getLeastVersionName()),
)
notSupported()
}
}
}