Updated on 2026-08-14

This commit is contained in:
Tangem 2025-03-07 13:44:37 +03:00
parent 0c554c5797
commit c476d59006
25 changed files with 600 additions and 1 deletions

View file

@ -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)

View file

@ -58,5 +58,9 @@
{
"name": "TX_HISTORY_REFACTORING_ENABLED",
"version": "undefined"
},
{
"name": "ASK_BIOMETRY_REFACTORING_ENABLED",
"version": "undefined"
}
]

View file

@ -241,6 +241,7 @@
<string name="details_manage_security_long_tap_description">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.</string>
<string name="details_manage_security_passcode">Passcode</string>
<string name="details_manage_security_passcode_description">Bevor du einen Befehl ausführst, der eine Änderung des Kartenstatus zur Folge hat, musst du den Passcode eingeben.</string>
<string name="details_nft_title">NFT</string>
<string name="details_referral_title">Empfehlungsprogramm</string>
<string name="details_row_description_flip_to_hide">Flippe den Bildschirm deines Geräts nach unten, um Salden schnell ein- und auszublenden</string>
<string name="details_row_subtitle_signed_hashes_format">%s Hasch</string>
@ -536,6 +537,7 @@
<string name="onboarding_exit_alert_title">Möchtest du den Aktivierungsprozess abbrechen?</string>
<string name="onboarding_getting_started">Erste Schritte</string>
<string name="onboarding_linking_error_card_with_wallets">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.</string>
<string name="onboarding_navbar_save_wallet">Sicher deine Wallet</string>
<string name="onboarding_navbar_title_creating_backup">Backups anlegen</string>
<string name="onboarding_seed_button_read_more">Lese mehr über die Seed-Phrase</string>
<plurals name="onboarding_seed_generate_message_words_count">
@ -668,6 +670,7 @@
<string name="save_user_wallet_agreement_allow_biometrics">Nutzung biometrischer Daten zulassen</string>
<string name="save_user_wallet_agreement_code_description_biometrics">Bei Interaktionen mit deiner Wallet werden anstelle des Zugangscodes biometrische Daten abgefragt</string>
<string name="save_user_wallet_agreement_code_title">Zugangscode</string>
<string name="save_user_wallet_agreement_dont_allow">Nicht zulassen</string>
<string name="save_user_wallet_agreement_enroll_biometrics_description">Es sieht so aus, als ob du die biometrische Authentifizierung deaktiviert hast. Diese ist notwendig, um Wallets zu speichern</string>
<string name="save_user_wallet_agreement_enroll_biometrics_title">Biometrische Autorisierung aktivieren</string>
<string name="save_user_wallet_agreement_header_biometrics">Möchtest du Biometrie nutzen?</string>

View file

@ -241,6 +241,7 @@
<string name="details_manage_security_long_tap_description">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.</string>
<string name="details_manage_security_passcode">Contraseña</string>
<string name="details_manage_security_passcode_description">Antes de ejecutar un comando que cambie el estado de la tarjeta, deberá ingresar una contraseña.</string>
<string name="details_nft_title">NFT</string>
<string name="details_referral_title">Programa de referidos</string>
<string name="details_row_description_flip_to_hide">Gire la pantalla de su dispositivo hacia abajo para ocultar y mostrar rápidamente los saldos</string>
<string name="details_row_subtitle_signed_hashes_format">%s hashes</string>
@ -534,6 +535,7 @@
<string name="onboarding_exit_alert_title">¿Quiere salir del proceso de activación?</string>
<string name="onboarding_getting_started">Inicializando</string>
<string name="onboarding_linking_error_card_with_wallets">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.</string>
<string name="onboarding_navbar_save_wallet">Guardar su billetera</string>
<string name="onboarding_navbar_title_creating_backup">Creando un backup</string>
<string name="onboarding_seed_button_read_more">Leer más sobre seed phrase</string>
<plurals name="onboarding_seed_generate_message_words_count">
@ -666,6 +668,7 @@
<string name="save_user_wallet_agreement_allow_biometrics">Permitir el uso de biometría</string>
<string name="save_user_wallet_agreement_code_description_biometrics">Se solicitará la biometría en lugar del código de acceso para interactuar con su billetera</string>
<string name="save_user_wallet_agreement_code_title">Código de acceso</string>
<string name="save_user_wallet_agreement_dont_allow">No autorizar</string>
<string name="save_user_wallet_agreement_enroll_biometrics_description">Parece que tiene la autenticación biométrica deshabilitada, es necesaria para guardar billeteras</string>
<string name="save_user_wallet_agreement_enroll_biometrics_title">Activar la autorización biométrica</string>
<string name="save_user_wallet_agreement_header_biometrics">¿Le gustaría usar la biometría?</string>

View file

@ -241,6 +241,7 @@
<string name="details_manage_security_long_tap_description">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.</string>
<string name="details_manage_security_passcode">Mot de passe</string>
<string name="details_manage_security_passcode_description">Avant d\'exécuter une commande qui modifie l\'état de la carte, vous devrez entrer un mot de passe.</string>
<string name="details_nft_title">NFT</string>
<string name="details_referral_title">Programme de parrainage</string>
<string name="details_row_description_flip_to_hide">Retournez l\'écran de votre appareil vers le bas pour masquer et afficher rapidement les soldes</string>
<string name="details_row_subtitle_signed_hashes_format">%s hashes</string>
@ -536,6 +537,7 @@
<string name="onboarding_exit_alert_title">Voulez-vous quitter le processus d\'activation ?</string>
<string name="onboarding_getting_started">Initialisation en cours</string>
<string name="onboarding_linking_error_card_with_wallets">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.</string>
<string name="onboarding_navbar_save_wallet">Enregistrer votre portefeuille</string>
<string name="onboarding_navbar_title_creating_backup">Sauvegarde en cours</string>
<string name="onboarding_seed_button_read_more">En savoir plus sur les seed phrases</string>
<plurals name="onboarding_seed_generate_message_words_count">
@ -668,6 +670,7 @@
<string name="save_user_wallet_agreement_allow_biometrics">Autoriser l\'utilisation de la biométrie</string>
<string name="save_user_wallet_agreement_code_description_biometrics">La biométrie sera demandée à la place du code d\'accès pour les interactions avec votre portefeuille</string>
<string name="save_user_wallet_agreement_code_title">Code d\'accès</string>
<string name="save_user_wallet_agreement_dont_allow">Ne pas autoriser</string>
<string name="save_user_wallet_agreement_enroll_biometrics_description">Il semble que vous ayez désactivé l\'authentification biométrique, elle est nécessaire pour sauvegarder les portefeuilles</string>
<string name="save_user_wallet_agreement_enroll_biometrics_title">Activer l\'autorisation biométrique</string>
<string name="save_user_wallet_agreement_header_biometrics">Souhaitez-vous utiliser la biométrie ?</string>

View file

@ -19,6 +19,7 @@
<string name="details_manage_security_long_tap_description">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.</string>
<string name="details_manage_security_passcode">Password</string>
<string name="details_manage_security_passcode_description">Dovrai inserire una password prima di eseguire qualsiasi comando che modifichi lo stato della carta.</string>
<string name="details_nft_title">NFT</string>
<string name="details_row_subtitle_signed_hashes_format">Hash %s</string>
<string name="details_row_title_cid">ID carta</string>
<string name="details_row_title_currency">Valuta dell\'applicazione</string>

View file

@ -238,6 +238,7 @@
<string name="details_manage_security_long_tap_description">このメカニズムは、カードまたはリングに対する近接攻撃から保護します。コマンドの受信と実行の間に遅延を強制します。</string>
<string name="details_manage_security_passcode">パスコード</string>
<string name="details_manage_security_passcode_description">カードの状態の変更を伴うコマンドを実行する前に、パスコードを入力する必要があります。</string>
<string name="details_nft_title">NFT</string>
<string name="details_referral_title">紹介プログラム</string>
<string name="details_row_description_flip_to_hide">デバイスの画面を下に向けると、残高をすばやく非表示にしたり表示したりできます。</string>
<string name="details_row_subtitle_signed_hashes_format">%sハッシュ</string>
@ -530,6 +531,7 @@
<string name="onboarding_exit_alert_title">アクティベーション処理を途中で終了しますか?</string>
<string name="onboarding_getting_started">スタート</string>
<string name="onboarding_linking_error_card_with_wallets">追加しようとしているカードには、すでに別のウォレットが作成されています。このウォレットに資金がある場合は、それを引き出してからこのカードをリセットし、バックアップとして追加してください。</string>
<string name="onboarding_navbar_save_wallet">ウォレットを保存する</string>
<string name="onboarding_navbar_title_creating_backup">バックアップの作成</string>
<string name="onboarding_seed_button_read_more">シードフレーズについてもっと読む</string>
<plurals name="onboarding_seed_generate_message_words_count">
@ -658,6 +660,7 @@
<string name="save_user_wallet_agreement_allow_biometrics">生体認証の使用を許可する</string>
<string name="save_user_wallet_agreement_code_description_biometrics">ウォレットの操作には、アクセスコードの代わりに生体認証が要求されます。</string>
<string name="save_user_wallet_agreement_code_title">アクセスコード</string>
<string name="save_user_wallet_agreement_dont_allow">許可しない</string>
<string name="save_user_wallet_agreement_enroll_biometrics_description">生体認証が無効になっているようです。ウォレットを保存する必要があります。</string>
<string name="save_user_wallet_agreement_enroll_biometrics_title">生体認証を有効にする</string>
<string name="save_user_wallet_agreement_header_biometrics">生体認証を使用しますか?</string>
@ -768,6 +771,7 @@
<string name="staking_details_estimated_profit">%s 推定利益</string>
<string name="staking_details_market_rating">市場評価</string>
<string name="staking_details_metrics_block_header">指標</string>
<string name="staking_details_min_rewards_notification">%1$sネットワークルールによれば、 %2$sからの請求が可能です。以下の金額は、ステーキング解除時にアカウントに入金されます。</string>
<string name="staking_details_minimum_requirement">最低要件</string>
<string name="staking_details_no_rewards_to_claim">報酬はありません</string>
<string name="staking_details_reward_claiming">請求中の報酬</string>

View file

@ -247,6 +247,7 @@
<string name="details_manage_security_long_tap_description">Этот механизм защищает карту или кольцо от бесконтактных атак. Между сканированием и выполнением команды будет добавлена задержка.</string>
<string name="details_manage_security_passcode">Пароль</string>
<string name="details_manage_security_passcode_description">Перед выполнением любой команды, влекущей за собой изменение состояния карты, вам необходимо будет ввести пароль.</string>
<string name="details_nft_title">NFT</string>
<string name="details_referral_title">Реферальная программа</string>
<string name="details_row_description_flip_to_hide">Переверните экран вашего устройства вниз, чтобы быстро скрыть и отобразить балансы</string>
<string name="details_row_subtitle_signed_hashes_format">%s хэшей</string>
@ -546,6 +547,7 @@
<string name="onboarding_exit_alert_title">Вы хотите выйти из процесса активации?</string>
<string name="onboarding_getting_started">Подготовка</string>
<string name="onboarding_linking_error_card_with_wallets">Другой кошелек уже был создан на карте, которую вы пытаетесь добавить. Если на нем есть средства, пожалуйста сначала выведите их, а затем сделайте сброс до заводских настроек и используйте как резервную.</string>
<string name="onboarding_navbar_save_wallet">Сохраните ваш кошелек</string>
<string name="onboarding_navbar_title_creating_backup">Резервная копия</string>
<string name="onboarding_seed_button_read_more">Прочитать о seed-фразе</string>
<plurals name="onboarding_seed_generate_message_words_count">
@ -639,7 +641,7 @@
<string name="receive_bottom_sheet_warning_message">%1$s (%2$s) в сети %3$s</string>
<string name="receive_bottom_sheet_warning_message_description">Использование другой сети может привести к утрате средств.</string>
<string name="receive_bottom_sheet_warning_message_full">Отправляйте только %s на этот адрес. Использование другой сети может привести к утрате средств.</string>
<string name="receive_bottom_sheet_warning_title">Отправляйте только %1$s в сети %2$s </string>
<string name="receive_bottom_sheet_warning_title">Отправляйте только %1$s в сети %2$s</string>
<string name="receive_token_description">Переводите средства с любого кошелька или биржи</string>
<string name="referral_button_participate">Участвовать</string>
<string name="referral_error_failed_to_load_info">Не удалось загрузить информацию по реферальной программе. Пожалуйста, попробуйте позже.</string>
@ -686,6 +688,7 @@
<string name="save_user_wallet_agreement_allow_biometrics">Использовать биометрию</string>
<string name="save_user_wallet_agreement_code_description_biometrics">Для операций с вашим кошельком будет запрашиваться биометрия вместо кода доступа карты</string>
<string name="save_user_wallet_agreement_code_title">Код доступа</string>
<string name="save_user_wallet_agreement_dont_allow">Не использовать</string>
<string name="save_user_wallet_agreement_enroll_biometrics_description">Похоже, что у вас отключена биометрическая аутентификация, она необходима для сохранения кошельков</string>
<string name="save_user_wallet_agreement_enroll_biometrics_title">Включите биометрическую аутентификацию</string>
<string name="save_user_wallet_agreement_header_biometrics">Вы хотите использовать биометрию?</string>

View file

@ -247,6 +247,7 @@
<string name="details_manage_security_long_tap_description">Цей механізм захищає картку або кільце від безконтактних атак. Між скануванням картки та виконанням команди буде додана затримка.</string>
<string name="details_manage_security_passcode">Пароль</string>
<string name="details_manage_security_passcode_description">Перед виконанням будь-якої команди, що тягне за собою зміну стану картки, вам необхідно буде ввести пароль.</string>
<string name="details_nft_title">NFT</string>
<string name="details_referral_title">Реферальна програма</string>
<string name="details_row_description_flip_to_hide">Переверніть екран пристрою вниз, щоб швидко приховати та відобразити баланси</string>
<string name="details_row_subtitle_signed_hashes_format">%s хешів</string>
@ -546,6 +547,7 @@
<string name="onboarding_exit_alert_title">Ви хочете вийти з процесу активації?</string>
<string name="onboarding_getting_started">Підготовка</string>
<string name="onboarding_linking_error_card_with_wallets">На картці, яку ви намагаєтеся додати, вже створено інший гаманець. Якщо у вас є кошти на цьому гаманці, будь ласка, виведіть їх, а потім скиньте цю картку до заводських налаштувань і додайте її як резервну.</string>
<string name="onboarding_navbar_save_wallet">Збережіть ваш гаманець</string>
<string name="onboarding_navbar_title_creating_backup">Резервна копія</string>
<string name="onboarding_seed_button_read_more">Дізнатися більше про seed-фразу</string>
<plurals name="onboarding_seed_generate_message_words_count">
@ -686,6 +688,7 @@
<string name="save_user_wallet_agreement_allow_biometrics">Використовувати біометрію</string>
<string name="save_user_wallet_agreement_code_description_biometrics">Для взаємодії з гаманцем будуть запитуватися біометричні дані замість коду доступу</string>
<string name="save_user_wallet_agreement_code_title">Код доступу</string>
<string name="save_user_wallet_agreement_dont_allow">Не дозволяти</string>
<string name="save_user_wallet_agreement_enroll_biometrics_description">Схоже, що у вас відключена біометрична автентифікація, вона необхідна для збереження гаманців</string>
<string name="save_user_wallet_agreement_enroll_biometrics_title">Увімкніть біометричну автентифікацію</string>
<string name="save_user_wallet_agreement_header_biometrics">Бажаєте використовувати біометрію?</string>

View file

@ -108,6 +108,7 @@
<string name="details_manage_security_long_tap_description">此機制可防止對卡進行鄰近攻擊。它將強制執行命令的接收和執行之間的延遲。</string>
<string name="details_manage_security_passcode">密碼</string>
<string name="details_manage_security_passcode_description">在執行任何需要更改卡狀態的命令之前,您必須輸入密碼</string>
<string name="details_nft_title">NFT</string>
<string name="details_referral_title">推薦計畫</string>
<string name="details_row_subtitle_signed_hashes_format">%s hashes</string>
<string name="details_row_title_cid">卡號</string>
@ -176,6 +177,7 @@
<string name="onboarding_exit_alert_message">這此情況,您必須要重新開始</string>
<string name="onboarding_exit_alert_title">您想要離開啟用程序嗎?</string>
<string name="onboarding_getting_started">開始</string>
<string name="onboarding_navbar_save_wallet">儲存你的錢包</string>
<string name="onboarding_navbar_title_creating_backup">創建備份</string>
<string name="onboarding_seed_button_read_more">閱讀更多關於助記詞的訊息</string>
<plurals name="onboarding_seed_generate_message_words_count">
@ -241,6 +243,7 @@
<string name="save_user_wallet_agreement_allow_biometrics">允許使用生物辨識</string>
<string name="save_user_wallet_agreement_code_description_biometrics">將要求生物識別而不是訪問密碼來與您的錢包進行交互</string>
<string name="save_user_wallet_agreement_code_title">訪問密碼</string>
<string name="save_user_wallet_agreement_dont_allow">不允許</string>
<string name="save_user_wallet_agreement_enroll_biometrics_description">您好像禁用了生物識別,有必要保存錢包</string>
<string name="save_user_wallet_agreement_enroll_biometrics_title">啟用生物識別授權</string>
<string name="save_user_wallet_agreement_header_biometrics">您想使用生物識別技術嗎?</string>

View file

@ -537,6 +537,7 @@
<string name="onboarding_exit_alert_title">Do you want to exit the activation process?</string>
<string name="onboarding_getting_started">Getting started</string>
<string name="onboarding_linking_error_card_with_wallets">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.</string>
<string name="onboarding_navbar_save_wallet">Save your wallet</string>
<string name="onboarding_navbar_title_creating_backup">Creating a backup</string>
<string name="onboarding_seed_button_read_more">Read more about seed phrase</string>
<plurals name="onboarding_seed_generate_message_words_count">
@ -669,6 +670,7 @@
<string name="save_user_wallet_agreement_allow_biometrics">Allow to use biometrics</string>
<string name="save_user_wallet_agreement_code_description_biometrics">Biometrics will be requested instead of the access code for interactions with your wallet</string>
<string name="save_user_wallet_agreement_code_title">Access code</string>
<string name="save_user_wallet_agreement_dont_allow">Don\'t allow</string>
<string name="save_user_wallet_agreement_enroll_biometrics_description">It looks like you have biometric authentication disabled, it is necessary to save wallets</string>
<string name="save_user_wallet_agreement_enroll_biometrics_title">Enable biometric authorization</string>
<string name="save_user_wallet_agreement_header_biometrics">Would you like to use biometrics?</string>
@ -779,6 +781,7 @@
<string name="staking_details_estimated_profit">%s est. profit</string>
<string name="staking_details_market_rating">Market rating</string>
<string name="staking_details_metrics_block_header">Metrics</string>
<string name="staking_details_min_rewards_notification">According to %1$s network rules, claims are possible from %2$s. Amounts below will be credited to your account upon unstaking.</string>
<string name="staking_details_minimum_requirement">Minimum Requirement</string>
<string name="staking_details_no_rewards_to_claim">No rewards available</string>
<string name="staking_details_reward_claiming">Reward claiming</string>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000000"
android:pathData="M17.8,4.5C17.7,4.5 17.7,4.5 17.6,4.4C15.7,3.4 14,3 12,3C10,3 8.2,3.5 6.4,4.4C6.2,4.5 5.9,4.5 5.8,4.2C5.6,4 5.7,3.7 6,3.5C7.8,2.5 9.9,2 12,2C14.1,2 16,2.5 18,3.5C18.3,3.7 18.4,4 18.3,4.2C18.2,4.4 18,4.5 17.8,4.5ZM3.5,9.7C3.4,9.7 3.3,9.7 3.2,9.6C3,9.5 2.9,9.2 3.1,8.9C4.1,7.5 5.3,6.4 6.8,5.7C10,4 14,4 17.2,5.7C18.7,6.4 19.9,7.5 20.9,8.9C21.1,9.1 21,9.4 20.8,9.6C20.6,9.8 20.2,9.7 20.1,9.5C19.2,8.2 18,7.2 16.7,6.5C13.8,5.1 10.2,5.1 7.3,6.6C5.9,7.3 4.8,8.3 3.9,9.5C3.8,9.7 3.7,9.7 3.5,9.7ZM9.8,21.8C9.6,21.8 9.5,21.7 9.4,21.6C8.5,20.8 8.1,20.2 7.4,19C6.7,17.8 6.3,16.3 6.3,14.7C6.3,11.7 8.9,9.3 12,9.3C15.1,9.3 17.7,11.7 17.7,14.7C17.7,14.8 17.6,14.9 17.5,15C17.4,15.1 17.3,15.2 17.2,15.2C17,15.2 16.9,15.1 16.8,15C16.7,14.9 16.7,14.8 16.7,14.7C16.7,12.2 14.6,10.3 12,10.3C9.4,10.3 7.3,12.2 7.3,14.7C7.3,16.1 7.7,17.4 8.3,18.5C8.9,19.7 9.4,20.2 10.1,20.9C10.3,21.1 10.3,21.4 10.1,21.6C10,21.7 9.9,21.8 9.8,21.8ZM16.9,19.9C15.7,19.9 14.7,19.6 13.8,19.1C12.3,18 11.4,16.4 11.4,14.7C11.4,14.5 11.5,14.4 11.6,14.3C11.7,14.2 11.8,14.2 11.9,14.2C12.1,14.2 12.2,14.2 12.3,14.3C12.4,14.4 12.4,14.5 12.4,14.7C12.4,16.1 13.2,17.4 14.4,18.2C15.1,18.7 15.9,18.9 16.9,18.9C17.2,18.9 17.6,18.9 18,18.8C18.2,18.8 18.5,19 18.5,19.2C18.6,19.5 18.4,19.8 18.1,19.8C17.6,19.9 17.1,19.9 16.9,19.9ZM14.9,22C14.9,22 14.8,22 14.8,22C13.2,21.5 12.2,21 11.1,19.9C9.7,18.5 8.9,16.6 8.9,14.7C8.9,13 10.3,11.7 12,11.7C13.7,11.7 15.1,13 15.1,14.7C15.1,15.7 16,16.6 17.1,16.6C18.3,16.6 19.2,15.7 19.2,14.7C19.2,10.9 16,7.8 12,7.8C9.1,7.8 6.5,9.4 5.4,11.9C5,12.7 4.8,13.6 4.8,14.7C4.8,15.4 4.8,16.7 5.4,18.3C5.5,18.5 5.4,18.8 5.1,18.9C4.9,19 4.6,18.9 4.5,18.6C4,17.3 3.8,16 3.8,14.7C3.8,13.5 4,12.4 4.5,11.4C5.8,8.6 8.7,6.8 12,6.8C16.5,6.8 20.2,10.3 20.2,14.7C20.2,16.3 18.8,17.6 17.1,17.6C15.4,17.6 14.1,16.3 14.1,14.7C14.1,13.6 13.1,12.7 12,12.7C10.8,12.7 9.9,13.6 9.9,14.7C9.9,16.4 10.6,18 11.8,19.2C12.7,20.1 13.6,20.6 15,21C15.3,21.1 15.5,21.4 15.4,21.6C15.3,21.9 15.1,22 14.9,22Z" />
</vector>

1
features/ask-biometry/api/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

@ -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)
}

View file

@ -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<Params, AskBiometryComponent>
}

View file

@ -0,0 +1,5 @@
package com.tangem.features.askbiometry
interface AskBiometryFeatureToggles {
val isEnabled: Boolean
}

1
features/ask-biometry/impl/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

@ -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)
}

View file

@ -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<TangemBottomSheetConfigContent.Empty>(
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
}
}

View file

@ -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")
}

View file

@ -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
}

View file

@ -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<AskBiometryComponent.Params>()
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,
),
)
}
}

View file

@ -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),
)
}
}

View file

@ -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 = {},
)

View file

@ -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