diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 36145b0415..65f7822aff 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -188,6 +188,8 @@ dependencies {
implementation(projects.features.stories.impl)
implementation(projects.features.txhistory.api)
implementation(projects.features.txhistory.impl)
+ implementation(projects.features.askBiometry.api)
+ implementation(projects.features.askBiometry.impl)
implementation(projects.features.nft.api)
implementation(projects.features.nft.impl)
diff --git a/core/config-toggles/src/main/assets/configs/feature_toggles_config.json b/core/config-toggles/src/main/assets/configs/feature_toggles_config.json
index a9a8c23e01..a7304f434c 100644
--- a/core/config-toggles/src/main/assets/configs/feature_toggles_config.json
+++ b/core/config-toggles/src/main/assets/configs/feature_toggles_config.json
@@ -58,5 +58,9 @@
{
"name": "TX_HISTORY_REFACTORING_ENABLED",
"version": "undefined"
+ },
+ {
+ "name": "ASK_BIOMETRY_REFACTORING_ENABLED",
+ "version": "undefined"
}
]
diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml
index 6edea905ea..003034b671 100644
--- a/core/res/src/main/res/values-de/strings.xml
+++ b/core/res/src/main/res/values-de/strings.xml
@@ -241,6 +241,7 @@
Dieser Mechanismus schützt vor Annäherungsangriffen auf eine Karte oder einen Ring. Er erzwingt eine Verzögerung zwischen dem Empfang und der Ausführung eines Befehls.
Passcode
Bevor du einen Befehl ausführst, der eine Änderung des Kartenstatus zur Folge hat, musst du den Passcode eingeben.
+ NFT
Empfehlungsprogramm
Flippe den Bildschirm deines Geräts nach unten, um Salden schnell ein- und auszublenden
%s Hasch
@@ -536,6 +537,7 @@
Möchtest du den Aktivierungsprozess abbrechen?
Erste Schritte
Für die Karte oder Ring, die du hinzufügen möchtest, wurde bereits eine andere Wallets erstellt. Wenn du Guthaben auf dieser Wallets hast, hebe es bitte ab, setze diese Karte oder Ring zurück und füge sie als Backup hinzu.
+ Sicher deine Wallet
Backups anlegen
Lese mehr über die Seed-Phrase
@@ -668,6 +670,7 @@
Nutzung biometrischer Daten zulassen
Bei Interaktionen mit deiner Wallet werden anstelle des Zugangscodes biometrische Daten abgefragt
Zugangscode
+ Nicht zulassen
Es sieht so aus, als ob du die biometrische Authentifizierung deaktiviert hast. Diese ist notwendig, um Wallets zu speichern
Biometrische Autorisierung aktivieren
Möchtest du Biometrie nutzen?
diff --git a/core/res/src/main/res/values-es/strings.xml b/core/res/src/main/res/values-es/strings.xml
index ada8a5bd4d..5b203d5b9a 100644
--- a/core/res/src/main/res/values-es/strings.xml
+++ b/core/res/src/main/res/values-es/strings.xml
@@ -241,6 +241,7 @@
Este mecanismo protege contra los ataques de proximidad a una tarjeta o anillo. Aplicará un retardo entre la recepción y la ejecución de un comando.
Contraseña
Antes de ejecutar un comando que cambie el estado de la tarjeta, deberá ingresar una contraseña.
+ NFT
Programa de referidos
Gire la pantalla de su dispositivo hacia abajo para ocultar y mostrar rápidamente los saldos
%s hashes
@@ -534,6 +535,7 @@
¿Quiere salir del proceso de activación?
Inicializando
Ya se ha creado otra billetera en la tarjeta que está intentando agregar. Si tiene fondos en esta billetera, por favor retírelos y luego reinicie esta tarjeta y agréguela como backup.
+ Guardar su billetera
Creando un backup
Leer más sobre seed phrase
@@ -666,6 +668,7 @@
Permitir el uso de biometría
Se solicitará la biometría en lugar del código de acceso para interactuar con su billetera
Código de acceso
+ No autorizar
Parece que tiene la autenticación biométrica deshabilitada, es necesaria para guardar billeteras
Activar la autorización biométrica
¿Le gustaría usar la biometría?
diff --git a/core/res/src/main/res/values-fr/strings.xml b/core/res/src/main/res/values-fr/strings.xml
index 529de205c8..05074ae06e 100644
--- a/core/res/src/main/res/values-fr/strings.xml
+++ b/core/res/src/main/res/values-fr/strings.xml
@@ -241,6 +241,7 @@
Ce mécanisme protège contre les attaques sans contact sur la carte. Il y a un délai entre la réception et l\'exécution de la commande. Après la première transaction signée, ce téléphone sera associé à la carte et les transactions seront signées immédiatement.
Mot de passe
Avant d\'exécuter une commande qui modifie l\'état de la carte, vous devrez entrer un mot de passe.
+ NFT
Programme de parrainage
Retournez l\'écran de votre appareil vers le bas pour masquer et afficher rapidement les soldes
%s hashes
@@ -536,6 +537,7 @@
Voulez-vous quitter le processus d\'activation ?
Initialisation en cours
Un autre portefeuille a déjà été créé sur la carte que vous essayez d\'ajouter. Si vous avez des fonds dans ce portefeuille, veuillez les retirer, puis réinitialiser cette carte et l\'ajouter comme sauvegarde.
+ Enregistrer votre portefeuille
Sauvegarde en cours
En savoir plus sur les seed phrases
@@ -668,6 +670,7 @@
Autoriser l\'utilisation de la biométrie
La biométrie sera demandée à la place du code d\'accès pour les interactions avec votre portefeuille
Code d\'accès
+ Ne pas autoriser
Il semble que vous ayez désactivé l\'authentification biométrique, elle est nécessaire pour sauvegarder les portefeuilles
Activer l\'autorisation biométrique
Souhaitez-vous utiliser la biométrie ?
diff --git a/core/res/src/main/res/values-it/strings.xml b/core/res/src/main/res/values-it/strings.xml
index 900ef94aa5..9b25886a73 100644
--- a/core/res/src/main/res/values-it/strings.xml
+++ b/core/res/src/main/res/values-it/strings.xml
@@ -19,6 +19,7 @@
Questo meccanismo protegge dagli avvicinamenti senza contatto sulla carta. Attiva un ritardo tra la ricezione e l\'esecuzione di un comando. Dopo la prima transazione firmata, questo telefono verrà associato alla carta e le transazioni verranno firmate immediatamente.
Password
Dovrai inserire una password prima di eseguire qualsiasi comando che modifichi lo stato della carta.
+ NFT
Hash %s
ID carta
Valuta dell\'applicazione
diff --git a/core/res/src/main/res/values-ja/strings.xml b/core/res/src/main/res/values-ja/strings.xml
index aedabcb589..6abf715762 100644
--- a/core/res/src/main/res/values-ja/strings.xml
+++ b/core/res/src/main/res/values-ja/strings.xml
@@ -238,6 +238,7 @@
このメカニズムは、カードまたはリングに対する近接攻撃から保護します。コマンドの受信と実行の間に遅延を強制します。
パスコード
カードの状態の変更を伴うコマンドを実行する前に、パスコードを入力する必要があります。
+ NFT
紹介プログラム
デバイスの画面を下に向けると、残高をすばやく非表示にしたり表示したりできます。
%sハッシュ
@@ -530,6 +531,7 @@
アクティベーション処理を途中で終了しますか?
スタート
追加しようとしているカードには、すでに別のウォレットが作成されています。このウォレットに資金がある場合は、それを引き出してからこのカードをリセットし、バックアップとして追加してください。
+ ウォレットを保存する
バックアップの作成
シードフレーズについてもっと読む
@@ -658,6 +660,7 @@
生体認証の使用を許可する
ウォレットの操作には、アクセスコードの代わりに生体認証が要求されます。
アクセスコード
+ 許可しない
生体認証が無効になっているようです。ウォレットを保存する必要があります。
生体認証を有効にする
生体認証を使用しますか?
@@ -768,6 +771,7 @@
%s 推定利益
市場評価
指標
+ %1$sネットワークルールによれば、 %2$sからの請求が可能です。以下の金額は、ステーキング解除時にアカウントに入金されます。
最低要件
報酬はありません
請求中の報酬
diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml
index 8793740014..33b0c3b4a9 100644
--- a/core/res/src/main/res/values-ru/strings.xml
+++ b/core/res/src/main/res/values-ru/strings.xml
@@ -247,6 +247,7 @@
Этот механизм защищает карту или кольцо от бесконтактных атак. Между сканированием и выполнением команды будет добавлена задержка.
Пароль
Перед выполнением любой команды, влекущей за собой изменение состояния карты, вам необходимо будет ввести пароль.
+ NFT
Реферальная программа
Переверните экран вашего устройства вниз, чтобы быстро скрыть и отобразить балансы
%s хэшей
@@ -546,6 +547,7 @@
Вы хотите выйти из процесса активации?
Подготовка
Другой кошелек уже был создан на карте, которую вы пытаетесь добавить. Если на нем есть средства, пожалуйста сначала выведите их, а затем сделайте сброс до заводских настроек и используйте как резервную.
+ Сохраните ваш кошелек
Резервная копия
Прочитать о seed-фразе
@@ -639,7 +641,7 @@
%1$s (%2$s) в сети %3$s
Использование другой сети может привести к утрате средств.
Отправляйте только %s на этот адрес. Использование другой сети может привести к утрате средств.
- Отправляйте только %1$s в сети %2$s
+ Отправляйте только %1$s в сети %2$s
Переводите средства с любого кошелька или биржи
Участвовать
Не удалось загрузить информацию по реферальной программе. Пожалуйста, попробуйте позже.
@@ -686,6 +688,7 @@
Использовать биометрию
Для операций с вашим кошельком будет запрашиваться биометрия вместо кода доступа карты
Код доступа
+ Не использовать
Похоже, что у вас отключена биометрическая аутентификация, она необходима для сохранения кошельков
Включите биометрическую аутентификацию
Вы хотите использовать биометрию?
diff --git a/core/res/src/main/res/values-uk-rUA/strings.xml b/core/res/src/main/res/values-uk-rUA/strings.xml
index 91701bc5c1..214896573f 100644
--- a/core/res/src/main/res/values-uk-rUA/strings.xml
+++ b/core/res/src/main/res/values-uk-rUA/strings.xml
@@ -247,6 +247,7 @@
Цей механізм захищає картку або кільце від безконтактних атак. Між скануванням картки та виконанням команди буде додана затримка.
Пароль
Перед виконанням будь-якої команди, що тягне за собою зміну стану картки, вам необхідно буде ввести пароль.
+ NFT
Реферальна програма
Переверніть екран пристрою вниз, щоб швидко приховати та відобразити баланси
%s хешів
@@ -546,6 +547,7 @@
Ви хочете вийти з процесу активації?
Підготовка
На картці, яку ви намагаєтеся додати, вже створено інший гаманець. Якщо у вас є кошти на цьому гаманці, будь ласка, виведіть їх, а потім скиньте цю картку до заводських налаштувань і додайте її як резервну.
+ Збережіть ваш гаманець
Резервна копія
Дізнатися більше про seed-фразу
@@ -686,6 +688,7 @@
Використовувати біометрію
Для взаємодії з гаманцем будуть запитуватися біометричні дані замість коду доступу
Код доступу
+ Не дозволяти
Схоже, що у вас відключена біометрична автентифікація, вона необхідна для збереження гаманців
Увімкніть біометричну автентифікацію
Бажаєте використовувати біометрію?
diff --git a/core/res/src/main/res/values-zh-rTW/strings.xml b/core/res/src/main/res/values-zh-rTW/strings.xml
index aaa494dbeb..d4ab1bc4e2 100644
--- a/core/res/src/main/res/values-zh-rTW/strings.xml
+++ b/core/res/src/main/res/values-zh-rTW/strings.xml
@@ -108,6 +108,7 @@
此機制可防止對卡進行鄰近攻擊。它將強制執行命令的接收和執行之間的延遲。
密碼
在執行任何需要更改卡狀態的命令之前,您必須輸入密碼
+ NFT
推薦計畫
%s hashes
卡號
@@ -176,6 +177,7 @@
這此情況,您必須要重新開始
您想要離開啟用程序嗎?
開始
+ 儲存你的錢包
創建備份
閱讀更多關於助記詞的訊息
@@ -241,6 +243,7 @@
允許使用生物辨識
將要求生物識別而不是訪問密碼來與您的錢包進行交互
訪問密碼
+ 不允許
您好像禁用了生物識別,有必要保存錢包
啟用生物識別授權
您想使用生物識別技術嗎?
diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml
index 9b4f9524be..68238443cf 100644
--- a/core/res/src/main/res/values/strings.xml
+++ b/core/res/src/main/res/values/strings.xml
@@ -537,6 +537,7 @@
Do you want to exit the activation process?
Getting started
Another wallet has already been created on the card you\'re trying to add. If you have funds in this wallet, please withdraw it and then reset this card and add it as a backup.
+ Save your wallet
Creating a backup
Read more about seed phrase
@@ -669,6 +670,7 @@
Allow to use biometrics
Biometrics will be requested instead of the access code for interactions with your wallet
Access code
+ Don\'t allow
It looks like you have biometric authentication disabled, it is necessary to save wallets
Enable biometric authorization
Would you like to use biometrics?
@@ -779,6 +781,7 @@
%s est. profit
Market rating
Metrics
+ According to %1$s network rules, claims are possible from %2$s. Amounts below will be credited to your account upon unstaking.
Minimum Requirement
No rewards available
Reward claiming
diff --git a/core/ui/src/main/res/drawable/ic_fingerprint_24.xml b/core/ui/src/main/res/drawable/ic_fingerprint_24.xml
new file mode 100644
index 0000000000..df5bdf4868
--- /dev/null
+++ b/core/ui/src/main/res/drawable/ic_fingerprint_24.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/features/ask-biometry/api/.gitignore b/features/ask-biometry/api/.gitignore
new file mode 100644
index 0000000000..796b96d1c4
--- /dev/null
+++ b/features/ask-biometry/api/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/features/ask-biometry/api/build.gradle.kts b/features/ask-biometry/api/build.gradle.kts
new file mode 100644
index 0000000000..4e2e227056
--- /dev/null
+++ b/features/ask-biometry/api/build.gradle.kts
@@ -0,0 +1,15 @@
+plugins {
+ alias(deps.plugins.android.library)
+ alias(deps.plugins.kotlin.android)
+ id("configuration")
+}
+
+android {
+ namespace = "com.tangem.features.askbiometry"
+}
+
+dependencies {
+ /* Project - Core */
+ implementation(projects.core.decompose)
+ implementation(projects.core.ui)
+}
\ No newline at end of file
diff --git a/features/ask-biometry/api/src/main/kotlin/com/tangem/features/askbiometry/AskBiometryComponent.kt b/features/ask-biometry/api/src/main/kotlin/com/tangem/features/askbiometry/AskBiometryComponent.kt
new file mode 100644
index 0000000000..c01180af46
--- /dev/null
+++ b/features/ask-biometry/api/src/main/kotlin/com/tangem/features/askbiometry/AskBiometryComponent.kt
@@ -0,0 +1,20 @@
+package com.tangem.features.askbiometry
+
+import com.tangem.core.decompose.factory.ComponentFactory
+import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
+import com.tangem.core.ui.decompose.ComposableContentComponent
+
+interface AskBiometryComponent : ComposableContentComponent, ComposableBottomSheetComponent {
+
+ data class Params(
+ val bottomSheetVariant: Boolean,
+ val modelCallbacks: ModelCallbacks,
+ )
+
+ interface ModelCallbacks {
+ fun onAllowed()
+ fun onDenied()
+ }
+
+ interface Factory : ComponentFactory
+}
\ No newline at end of file
diff --git a/features/ask-biometry/api/src/main/kotlin/com/tangem/features/askbiometry/AskBiometryFeatureToggles.kt b/features/ask-biometry/api/src/main/kotlin/com/tangem/features/askbiometry/AskBiometryFeatureToggles.kt
new file mode 100644
index 0000000000..22425c768e
--- /dev/null
+++ b/features/ask-biometry/api/src/main/kotlin/com/tangem/features/askbiometry/AskBiometryFeatureToggles.kt
@@ -0,0 +1,5 @@
+package com.tangem.features.askbiometry
+
+interface AskBiometryFeatureToggles {
+ val isEnabled: Boolean
+}
\ No newline at end of file
diff --git a/features/ask-biometry/impl/.gitignore b/features/ask-biometry/impl/.gitignore
new file mode 100644
index 0000000000..796b96d1c4
--- /dev/null
+++ b/features/ask-biometry/impl/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/features/ask-biometry/impl/build.gradle.kts b/features/ask-biometry/impl/build.gradle.kts
new file mode 100644
index 0000000000..c9d6bc3fac
--- /dev/null
+++ b/features/ask-biometry/impl/build.gradle.kts
@@ -0,0 +1,52 @@
+plugins {
+ alias(deps.plugins.android.library)
+ alias(deps.plugins.kotlin.android)
+ alias(deps.plugins.kotlin.kapt)
+ alias(deps.plugins.kotlin.serialization)
+ alias(deps.plugins.hilt.android)
+ id("configuration")
+}
+
+android {
+ namespace = "com.tangem.features.askbiometry.impl"
+}
+
+dependencies {
+ api(projects.features.askBiometry.api)
+
+ /** Core modules */
+ implementation(projects.core.ui)
+ implementation(projects.core.res)
+ implementation(projects.core.decompose)
+ implementation(projects.core.analytics)
+ implementation(projects.core.analytics.models)
+ implementation(projects.core.configToggles)
+ implementation(projects.core.navigation)
+
+ /** Domain */
+ implementation(projects.domain.wallets)
+ implementation(projects.domain.wallets.models)
+ implementation(projects.domain.settings)
+ implementation(projects.domain.card)
+
+ /** Compose libraries */
+ implementation(deps.compose.material3)
+ implementation(deps.compose.animation)
+ implementation(deps.compose.foundation)
+ implementation(deps.compose.ui)
+ implementation(deps.compose.ui.tooling)
+
+ /** Tangem libraries */
+ implementation(projects.libs.tangemSdkApi)
+ implementation(tangemDeps.card.core)
+ implementation(tangemDeps.card.android) {
+ exclude(module = "joda-time")
+ }
+
+ /** Other */
+ implementation(deps.timber)
+
+ /** DI */
+ implementation(deps.hilt.android)
+ kapt(deps.hilt.kapt)
+}
\ No newline at end of file
diff --git a/features/ask-biometry/impl/src/main/kotlin/com/tangem/features/askbiometry/impl/DefaultAskBiometryComponent.kt b/features/ask-biometry/impl/src/main/kotlin/com/tangem/features/askbiometry/impl/DefaultAskBiometryComponent.kt
new file mode 100644
index 0000000000..3f93ca138e
--- /dev/null
+++ b/features/ask-biometry/impl/src/main/kotlin/com/tangem/features/askbiometry/impl/DefaultAskBiometryComponent.kt
@@ -0,0 +1,63 @@
+package com.tangem.features.askbiometry.impl
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+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.ui.components.bottomsheets.TangemBottomSheet
+import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
+import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
+import com.tangem.features.askbiometry.AskBiometryComponent
+import com.tangem.features.askbiometry.impl.model.AskBiometryModel
+import com.tangem.features.askbiometry.impl.ui.AskBiometry
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+internal class DefaultAskBiometryComponent @AssistedInject constructor(
+ @Assisted appComponentContext: AppComponentContext,
+ @Assisted params: AskBiometryComponent.Params,
+) : AskBiometryComponent, AppComponentContext by appComponentContext {
+
+ private val model: AskBiometryModel = getOrCreateModel(params)
+
+ override fun dismiss() = model.dismiss()
+
+ @Composable
+ override fun BottomSheet() {
+ val state by model.uiState.collectAsStateWithLifecycle()
+ val bsConfig = remember(this) {
+ TangemBottomSheetConfig(
+ isShown = true,
+ onDismissRequest = ::dismiss,
+ content = TangemBottomSheetConfigContent.Empty,
+ )
+ }
+
+ TangemBottomSheet(
+ config = bsConfig,
+ content = { AskBiometry(state) },
+ )
+ }
+
+ @Composable
+ override fun Content(modifier: Modifier) {
+ val state by model.uiState.collectAsStateWithLifecycle()
+
+ AskBiometry(
+ modifier = modifier,
+ state = state,
+ )
+ }
+
+ @AssistedFactory
+ interface Factory : AskBiometryComponent.Factory {
+ override fun create(
+ context: AppComponentContext,
+ params: AskBiometryComponent.Params,
+ ): DefaultAskBiometryComponent
+ }
+}
\ No newline at end of file
diff --git a/features/ask-biometry/impl/src/main/kotlin/com/tangem/features/askbiometry/impl/DefaultAskBiometryFeatureToggles.kt b/features/ask-biometry/impl/src/main/kotlin/com/tangem/features/askbiometry/impl/DefaultAskBiometryFeatureToggles.kt
new file mode 100644
index 0000000000..e2be13dfb9
--- /dev/null
+++ b/features/ask-biometry/impl/src/main/kotlin/com/tangem/features/askbiometry/impl/DefaultAskBiometryFeatureToggles.kt
@@ -0,0 +1,12 @@
+package com.tangem.features.askbiometry.impl
+
+import com.tangem.core.configtoggle.feature.FeatureTogglesManager
+import com.tangem.features.askbiometry.AskBiometryFeatureToggles
+import javax.inject.Inject
+
+class DefaultAskBiometryFeatureToggles @Inject constructor(
+ private val featureTogglesManager: FeatureTogglesManager,
+) : AskBiometryFeatureToggles {
+ override val isEnabled: Boolean
+ get() = featureTogglesManager.isFeatureEnabled("ASK_BIOMETRY_REFACTORING_ENABLED")
+}
\ No newline at end of file
diff --git a/features/ask-biometry/impl/src/main/kotlin/com/tangem/features/askbiometry/impl/di/FeatureModule.kt b/features/ask-biometry/impl/src/main/kotlin/com/tangem/features/askbiometry/impl/di/FeatureModule.kt
new file mode 100644
index 0000000000..e9e8bc3bc1
--- /dev/null
+++ b/features/ask-biometry/impl/src/main/kotlin/com/tangem/features/askbiometry/impl/di/FeatureModule.kt
@@ -0,0 +1,36 @@
+package com.tangem.features.askbiometry.impl.di
+
+import com.tangem.core.decompose.di.ModelComponent
+import com.tangem.core.decompose.model.Model
+import com.tangem.features.askbiometry.AskBiometryComponent
+import com.tangem.features.askbiometry.AskBiometryFeatureToggles
+import com.tangem.features.askbiometry.impl.DefaultAskBiometryComponent
+import com.tangem.features.askbiometry.impl.DefaultAskBiometryFeatureToggles
+import com.tangem.features.askbiometry.impl.model.AskBiometryModel
+import dagger.Binds
+import dagger.Module
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+
+@Module
+@InstallIn(SingletonComponent::class)
+internal interface FeatureModule {
+
+ @Binds
+ fun bindFeatureToggles(impl: DefaultAskBiometryFeatureToggles): AskBiometryFeatureToggles
+
+ @Binds
+ fun bindComponentFactory(impl: DefaultAskBiometryComponent.Factory): AskBiometryComponent.Factory
+}
+
+@Module
+@InstallIn(ModelComponent::class)
+internal interface ModelModule {
+
+ @Binds
+ @IntoMap
+ @ClassKey(AskBiometryModel::class)
+ fun provideModel(model: AskBiometryModel): Model
+}
\ No newline at end of file
diff --git a/features/ask-biometry/impl/src/main/kotlin/com/tangem/features/askbiometry/impl/model/AskBiometryModel.kt b/features/ask-biometry/impl/src/main/kotlin/com/tangem/features/askbiometry/impl/model/AskBiometryModel.kt
new file mode 100644
index 0000000000..a72832828f
--- /dev/null
+++ b/features/ask-biometry/impl/src/main/kotlin/com/tangem/features/askbiometry/impl/model/AskBiometryModel.kt
@@ -0,0 +1,130 @@
+package com.tangem.features.askbiometry.impl.model
+
+import androidx.compose.runtime.Stable
+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.ui.UiMessageSender
+import com.tangem.core.navigation.settings.SettingsManager
+import com.tangem.core.ui.R
+import com.tangem.core.ui.extensions.resourceReference
+import com.tangem.core.ui.extensions.stringReference
+import com.tangem.core.ui.message.DialogMessage
+import com.tangem.core.ui.message.EventMessageAction
+import com.tangem.core.ui.message.SnackbarMessage
+import com.tangem.domain.card.repository.CardSdkConfigRepository
+import com.tangem.domain.settings.SetSaveWalletScreenShownUseCase
+import com.tangem.domain.settings.repositories.SettingsRepository
+import com.tangem.domain.wallets.legacy.UserWalletsListManager
+import com.tangem.domain.wallets.models.UserWallet
+import com.tangem.domain.wallets.repository.WalletsRepository
+import com.tangem.features.askbiometry.AskBiometryComponent
+import com.tangem.features.askbiometry.impl.ui.state.AskBiometryUM
+import com.tangem.sdk.api.TangemSdkManager
+import com.tangem.utils.coroutines.CoroutineDispatcherProvider
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
+import timber.log.Timber
+import javax.inject.Inject
+
+@Suppress("LongParameterList")
+@Stable
+@ModelScoped
+internal class AskBiometryModel @Inject constructor(
+ override val dispatchers: CoroutineDispatcherProvider,
+ paramsContainer: ParamsContainer,
+ private val setSaveWalletScreenShownUseCase: SetSaveWalletScreenShownUseCase,
+ private val settingsRepository: SettingsRepository,
+ private val tangemSdkManager: TangemSdkManager,
+ private val userWalletsListManager: UserWalletsListManager,
+ private val walletsRepository: WalletsRepository,
+ private val cardSdkConfigRepository: CardSdkConfigRepository,
+ private val settingsManager: SettingsManager,
+ private val uiMessageSender: UiMessageSender,
+) : Model() {
+
+ private val params = paramsContainer.require()
+
+ private val _uiState = MutableStateFlow(
+ AskBiometryUM(
+ bottomSheetVariant = params.bottomSheetVariant,
+ onAllowClick = ::onAllowClick,
+ onDontAllowClick = ::dontAllow,
+ ),
+ )
+ val uiState = _uiState.asStateFlow()
+
+ init {
+ modelScope.launch {
+ setSaveWalletScreenShownUseCase()
+ }
+ }
+
+ private fun onAllowClick() {
+ _uiState.update { it.copy(showProgress = true) }
+ allowToUseBiometrics()
+ }
+
+ private fun dontAllow() {
+ params.modelCallbacks.onDenied()
+ }
+
+ fun dismiss() {
+ dontAllow()
+ }
+
+ private fun allowToUseBiometrics() {
+ modelScope.launch {
+ if (tangemSdkManager.checkNeedEnrollBiometrics()) {
+ showEnrollBiometricsDialog()
+ return@launch
+ }
+
+ /*
+
+ * because it will be automatically saved on UserWalletsListManager switch
+ */
+ val selectedUserWallet = userWalletsListManager.selectedUserWalletSync ?: run {
+ Timber.e("Unable to save user wallet")
+ uiMessageSender.send(
+ SnackbarMessage(stringReference("No selected user wallet")),
+ )
+
+ return@launch
+ }
+
+ handleSuccessAllowing(selectedUserWallet)
+ }
+ }
+
+ private suspend fun handleSuccessAllowing(userWallet: UserWallet) {
+ walletsRepository.saveShouldSaveUserWallets(item = true)
+ settingsRepository.setShouldSaveAccessCodes(value = true)
+
+ cardSdkConfigRepository.setAccessCodeRequestPolicy(
+ isBiometricsRequestPolicy = userWallet.hasAccessCode,
+ )
+
+ params.modelCallbacks.onAllowed()
+ }
+
+ private fun showEnrollBiometricsDialog() {
+ uiMessageSender.send(
+ DialogMessage(
+ message = resourceReference(R.string.save_user_wallet_agreement_enroll_biometrics_description),
+ title = resourceReference(R.string.save_user_wallet_agreement_enroll_biometrics_title),
+ firstAction = EventMessageAction(
+ title = resourceReference(R.string.common_enable),
+ onClick = { settingsManager.openBiometricSettings() },
+ ),
+ secondAction = EventMessageAction(
+ title = resourceReference(R.string.common_cancel),
+ onClick = {},
+ ),
+ dismissOnFirstAction = true,
+ ),
+ )
+ }
+}
\ No newline at end of file
diff --git a/features/ask-biometry/impl/src/main/kotlin/com/tangem/features/askbiometry/impl/ui/AskBiometry.kt b/features/ask-biometry/impl/src/main/kotlin/com/tangem/features/askbiometry/impl/ui/AskBiometry.kt
new file mode 100644
index 0000000000..09521a2b56
--- /dev/null
+++ b/features/ask-biometry/impl/src/main/kotlin/com/tangem/features/askbiometry/impl/ui/AskBiometry.kt
@@ -0,0 +1,209 @@
+package com.tangem.features.askbiometry.impl.ui
+
+import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.painter.Painter
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.tooling.preview.Preview
+import com.tangem.core.ui.components.*
+import com.tangem.core.ui.components.atoms.Hand
+import com.tangem.core.ui.extensions.stringResourceSafe
+import com.tangem.core.ui.res.TangemTheme
+import com.tangem.core.ui.res.TangemThemePreview
+import com.tangem.features.askbiometry.impl.ui.state.AskBiometryUM
+import com.tangem.features.askbiometry.impl.R
+
+@Composable
+internal fun AskBiometry(state: AskBiometryUM, modifier: Modifier = Modifier) {
+ Column(
+ modifier = modifier,
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ Column(
+ modifier = Modifier.weight(1f),
+ ) {
+ if (state.bottomSheetVariant) {
+ Header(onCloseClick = state.onDontAllowClick)
+ }
+
+ SpacerH32()
+
+ Column(
+ modifier = Modifier.verticalScroll(rememberScrollState()),
+ ) {
+ Title(modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing22))
+ SpacerH32()
+ Description(
+ modifier = Modifier
+ .padding(horizontal = TangemTheme.dimens.spacing34)
+ .fillMaxWidth(),
+ )
+ }
+ }
+ Footer(state = state)
+ SpacerH16()
+ }
+}
+
+@Composable
+private fun Header(onCloseClick: () -> Unit) {
+ Column(
+ modifier = Modifier.fillMaxWidth(),
+ verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8),
+ ) {
+ Hand()
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.End,
+ ) {
+ IconButton(
+ modifier = Modifier.size(TangemTheme.dimens.size32),
+ onClick = onCloseClick,
+ ) {
+ Icon(
+ modifier = Modifier.size(TangemTheme.dimens.size24),
+ painter = painterResource(id = R.drawable.ic_close_24),
+ tint = TangemTheme.colors.icon.secondary,
+ contentDescription = stringResourceSafe(id = R.string.common_cancel),
+ )
+ }
+ SpacerW8()
+ }
+ }
+}
+
+@Composable
+private fun Title(modifier: Modifier = Modifier) {
+ Column(
+ modifier = modifier,
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing32),
+ ) {
+ Icon(
+ modifier = Modifier.size(TangemTheme.dimens.size56),
+ painter = painterResource(id = R.drawable.ic_fingerprint_24),
+ tint = TangemTheme.colors.icon.primary1,
+ contentDescription = null,
+ )
+ Text(
+ text = stringResourceSafe(id = R.string.save_user_wallet_agreement_header_biometrics),
+ style = TangemTheme.typography.h2,
+ color = TangemTheme.colors.text.primary1,
+ textAlign = TextAlign.Center,
+ )
+ }
+}
+
+@Composable
+private fun Description(modifier: Modifier = Modifier) {
+ Column(
+ modifier = modifier,
+ verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing24),
+ horizontalAlignment = Alignment.Start,
+ ) {
+ DescriptionItem(
+ iconPainter = painterResource(id = R.drawable.ic_face_recognition_24),
+ title = stringResourceSafe(id = R.string.save_user_wallet_agreement_access_title),
+ description = stringResourceSafe(id = R.string.save_user_wallet_agreement_access_description),
+ )
+ DescriptionItem(
+ iconPainter = painterResource(id = R.drawable.ic_lock_24),
+ title = stringResourceSafe(id = R.string.save_user_wallet_agreement_code_title),
+ description = stringResourceSafe(id = R.string.save_user_wallet_agreement_code_description_biometrics),
+ )
+ }
+}
+
+@Composable
+private fun Footer(state: AskBiometryUM, modifier: Modifier = Modifier) {
+ Column(
+ modifier = modifier
+ .padding(horizontal = TangemTheme.dimens.spacing16)
+ .fillMaxWidth(),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ PrimaryButton(
+ modifier = Modifier.fillMaxWidth(),
+ showProgress = state.showProgress,
+ text = stringResourceSafe(id = R.string.save_user_wallet_agreement_allow_biometrics),
+ onClick = state.onAllowClick,
+ )
+
+ if (state.bottomSheetVariant.not()) {
+ SpacerH12()
+
+ SecondaryButton(
+ modifier = Modifier.fillMaxWidth(),
+ text = stringResourceSafe(id = R.string.save_user_wallet_agreement_dont_allow),
+ onClick = state.onDontAllowClick,
+ )
+ }
+
+ SpacerH16()
+
+ Text(
+ modifier = Modifier.fillMaxWidth(fraction = .7f),
+ text = stringResourceSafe(R.string.save_user_wallet_agreement_notice),
+ style = TangemTheme.typography.caption2,
+ color = TangemTheme.colors.text.tertiary,
+ textAlign = TextAlign.Center,
+ )
+ }
+}
+
+@Composable
+private fun DescriptionItem(iconPainter: Painter, title: String, description: String) {
+ Row(
+ verticalAlignment = Alignment.Top,
+ horizontalArrangement = Arrangement.SpaceEvenly,
+ ) {
+ Icon(
+ modifier = Modifier.size(TangemTheme.dimens.size24),
+ painter = iconPainter,
+ tint = TangemTheme.colors.icon.primary1,
+ contentDescription = null,
+ )
+ SpacerW24()
+ Column {
+ Text(
+ text = title,
+ style = TangemTheme.typography.subtitle1,
+ color = TangemTheme.colors.text.primary1,
+ )
+ SpacerH4()
+ Text(
+ text = description,
+ style = TangemTheme.typography.body2,
+ color = TangemTheme.colors.text.secondary,
+ )
+ }
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun Preview() {
+ TangemThemePreview {
+ AskBiometry(
+ state = AskBiometryUM(),
+ )
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun PreviewBS() {
+ TangemThemePreview {
+ AskBiometry(
+ state = AskBiometryUM(bottomSheetVariant = true),
+ )
+ }
+}
\ No newline at end of file
diff --git a/features/ask-biometry/impl/src/main/kotlin/com/tangem/features/askbiometry/impl/ui/state/AskBiometryUM.kt b/features/ask-biometry/impl/src/main/kotlin/com/tangem/features/askbiometry/impl/ui/state/AskBiometryUM.kt
new file mode 100644
index 0000000000..27a511e379
--- /dev/null
+++ b/features/ask-biometry/impl/src/main/kotlin/com/tangem/features/askbiometry/impl/ui/state/AskBiometryUM.kt
@@ -0,0 +1,11 @@
+package com.tangem.features.askbiometry.impl.ui.state
+
+import com.tangem.core.ui.extensions.TextReference
+
+internal data class AskBiometryUM(
+ val bottomSheetVariant: Boolean = false,
+ val showProgress: Boolean = false,
+ val error: TextReference? = null,
+ val onAllowClick: () -> Unit = {},
+ val onDontAllowClick: () -> Unit = {},
+)
\ No newline at end of file
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 73be74c713..cbc2e965dd 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -228,6 +228,9 @@ include(":features:stories:impl")
include(":features:txhistory:api")
include(":features:txhistory:impl")
+include(":features:ask-biometry:api")
+include(":features:ask-biometry:impl")
+
include(":features:nft:api")
include(":features:nft:impl")
// endregion Feature modules