diff --git a/app/src/main/java/com/tangem/tap/di/hot/TangemHotSdkModule.kt b/app/src/main/java/com/tangem/tap/di/hot/TangemHotSdkModule.kt index 88389fafb6..c21b55928b 100644 --- a/app/src/main/java/com/tangem/tap/di/hot/TangemHotSdkModule.kt +++ b/app/src/main/java/com/tangem/tap/di/hot/TangemHotSdkModule.kt @@ -1,23 +1,18 @@ package com.tangem.tap.di.hot -import android.content.Context -import androidx.appcompat.app.AppCompatActivity import com.tangem.hot.sdk.TangemHotSdk -import com.tangem.hot.sdk.android.create +import com.tangem.tap.features.hot.TangemHotSDKProxy +import dagger.Binds import dagger.Module -import dagger.Provides import dagger.hilt.InstallIn -import dagger.hilt.android.components.ActivityComponent -import dagger.hilt.android.qualifiers.ActivityContext -import dagger.hilt.android.scopes.ActivityScoped +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton @Module -@InstallIn(ActivityComponent::class) -internal object TangemHotSdkModule { +@InstallIn(SingletonComponent::class) +internal interface TangemHotSdkModule { - @Provides - @ActivityScoped - fun provideRootAppComponentContext(@ActivityContext context: Context): TangemHotSdk { - return TangemHotSdk.create(context as AppCompatActivity) - } + @Binds + @Singleton + fun bindTangemHotSdk(proxy: TangemHotSDKProxy): TangemHotSdk } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/hot/TangemHotSDKProxy.kt b/app/src/main/java/com/tangem/tap/features/hot/TangemHotSDKProxy.kt new file mode 100644 index 0000000000..9cc8db4191 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/hot/TangemHotSDKProxy.kt @@ -0,0 +1,53 @@ +package com.tangem.tap.features.hot + +import com.tangem.crypto.bip39.Mnemonic +import com.tangem.hot.sdk.TangemHotSdk +import com.tangem.hot.sdk.model.* +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.withTimeout +import javax.inject.Inject +import javax.inject.Singleton + +/** + * Proxy for [TangemHotSdk] to allow lazy initialization and provide a way to access the SDK state from domain layer. + * SDK is initialized in the [com.tangem.tap.routing.component.RoutingComponent] and can be accessed through this proxy. + * Be aware that the SDK is initialized on activity creation, so it may not be available immediately. + */ +@Singleton +class TangemHotSDKProxy @Inject constructor() : TangemHotSdk { + + val sdkState = MutableStateFlow(null) + + override suspend fun importWallet(mnemonic: Mnemonic, passphrase: CharArray?, auth: HotAuth): HotWalletId = + callSdk { importWallet(mnemonic, passphrase, auth) } + + override suspend fun generateWallet(auth: HotAuth, mnemonicType: MnemonicType): HotWalletId = + callSdk { generateWallet(auth, mnemonicType) } + + override suspend fun exportMnemonic(unlockHotWallet: UnlockHotWallet): SeedPhrasePrivateInfo = + callSdk { exportMnemonic(unlockHotWallet) } + + override suspend fun exportBackup(unlockHotWallet: UnlockHotWallet): ByteArray = + callSdk { exportBackup(unlockHotWallet) } + + override suspend fun delete(id: HotWalletId) = callSdk { delete(id) } + + override suspend fun changeAuth(unlockHotWallet: UnlockHotWallet, auth: HotAuth): HotWalletId = + callSdk { changeAuth(unlockHotWallet, auth) } + + override suspend fun derivePublicKey( + unlockHotWallet: UnlockHotWallet, + request: DeriveWalletRequest, + ): DerivedPublicKeyResponse = callSdk { derivePublicKey(unlockHotWallet, request) } + + override suspend fun signHashes(unlockHotWallet: UnlockHotWallet, dataToSign: List): List = + callSdk { signHashes(unlockHotWallet, dataToSign) } + + private suspend fun callSdk(block: suspend TangemHotSdk.() -> T): T { + return withTimeout(timeMillis = 1000) { + sdkState.filterNotNull().first() + }.block() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/routing/component/impl/DefaultRoutingComponent.kt b/app/src/main/java/com/tangem/tap/routing/component/impl/DefaultRoutingComponent.kt index 64d10c0193..909be69b49 100644 --- a/app/src/main/java/com/tangem/tap/routing/component/impl/DefaultRoutingComponent.kt +++ b/app/src/main/java/com/tangem/tap/routing/component/impl/DefaultRoutingComponent.kt @@ -6,6 +6,7 @@ import com.arkivanov.decompose.router.stack.ChildStack import com.arkivanov.decompose.router.stack.childStack import com.arkivanov.decompose.value.Value import com.arkivanov.decompose.value.subscribe +import com.arkivanov.essenty.lifecycle.subscribe import com.google.android.material.snackbar.Snackbar import com.tangem.common.routing.AppRoute import com.tangem.core.decompose.context.AppComponentContext @@ -16,7 +17,10 @@ import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.message.SnackbarMessage import com.tangem.features.walletconnect.components.WcRoutingComponent +import com.tangem.hot.sdk.TangemHotSdk +import com.tangem.hot.sdk.android.create import com.tangem.tap.common.SnackbarHandler +import com.tangem.tap.features.hot.TangemHotSDKProxy import com.tangem.tap.routing.RootContent import com.tangem.tap.routing.component.RoutingComponent import com.tangem.tap.routing.component.RoutingComponent.Child @@ -36,6 +40,7 @@ internal class DefaultRoutingComponent @AssistedInject constructor( private val uiDependencies: UiDependencies, private val wcRoutingComponentFactory: WcRoutingComponent.Factory, private val deeplinkFactory: DeepLinkFactory, + private val tangemHotSDKProxy: TangemHotSDKProxy, ) : RoutingComponent, AppComponentContext by context, SnackbarHandler { @@ -70,6 +75,8 @@ internal class DefaultRoutingComponent @AssistedInject constructor( appRouterConfig.stack = stackItems } } + + configureHotSdk() } @Composable @@ -120,6 +127,17 @@ internal class DefaultRoutingComponent @AssistedInject constructor( initialStack } + private fun configureHotSdk() { + lifecycle.subscribe( + onCreate = { + tangemHotSDKProxy.sdkState.value = TangemHotSdk.create(activity) + }, + onDestroy = { + tangemHotSDKProxy.sdkState.value = null + }, + ) + } + @AssistedFactory interface Factory : RoutingComponent.Factory { override fun create(context: AppComponentContext, initialStack: List?): DefaultRoutingComponent diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/createmobilewallet/CreateMobileWalletModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/createmobilewallet/CreateMobileWalletModel.kt index 6daba0d509..c285fd8e48 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/createmobilewallet/CreateMobileWalletModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/createmobilewallet/CreateMobileWalletModel.kt @@ -2,9 +2,11 @@ package com.tangem.features.hotwallet.createmobilewallet import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model -import com.tangem.core.decompose.model.ParamsContainer import com.tangem.core.decompose.navigation.Router import com.tangem.features.hotwallet.createmobilewallet.entity.CreateMobileWalletUM +import com.tangem.hot.sdk.TangemHotSdk +import com.tangem.hot.sdk.model.HotAuth +import com.tangem.hot.sdk.model.MnemonicType import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -15,11 +17,9 @@ import javax.inject.Inject internal class CreateMobileWalletModel @Inject constructor( override val dispatchers: CoroutineDispatcherProvider, private val router: Router, - private val paramsContainer: ParamsContainer, + private val tangemHotSdk: TangemHotSdk, ) : Model() { - private val params = paramsContainer.require() - internal val uiState: StateFlow field = MutableStateFlow( CreateMobileWalletUM( @@ -30,9 +30,8 @@ internal class CreateMobileWalletModel @Inject constructor( private fun onCreateClick() { modelScope.launch { - val tangemHotSdk = params.tangemHotSdk.instanceAccess.get() ?: return@launch + tangemHotSdk.generateWallet(HotAuth.NoAuth, mnemonicType = MnemonicType.Words12) // TODO - // val hotWalletId = tangemHotSdk.generateWallet(HotAuth.NoAuth, mnemonicType = MnemonicType.Words12) // val hotUserWalletBuilder = hotUserWalletBuilderFactory.create(hotWalletId, tangemHotSdk) // saveUserWalletUseCase( // hotUserWalletBuilder.build(), diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/createmobilewallet/DefaultCreateMobileWalletComponent.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/createmobilewallet/DefaultCreateMobileWalletComponent.kt index c5ab0ce1ad..bf5fd61424 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/createmobilewallet/DefaultCreateMobileWalletComponent.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/createmobilewallet/DefaultCreateMobileWalletComponent.kt @@ -6,11 +6,8 @@ import androidx.compose.ui.Modifier import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.model.getOrCreateModel -import com.tangem.core.decompose.utils.ActivityInstanceHolder -import com.tangem.core.decompose.utils.getOrCreateActivityInstanceHolder import com.tangem.features.hotwallet.CreateMobileWalletComponent import com.tangem.features.hotwallet.createmobilewallet.ui.CreateMobileWalletContent -import com.tangem.hot.sdk.TangemHotSdk import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -19,15 +16,9 @@ import dagger.assisted.AssistedInject internal class DefaultCreateMobileWalletComponent @AssistedInject constructor( @Assisted private val context: AppComponentContext, @Assisted private val params: Unit, - private val tangemHotSdk: TangemHotSdk, ) : CreateMobileWalletComponent, AppComponentContext by context { - private val modelParams: Params = - Params( - tangemHotSdk = getOrCreateActivityInstanceHolder { tangemHotSdk }, - ) - - private val model: CreateMobileWalletModel = getOrCreateModel(modelParams) + private val model: CreateMobileWalletModel = getOrCreateModel(params) @Composable override fun Content(modifier: Modifier) { @@ -38,10 +29,6 @@ internal class DefaultCreateMobileWalletComponent @AssistedInject constructor( ) } - class Params( - val tangemHotSdk: ActivityInstanceHolder, - ) - @AssistedFactory interface Factory : CreateMobileWalletComponent.Factory { override fun create(context: AppComponentContext, params: Unit): DefaultCreateMobileWalletComponent