Cw 858 ledger enhancements (#2034)

* feat: Redesign HW Account select screen * fix: Avoid "Bluetooth disabled" Exception on dispose of HW Connection screen * feat: Proceed on Ledger Modal is not dismissible by tapping out * feat: Gracefully ignore errors thrown by monero ledger keep alive function * feat: Close a monero ledger wallet on wallet change * fix: Leave connect screen without connecting

Konstantin Ullrich committed Feb 18, 2025 at 23:23 UTC 9d6f985a59ff1464467ba4798dd4ea057d037b3f
36 files changed +80 -33
cw_monero/lib/ledger.dart
+6 -8
@@ -56,14 +56,12 @@ void enableLedgerExchange(monero.wallet ptr, LedgerConnection connection) {
56 void keepAlive(LedgerConnection connection) {
57 if (connection.connectionType == ConnectionType.ble) {
58 _ledgerKeepAlive = Timer.periodic(Duration(seconds: 10), (_) async {
59 - try {
60 - UniversalBle.setNotifiable(
61 - connection.device.id,
62 - connection.device.deviceInfo.serviceId,
63 - connection.device.deviceInfo.notifyCharacteristicKey,
64 - BleInputProperty.notification,
65 - );
66 - } catch (_) {}
59 + UniversalBle.setNotifiable(
60 + connection.device.id,
61 + connection.device.deviceInfo.serviceId,
62 + connection.device.deviceInfo.notifyCharacteristicKey,
63 + BleInputProperty.notification,
64 + ).onError((_, __) async {});
65 });
66 }
67 }
cw_monero/lib/monero_wallet.dart
+17
@@ -179,6 +179,23 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
179
180 @override
181 Future<void> close({bool shouldCleanup = false}) async {
182 + if (isHardwareWallet) {
183 + disableLedgerExchange();
184 + final currentWalletDirPath = await pathForWalletDir(name: name, type: type);
185 + if (openedWalletsByPath["$currentWalletDirPath/$name"] != null) {
186 + printV("closing wallet");
187 + final wmaddr = wmPtr.address;
188 + final waddr = openedWalletsByPath["$currentWalletDirPath/$name"]!.address;
189 + await Isolate.run(() {
190 + monero.WalletManager_closeWallet(
191 + Pointer.fromAddress(wmaddr), Pointer.fromAddress(waddr), true);
192 + });
193 + openedWalletsByPath.remove("$currentWalletDirPath/$name");
194 + wptr = null;
195 + printV("wallet closed");
196 + }
197 + }
198 +
199 _listener?.stop();
200 _onAccountChangeReaction?.reaction.dispose();
201 _onTxHistoryChangeReaction?.reaction.dispose();
lib/reactions/on_authentication_state_change.dart
+1
@@ -67,6 +67,7 @@ void startAuthenticationStateChange(
67 alertTitle: S.of(context).proceed_on_device,
68 alertContent: S.of(context).proceed_on_device_description,
69 buttonText: S.of(context).cancel,
70 + alertBarrierDismissible: false,
71 buttonAction: () => Navigator.of(context).pop()),
72 );
73 await loadCurrentWallet();
lib/src/screens/connect_device/monero_hardware_wallet_options_page.dart
+1
@@ -190,6 +190,7 @@ class _MoneroHardwareWalletOptionsFormState
190 alertTitle: S.of(context).proceed_on_device,
191 alertContent: S.of(context).proceed_on_device_description,
192 buttonText: S.of(context).cancel,
193 + alertBarrierDismissible: false,
194 buttonAction: () => Navigator.of(context).pop(),
195 ),
196 );
lib/src/screens/connect_device/select_hardware_wallet_account_page.dart
+3 -2
@@ -147,7 +147,7 @@ class _SelectHardwareWalletAccountFormState extends State<SelectHardwareWalletAc
147 child: Container(
148 width: double.infinity,
149 child: Text(
150 - "Available accounts",
150 + S.of(context).select_hw_account_below,
151 style: TextStyle(
152 fontSize: 16,
153 fontWeight: FontWeight.w500,
@@ -159,6 +159,7 @@ class _SelectHardwareWalletAccountFormState extends State<SelectHardwareWalletAc
159 Observer(
160 builder: (context) => Column(
161 children: _walletHardwareRestoreVM.availableAccounts.map((acc) {
162 +
163 final address = acc.address;
164 return Padding(
165 padding: EdgeInsets.only(top: 10),
@@ -170,7 +171,7 @@ class _SelectHardwareWalletAccountFormState extends State<SelectHardwareWalletAc
171 width: 24,
172 ),
173 text:
173 - "${address.substring(0, 6)}...${address.substring(address.length - 6)}",
174 + "${acc.accountIndex} - ${address.substring(0, 6)}...${address.substring(address.length - 6)}",
175 showTrailingIcon: false,
176 height: 54,
177 isSelected: _walletHardwareRestoreVM.selectedAccount == acc,
lib/src/screens/send/send_page.dart
+1
@@ -599,6 +599,7 @@ class SendPage extends BasePage {
599 alertTitle: S.of(context).proceed_on_device,
600 alertContent: S.of(context).proceed_on_device_description,
601 buttonText: S.of(context).cancel,
602 + alertBarrierDismissible: false,
603 buttonAction: () {
604 sendViewModel.state = InitialExecutionState();
605 Navigator.of(context).pop();
lib/src/screens/wallet_list/wallet_list_page.dart
+5 -2
@@ -31,8 +31,6 @@ import 'package:cw_core/wallet_type.dart';
31 import 'package:flutter/material.dart';
32 import 'package:flutter_mobx/flutter_mobx.dart';
33
34 -import '../../../themes/extensions/dashboard_page_theme.dart';
35 -
34 class WalletListPage extends BasePage {
35 WalletListPage({
36 required this.walletListViewModel,
@@ -506,23 +504,28 @@ class WalletListBodyState extends State<WalletListBody> {
504 final requireHardwareWalletConnection = widget.walletListViewModel
505 .requireHardwareWalletConnection(wallet);
506 if (requireHardwareWalletConnection) {
507 + bool didConnect = false;
508 await Navigator.of(context).pushNamed(
509 Routes.connectDevices,
510 arguments: ConnectDevicePageParams(
511 walletType: WalletType.monero,
512 onConnectDevice: (context, ledgerVM) async {
513 monero!.setGlobalLedgerConnection(ledgerVM.connection);
514 + didConnect = true;
515 Navigator.of(context).pop();
516 },
517 ),
518 );
519
520 + if (!didConnect) return;
521 +
522 showPopUp<void>(
523 context: context,
524 builder: (BuildContext context) => AlertWithOneAction(
525 alertTitle: S.of(context).proceed_on_device,
526 alertContent: S.of(context).proceed_on_device_description,
527 buttonText: S.of(context).cancel,
528 + alertBarrierDismissible: false,
529 buttonAction: () => Navigator.of(context).pop()),
530 );
531 }
lib/view_model/hardware_wallet/ledger_view_model.dart
+17 -20
@@ -16,7 +16,6 @@ import 'package:cw_core/utils/print_verbose.dart';
16 import 'package:cw_core/wallet_base.dart';
17 import 'package:cw_core/wallet_type.dart';
18 import 'package:flutter/widgets.dart';
19 -
19 import 'package:ledger_flutter_plus/ledger_flutter_plus.dart' as sdk;
20 import 'package:mobx/mobx.dart';
21 import 'package:permission_handler/permission_handler.dart';
@@ -61,20 +60,21 @@ abstract class LedgerViewModelBase with Store {
60 bool bleIsEnabled = false;
61
62 bool _bleIsInitialized = false;
63 +
64 Future<void> _initBLE() async {
65 if (bleIsEnabled && !_bleIsInitialized) {
66 ledgerPlusBLE = sdk.LedgerInterface.ble(
67 - onPermissionRequest: (_) async {
68 - Map<Permission, PermissionStatus> statuses = await [
69 - Permission.bluetoothScan,
70 - Permission.bluetoothConnect,
71 - Permission.bluetoothAdvertise,
72 - ].request();
73 -
74 - return statuses.values.where((status) => status.isDenied).isEmpty;
75 - },
76 - bleOptions:
77 - sdk.BluetoothOptions(maxScanDuration: Duration(minutes: 5)));
67 + onPermissionRequest: (_) async {
68 + Map<Permission, PermissionStatus> statuses = await [
69 + Permission.bluetoothScan,
70 + Permission.bluetoothConnect,
71 + Permission.bluetoothAdvertise,
72 + ].request();
73 +
74 + return statuses.values.where((status) => status.isDenied).isEmpty;
75 + },
76 + bleOptions: sdk.BluetoothOptions(maxScanDuration: Duration(minutes: 5)),
77 + );
78 _bleIsInitialized = true;
79 }
80 }
@@ -92,10 +92,8 @@ abstract class LedgerViewModelBase with Store {
92 Stream<sdk.LedgerDevice> scanForUsbDevices() => ledgerPlusUSB.scan();
93
94 Future<void> stopScanning() async {
95 - await ledgerPlusBLE.stopScanning();
96 - if (!Platform.isIOS) {
97 - await ledgerPlusUSB.stopScanning();
98 - }
95 + if (_bleIsInitialized) await ledgerPlusBLE.stopScanning();
96 + if (!Platform.isIOS) await ledgerPlusUSB.stopScanning();
97 }
98
99 Future<void> connectLedger(sdk.LedgerDevice device, WalletType type) async {
@@ -112,8 +110,8 @@ abstract class LedgerViewModelBase with Store {
110 : ledgerPlusUSB;
111
112 if (_connectionChangeSubscription == null) {
115 - _connectionChangeSubscription = ledger.deviceStateChanges
116 - .listen(_connectionChangeListener);
113 + _connectionChangeSubscription =
114 + ledger.deviceStateChanges.listen(_connectionChangeListener);
115 }
116
117 _connection = await ledger.connect(device);
@@ -125,8 +123,7 @@ abstract class LedgerViewModelBase with Store {
123 bool _isConnecting = true;
124 WalletType? _connectingWalletType;
125
128 - void _connectionChangeListener(
129 - sdk.BleConnectionState event, ) {
126 + void _connectionChangeListener(sdk.BleConnectionState event) {
127 printV('Ledger Device State Changed: $event');
128 if (event == sdk.BleConnectionState.disconnected && !_isConnecting) {
129 _connection = null;
res/values/strings_ar.arb
+1
@@ -668,6 +668,7 @@
668 "select_backup_file": "حدد ملف النسخ الاحتياطي",
669 "select_buy_provider_notice": "حدد مزود شراء أعلاه. يمكنك تخطي هذه الشاشة عن طريق تعيين مزود شراء الافتراضي في إعدادات التطبيق.",
670 "select_destination": ".ﻲﻃﺎﻴﺘﺣﻻﺍ ﺦﺴﻨﻟﺍ ﻒﻠﻣ ﺔﻬﺟﻭ ﺪﻳﺪﺤﺗ ءﺎﺟﺮﻟﺍ",
671 + "select_hw_account_below": "الرجاء تحديد حساب الاستعادة أدناه:",
672 "select_sell_provider_notice": ".ﻖﻴﺒﻄﺘﻟﺍ ﺕﺍﺩﺍﺪﻋﺇ ﻲﻓ ﻚﺑ ﺹﺎﺨﻟﺍ ﻲﺿﺍﺮﺘﻓﻻﺍ ﻊﻴﺒﻟﺍ ﺩﻭﺰﻣ ﻦﻴﻴﻌﺗ ﻖﻳﺮﻃ ﻦﻋ ﺔﺷﺎﺸﻟﺍ ﻩﺬﻫ ﻲﻄﺨﺗ",
673 "select_your_country": "الرجاء تحديد بلدك",
674 "sell": "بيع",
res/values/strings_bg.arb
+1
@@ -668,6 +668,7 @@
668 "select_backup_file": "Избор на резервно копие",
669 "select_buy_provider_notice": "Изберете доставчик на покупка по -горе. Можете да пропуснете този екран, като зададете вашия доставчик по подразбиране по подразбиране в настройките на приложението.",
670 "select_destination": "Моля, изберете дестинация за архивния файл.",
671 + "select_hw_account_below": "Моля, изберете кой акаунт да възстановите по -долу:",
672 "select_sell_provider_notice": "Изберете доставчик на продажба по-горе. Можете да пропуснете този екран, като зададете своя доставчик на продажба по подразбиране в настройките на приложението.",
673 "select_your_country": "Моля, изберете вашата страна",
674 "sell": "Продаване",
res/values/strings_cs.arb
+1
@@ -668,6 +668,7 @@
668 "select_backup_file": "Vybrat soubor se zálohou",
669 "select_buy_provider_notice": "Vyberte výše uvedeného poskytovatele nákupu. Tuto obrazovku můžete přeskočit nastavením výchozího poskytovatele nákupu v nastavení aplikace.",
670 "select_destination": "Vyberte cíl pro záložní soubor.",
671 + "select_hw_account_below": "Níže vyberte, který účet chcete obnovit:",
672 "select_sell_provider_notice": "Výše vyberte poskytovatele prodeje. Tuto obrazovku můžete přeskočit nastavením výchozího poskytovatele prodeje v nastavení aplikace.",
673 "select_your_country": "Vyberte prosím svou zemi",
674 "sell": "Prodat",
res/values/strings_de.arb
+2 -1
@@ -669,6 +669,7 @@
669 "select_backup_file": "Sicherungsdatei auswählen",
670 "select_buy_provider_notice": "Wählen Sie oben einen Anbieter kaufen. Sie können diese Seite überspringen, indem Sie Ihren Standard-Kaufanbieter in den App-Einstellungen festlegen.",
671 "select_destination": "Bitte wählen Sie das Ziel für die Sicherungsdatei aus.",
672 + "select_hw_account_below": "Bitte wählen Sie unten, welches Konto unten wiederhergestellt werden soll:",
673 "select_sell_provider_notice": "Wählen Sie oben einen Verkaufsanbieter aus. Sie können diesen Bildschirm überspringen, indem Sie in den App-Einstellungen Ihren Standard-Verkaufsanbieter festlegen.",
674 "select_your_country": "Bitte wählen Sie Ihr Land aus",
675 "sell": "Verkaufen",
@@ -1001,4 +1002,4 @@
1002 "you_will_get": "Konvertieren zu",
1003 "you_will_send": "Konvertieren von",
1004 "yy": "YY"
1004 -}
1005 +}
\ No newline at end of file
res/values/strings_en.arb
+1
@@ -669,6 +669,7 @@
669 "select_backup_file": "Select backup file",
670 "select_buy_provider_notice": "Select a buy provider above. You can skip this screen by setting your default buy provider in app settings.",
671 "select_destination": "Please select destination for the backup file.",
672 + "select_hw_account_below": "Please select which account to restore below:",
673 "select_sell_provider_notice": "Select a sell provider above. You can skip this screen by setting your default sell provider in app settings.",
674 "select_your_country": "Please select your country",
675 "sell": "Sell",
res/values/strings_es.arb
+1
@@ -669,6 +669,7 @@
669 "select_backup_file": "Seleccionar archivo de respaldo",
670 "select_buy_provider_notice": "Selecciona un proveedor de compra arriba. Puede omitir esta pantalla configurando su proveedor de compra predeterminado en la configuración de la aplicación.",
671 "select_destination": "Selecciona el destino del archivo de copia de seguridad.",
672 + "select_hw_account_below": "Seleccione qué cuenta restaurar a continuación:",
673 "select_sell_provider_notice": "Selecciona un proveedor de venta arriba. Puede omitir esta pantalla configurando su proveedor de venta predeterminado en la configuración de la aplicación.",
674 "select_your_country": "Seleccione su país",
675 "sell": "Vender",
res/values/strings_fr.arb
+1
@@ -668,6 +668,7 @@
668 "select_backup_file": "Sélectionnez le fichier de sauvegarde",
669 "select_buy_provider_notice": "Sélectionnez un fournisseur d'achat ci-dessus. Vous pouvez ignorer cet écran en définissant votre fournisseur d'achat par défaut dans les paramètres de l'application.",
670 "select_destination": "Veuillez sélectionner la destination du fichier de sauvegarde.",
671 + "select_hw_account_below": "Veuillez sélectionner le compte à restaurer ci-dessous:",
672 "select_sell_provider_notice": "Sélectionnez un fournisseur de vente ci-dessus. Vous pouvez ignorer cet écran en définissant votre fournisseur de vente par défaut dans les paramètres de l'application.",
673 "select_your_country": "Veuillez sélectionner votre pays",
674 "sell": "Vendre",
res/values/strings_ha.arb
+1
@@ -670,6 +670,7 @@
670 "select_backup_file": "Zaɓi fayil ɗin madadin",
671 "select_buy_provider_notice": "Zaɓi mai ba da kyauta a sama. Zaka iya tsallake wannan allon ta hanyar saita mai ba da isasshen busasshen mai ba da isasshen busasshiyar saiti.",
672 "select_destination": "Da fatan za a zaɓi wurin da za a yi wa madadin fayil ɗin.",
673 + "select_hw_account_below": "Da fatan za a zabi wanda asusun zai gyara a ƙasa:",
674 "select_sell_provider_notice": "Zaɓi mai bada siyarwa a sama. Kuna iya tsallake wannan allon ta saita mai bada siyar da ku a cikin saitunan app.",
675 "select_your_country": "Da fatan za a zabi ƙasarku",
676 "sell": "sayar",
res/values/strings_hi.arb
+1
@@ -670,6 +670,7 @@
670 "select_backup_file": "बैकअप फ़ाइल का चयन करें",
671 "select_buy_provider_notice": "ऊपर एक खरीद प्रदाता का चयन करें। आप इस स्क्रीन को ऐप सेटिंग्स में अपना डिफ़ॉल्ट बाय प्रदाता सेट करके छोड़ सकते हैं।",
672 "select_destination": "कृपया बैकअप फ़ाइल के लिए गंतव्य का चयन करें।",
673 + "select_hw_account_below": "कृपया नीचे पुनर्स्थापित करने के लिए कौन सा खाता चुनें:",
674 "select_sell_provider_notice": "ऊपर एक विक्रय प्रदाता का चयन करें। आप ऐप सेटिंग में अपना डिफ़ॉल्ट विक्रय प्रदाता सेट करके इस स्क्रीन को छोड़ सकते हैं।",
675 "select_your_country": "कृपया अपने देश का चयन करें",
676 "sell": "बेचना",
res/values/strings_hr.arb
+1
@@ -668,6 +668,7 @@
668 "select_backup_file": "Odaberite datoteku sigurnosne kopije",
669 "select_buy_provider_notice": "Odaberite gornji davatelj kupnje. Ovaj zaslon možete preskočiti postavljanjem zadanog davatelja usluga kupnje u postavkama aplikacija.",
670 "select_destination": "Odaberite odredište za datoteku sigurnosne kopije.",
671 + "select_hw_account_below": "Molimo odaberite koji će se račun vratiti u nastavku:",
672 "select_sell_provider_notice": "Gore odaberite pružatelja usluga prodaje. Ovaj zaslon možete preskočiti postavljanjem zadanog pružatelja usluga prodaje u postavkama aplikacije.",
673 "select_your_country": "Odaberite svoju zemlju",
674 "sell": "Prodavati",
res/values/strings_hy.arb
+1
@@ -668,6 +668,7 @@
668 "select_backup_file": "Ընտրել կրկնօրինակ ֆայլ",
669 "select_buy_provider_notice": "Ընտրեք գնման մատակարարը վերևում։ Դուք կարող եք բաց թողնել այս էկրանը ձեր լռելայն գնման մատակարարը հավելվածի կարգավորումներում սահմանելով",
670 "select_destination": "Խնդրում ենք ընտրել կրկնօրինակ ֆայլի նպատակակետը",
671 + "select_hw_account_below": "Խնդրում ենք ընտրել, թե որ հաշիվն է վերականգնել հետեւյալը.",
672 "select_sell_provider_notice": "Ընտրեք վաճառքի մատակարարը վերևում։ Դուք կարող եք բաց թողնել այս էկրանը ձեր լռելայն վաճառքի մատակարարը հավելվածի կարգավորումներում սահմանելով",
673 "select_your_country": "Խնդրում ենք ընտրել ձեր երկիրը",
674 "sell": "Ծախել",
res/values/strings_id.arb
+1
@@ -671,6 +671,7 @@
671 "select_backup_file": "Pilih file cadangan",
672 "select_buy_provider_notice": "Pilih penyedia beli di atas. Anda dapat melewatkan layar ini dengan mengatur penyedia pembelian default Anda di pengaturan aplikasi.",
673 "select_destination": "Silakan pilih tujuan untuk file cadangan.",
674 + "select_hw_account_below": "Pilih akun mana yang akan dikembalikan di bawah ini:",
675 "select_sell_provider_notice": "Pilih penyedia jual di atas. Anda dapat melewati layar ini dengan mengatur penyedia penjualan default Anda di pengaturan aplikasi.",
676 "select_your_country": "Pilih negara Anda",
677 "sell": "Jual",
res/values/strings_it.arb
+1
@@ -670,6 +670,7 @@
670 "select_backup_file": "Seleziona file di backup",
671 "select_buy_provider_notice": "Seleziona un fornitore di acquisto sopra. È possibile saltare questa schermata impostando il provider di acquisto predefinito nelle impostazioni dell'app.",
672 "select_destination": "Seleziona la destinazione per il file di backup.",
673 + "select_hw_account_below": "Seleziona quale account ripristina di seguito:",
674 "select_sell_provider_notice": "Seleziona un fornitore di vendita sopra. Puoi saltare questa schermata impostando il tuo fornitore di vendita predefinito nelle impostazioni dell'app.",
675 "select_your_country": "Seleziona il tuo paese",
676 "sell": "Vendere",
res/values/strings_ja.arb
+1
@@ -669,6 +669,7 @@
669 "select_backup_file": "バックアップファイルを選択",
670 "select_buy_provider_notice": "上記の購入プロバイダーを選択してください。デフォルトの購入プロバイダーをアプリ設定で設定して、この画面をスキップできます。",
671 "select_destination": "バックアップファイルの保存先を選択してください。",
672 + "select_hw_account_below": "以下に復元するアカウントを選択してください。",
673 "select_sell_provider_notice": "上記の販売プロバイダーを選択してください。アプリ設定でデフォルトの販売プロバイダーを設定することで、この画面をスキップできます。",
674 "select_your_country": "あなたの国を選択してください",
675 "sell": "売る",
res/values/strings_ko.arb
+1
@@ -668,6 +668,7 @@
668 "select_backup_file": "백업 파일 선택",
669 "select_buy_provider_notice": "위의 구매 제공자를 선택하십시오. 앱 설정에서 기본 구매 제공자를 설정 하여이 화면을 건너 뛸 수 있습니다.",
670 "select_destination": "백업 파일의 대상을 선택하십시오.",
671 + "select_hw_account_below": "아래를 복원 할 계정을 선택하십시오.",
672 "select_sell_provider_notice": "위에서 판매 공급자를 선택하세요. 앱 설정에서 기본 판매 공급자를 설정하면 이 화면을 건너뛸 수 있습니다.",
673 "select_your_country": "국가를 선택하십시오",
674 "sell": "팔다",
res/values/strings_my.arb
+1
@@ -668,6 +668,7 @@
668 "select_backup_file": "အရန်ဖိုင်ကို ရွေးပါ။",
669 "select_buy_provider_notice": "အပေါ်ကဝယ်သူတစ် ဦး ကိုရွေးချယ်ပါ။ သင်၏ default 0 ယ်သူအား app settings တွင် setting လုပ်ခြင်းဖြင့်ဤ screen ကိုကျော်သွားနိုင်သည်။",
670 "select_destination": "အရန်ဖိုင်အတွက် ဦးတည်ရာကို ရွေးပါ။",
671 + "select_hw_account_below": "အောက်ဖော်ပြပါမည်သည့်အကောင့်ကိုရွေးပါ။",
672 "select_sell_provider_notice": "အထက်ဖော်ပြပါ အရောင်းဝန်ဆောင်မှုပေးသူကို ရွေးပါ။ အက်ပ်ဆက်တင်များတွင် သင်၏မူလရောင်းချပေးသူကို သတ်မှတ်ခြင်းဖြင့် ဤစခရင်ကို ကျော်နိုင်သည်။",
673 "select_your_country": "ကျေးဇူးပြု. သင့်နိုင်ငံကိုရွေးချယ်ပါ",
674 "sell": "ရောင်း",
res/values/strings_nl.arb
+1
@@ -668,6 +668,7 @@
668 "select_backup_file": "Selecteer een back-upbestand",
669 "select_buy_provider_notice": "Selecteer hierboven een koopprovider. U kunt dit scherm overslaan door uw standaard kopenprovider in te stellen in app -instellingen.",
670 "select_destination": "Selecteer de bestemming voor het back-upbestand.",
671 + "select_hw_account_below": "Selecteer welk account u hieronder moet herstellen:",
672 "select_sell_provider_notice": "Selecteer hierboven een verkoopaanbieder. U kunt dit scherm overslaan door uw standaardverkoopprovider in te stellen in de app-instellingen.",
673 "select_your_country": "Selecteer uw land",
674 "sell": "Verkopen",
res/values/strings_pl.arb
+1
@@ -668,6 +668,7 @@
668 "select_backup_file": "Wybierz plik kopii zapasowej",
669 "select_buy_provider_notice": "Wybierz powyższe dostawcę zakupu. Możesz pominąć ten ekran, ustawiając domyślnego dostawcę zakupu w ustawieniach aplikacji.",
670 "select_destination": "Wybierz miejsce docelowe dla pliku kopii zapasowej.",
671 + "select_hw_account_below": "Wybierz, które konto przywrócić poniżej:",
672 "select_sell_provider_notice": "Wybierz dostawcę sprzedaży powyżej. Możesz pominąć ten ekran, ustawiając domyślnego dostawcę sprzedaży w ustawieniach aplikacji.",
673 "select_your_country": "Wybierz swój kraj",
674 "sell": "Sprzedać",
res/values/strings_pt.arb
+1
@@ -670,6 +670,7 @@
670 "select_backup_file": "Selecione o arquivo de backup",
671 "select_buy_provider_notice": "Selecione um provedor de compra acima. Você pode pular esta tela definindo seu provedor de compra padrão nas configurações de aplicativos.",
672 "select_destination": "Selecione o destino para o arquivo de backup.",
673 + "select_hw_account_below": "Selecione qual conta para restaurar abaixo:",
674 "select_sell_provider_notice": "Selecione um fornecedor de venda acima. Você pode pular esta tela definindo seu provedor de venda padrão nas configurações do aplicativo.",
675 "select_your_country": "Selecione seu país",
676 "sell": "Vender",
res/values/strings_ru.arb
+1
@@ -669,6 +669,7 @@
669 "select_backup_file": "Выберите файл резервной копии",
670 "select_buy_provider_notice": "Выберите поставщика покупки выше. Вы можете пропустить этот экран, установив поставщика покупки по умолчанию в настройках приложения.",
671 "select_destination": "Пожалуйста, выберите место для файла резервной копии.",
672 + "select_hw_account_below": "Пожалуйста, выберите, какую учетную запись восстановить ниже:",
673 "select_sell_provider_notice": "Выберите поставщика услуг продажи выше. Вы можете пропустить этот экран, установив поставщика услуг продаж по умолчанию в настройках приложения.",
674 "select_your_country": "Пожалуйста, выберите свою страну",
675 "sell": "Продавать",
res/values/strings_th.arb
+1
@@ -668,6 +668,7 @@
668 "select_backup_file": "เลือกไฟล์สำรอง",
669 "select_buy_provider_notice": "เลือกผู้ให้บริการซื้อด้านบน คุณสามารถข้ามหน้าจอนี้ได้โดยการตั้งค่าผู้ให้บริการซื้อเริ่มต้นในการตั้งค่าแอป",
670 "select_destination": "โปรดเลือกปลายทางสำหรับไฟล์สำรอง",
671 + "select_hw_account_below": "กรุณาเลือกบัญชีที่จะกู้คืนด้านล่าง:",
672 "select_sell_provider_notice": "เลือกผู้ให้บริการการขายด้านบน คุณสามารถข้ามหน้าจอนี้ได้โดยการตั้งค่าผู้ให้บริการการขายเริ่มต้นในการตั้งค่าแอป",
673 "select_your_country": "กรุณาเลือกประเทศของคุณ",
674 "sell": "ขาย",
res/values/strings_tl.arb
+1
@@ -668,6 +668,7 @@
668 "select_backup_file": "Piliin ang backup na file",
669 "select_buy_provider_notice": "Pumili ng provider ng pagbili sa itaas. Maaari mong laktawan ang screen na ito sa pamamagitan ng pagtatakda ng iyong default na provider ng pagbili sa mga setting ng app.",
670 "select_destination": "Mangyaring piliin ang patutunguhan para sa backup na file.",
671 + "select_hw_account_below": "Mangyaring piliin kung aling account ang ibabalik sa ibaba:",
672 "select_sell_provider_notice": "Pumili ng provider ng nagbebenta sa itaas. Maaari mong laktawan ang screen na ito sa pamamagitan ng pagtatakda ng iyong default na sell provider sa mga setting ng app.",
673 "select_your_country": "Mangyaring piliin ang iyong bansa",
674 "sell": "Ibenta",
res/values/strings_tr.arb
+1
@@ -668,6 +668,7 @@
668 "select_backup_file": "Yedek dosyası seç",
669 "select_buy_provider_notice": "Yukarıda bir satın alma sağlayıcısı seçin. App ayarlarında varsayılan satın alma sağlayıcınızı ayarlayarak bu ekranı atlayabilirsiniz.",
670 "select_destination": "Lütfen yedekleme dosyası için hedef seçin.",
671 + "select_hw_account_below": "Lütfen aşağıda hangi hesabı geri yükleyeceğinizi seçin:",
672 "select_sell_provider_notice": "Yukarıdan bir satış sağlayıcısı seçin. Uygulama ayarlarında varsayılan satış sağlayıcınızı ayarlayarak bu ekranı atlayabilirsiniz.",
673 "select_your_country": "Lütfen ülkenizi seçin",
674 "sell": "Satış",
res/values/strings_uk.arb
+1
@@ -669,6 +669,7 @@
669 "select_backup_file": "Виберіть файл резервної копії",
670 "select_buy_provider_notice": "Виберіть постачальника купівлі вище. Ви можете пропустити цей екран, встановивши свого постачальника купівлі за замовчуванням у налаштуваннях додатків.",
671 "select_destination": "Виберіть місце призначення для файлу резервної копії.",
672 + "select_hw_account_below": "Виберіть, який рахунок відновити нижче:",
673 "select_sell_provider_notice": "Виберіть вище постачальника послуг продажу. Ви можете пропустити цей екран, встановивши постачальника послуг продажу за умовчанням у налаштуваннях програми.",
674 "select_your_country": "Будь ласка, виберіть свою країну",
675 "sell": "Продати",
res/values/strings_ur.arb
+1
@@ -670,6 +670,7 @@
670 "select_backup_file": "بیک اپ فائل کو منتخب کریں۔",
671 "select_buy_provider_notice": "اوپر خریدنے والا خریدنے والا منتخب کریں۔ آپ ایپ کی ترتیبات میں اپنے پہلے سے طے شدہ خریدنے والے کو ترتیب دے کر اس اسکرین کو چھوڑ سکتے ہیں۔",
672 "select_destination": "۔ﮟﯾﺮﮐ ﺏﺎﺨﺘﻧﺍ ﺎﮐ ﻝﺰﻨﻣ ﮯﯿﻟ ﮯﮐ ﻞﺋﺎﻓ ﭖﺍ ﮏﯿﺑ ﻡﺮﮐ ﮦﺍﺮﺑ",
673 + "select_hw_account_below": "براہ کرم ذیل میں کون سا اکاؤنٹ بحال کرنا ہے منتخب کریں:",
674 "select_sell_provider_notice": "۔ﮟﯿﮨ ﮯﺘﮑﺳ ﮌﻮﮭﭼ ﻮﮐ ﻦﯾﺮﮑﺳﺍ ﺱﺍ ﺮﮐ ﮮﺩ ﺐﯿﺗﺮﺗ ﻮﮐ ﮦﺪﻨﻨﮐ ﻢﮨﺍﺮﻓ ﻞﯿﺳ ﭧﻟﺎﻔﯾﮈ ﮯﻨﭘﺍ ﮟﯿﻣ ﺕﺎﺒ",
675 "select_your_country": "براہ کرم اپنے ملک کو منتخب کریں",
676 "sell": "بیچنا",
res/values/strings_vi.arb
+1
@@ -667,6 +667,7 @@
667 "select_backup_file": "Chọn tệp sao lưu",
668 "select_buy_provider_notice": "Chọn nhà cung cấp mua ở trên. Bạn có thể bỏ qua màn hình này bằng cách thiết lập nhà cung cấp mua mặc định trong cài đặt ứng dụng.",
669 "select_destination": "Vui lòng chọn đích cho tệp sao lưu.",
670 + "select_hw_account_below": "Vui lòng chọn tài khoản nào để khôi phục bên dưới:",
671 "select_sell_provider_notice": "Chọn nhà cung cấp bán ở trên. Bạn có thể bỏ qua màn hình này bằng cách thiết lập nhà cung cấp bán mặc định trong cài đặt ứng dụng.",
672 "select_your_country": "Vui lòng chọn quốc gia của bạn",
673 "sell": "Bán",
res/values/strings_yo.arb
+1
@@ -669,6 +669,7 @@
669 "select_backup_file": "Select backup file",
670 "select_buy_provider_notice": "Yan olupese Ra loke. O le skii iboju yii nipa ṣiṣeto olupese rẹ ni awọn eto App.",
671 "select_destination": "Jọwọ yan ibi ti o nlo fun faili afẹyinti.",
672 + "select_hw_account_below": "Jọwọ yan iru iroyin lati mu pada ni isalẹ:",
673 "select_sell_provider_notice": "Yan olupese ti o ta loke. O le foju iboju yii nipa tito olupese iṣẹ tita aiyipada rẹ ni awọn eto app.",
674 "select_your_country": "Jọwọ yan orilẹ-ede rẹ",
675 "sell": "Tà",
res/values/strings_zh.arb
+1
@@ -668,6 +668,7 @@
668 "select_backup_file": "选择备份文件",
669 "select_buy_provider_notice": "在上面选择买入提供商。您可以通过在应用程序设置中设置默认的购买提供商来跳过此屏幕。",
670 "select_destination": "请选择备份文件的目的地。",
671 + "select_hw_account_below": "请在下面选择要还原的帐户:",
672 "select_sell_provider_notice": "选择上面的销售提供商。您可以通过在应用程序设置中设置默认销售提供商来跳过此屏幕。",
673 "select_your_country": "请选择你的国家",
674 "sell": "卖",