mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-06-28 20:39:51 +00:00
Merge branch 'main' into btc-addr-type
This commit is contained in:
commit
da9f0e4507
48 changed files with 473 additions and 114 deletions
|
@ -8,6 +8,7 @@
|
||||||
<uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
<uses-permission android:name="android.permission.CAMERA" />
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
|
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".Application"
|
android:name=".Application"
|
||||||
|
|
|
@ -15,6 +15,10 @@ import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.PowerManager;
|
||||||
|
import android.provider.Settings;
|
||||||
|
|
||||||
import com.unstoppabledomains.resolution.DomainResolution;
|
import com.unstoppabledomains.resolution.DomainResolution;
|
||||||
import com.unstoppabledomains.resolution.Resolution;
|
import com.unstoppabledomains.resolution.Resolution;
|
||||||
|
@ -65,6 +69,14 @@ public class MainActivity extends FlutterFragmentActivity {
|
||||||
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
|
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "disableBatteryOptimization":
|
||||||
|
disableBatteryOptimization();
|
||||||
|
handler.post(() -> result.success(null));
|
||||||
|
break;
|
||||||
|
case "isBatteryOptimizationDisabled":
|
||||||
|
boolean isDisabled = isBatteryOptimizationDisabled();
|
||||||
|
handler.post(() -> result.success(isDisabled));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
handler.post(() -> result.notImplemented());
|
handler.post(() -> result.notImplemented());
|
||||||
}
|
}
|
||||||
|
@ -89,4 +101,22 @@ public class MainActivity extends FlutterFragmentActivity {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void disableBatteryOptimization() {
|
||||||
|
String packageName = getPackageName();
|
||||||
|
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
|
||||||
|
if (!pm.isIgnoringBatteryOptimizations(packageName)) {
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
|
||||||
|
intent.setData(Uri.parse("package:" + packageName));
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isBatteryOptimizationDisabled() {
|
||||||
|
String packageName = getPackageName();
|
||||||
|
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
|
||||||
|
return pm.isIgnoringBatteryOptimizations(packageName);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -14,6 +14,10 @@ import android.os.Build;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.PowerManager;
|
||||||
|
import android.provider.Settings;
|
||||||
|
|
||||||
import com.unstoppabledomains.resolution.DomainResolution;
|
import com.unstoppabledomains.resolution.DomainResolution;
|
||||||
import com.unstoppabledomains.resolution.Resolution;
|
import com.unstoppabledomains.resolution.Resolution;
|
||||||
|
@ -55,6 +59,14 @@ public class MainActivity extends FlutterFragmentActivity {
|
||||||
handler.post(() -> result.success(""));
|
handler.post(() -> result.success(""));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "disableBatteryOptimization":
|
||||||
|
disableBatteryOptimization();
|
||||||
|
handler.post(() -> result.success(null));
|
||||||
|
break;
|
||||||
|
case "isBatteryOptimizationDisabled":
|
||||||
|
boolean isDisabled = isBatteryOptimizationDisabled();
|
||||||
|
handler.post(() -> result.success(isDisabled));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
handler.post(() -> result.notImplemented());
|
handler.post(() -> result.notImplemented());
|
||||||
}
|
}
|
||||||
|
@ -79,4 +91,22 @@ public class MainActivity extends FlutterFragmentActivity {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void disableBatteryOptimization() {
|
||||||
|
String packageName = getPackageName();
|
||||||
|
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
|
||||||
|
if (!pm.isIgnoringBatteryOptimizations(packageName)) {
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
|
||||||
|
intent.setData(Uri.parse("package:" + packageName));
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isBatteryOptimizationDisabled() {
|
||||||
|
String packageName = getPackageName();
|
||||||
|
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
|
||||||
|
return pm.isIgnoringBatteryOptimizations(packageName);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -14,6 +14,10 @@ import android.os.Build;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.PowerManager;
|
||||||
|
import android.provider.Settings;
|
||||||
|
|
||||||
import com.unstoppabledomains.resolution.DomainResolution;
|
import com.unstoppabledomains.resolution.DomainResolution;
|
||||||
import com.unstoppabledomains.resolution.Resolution;
|
import com.unstoppabledomains.resolution.Resolution;
|
||||||
|
@ -64,6 +68,14 @@ public class MainActivity extends FlutterFragmentActivity {
|
||||||
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
|
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "disableBatteryOptimization":
|
||||||
|
disableBatteryOptimization();
|
||||||
|
handler.post(() -> result.success(null));
|
||||||
|
break;
|
||||||
|
case "isBatteryOptimizationDisabled":
|
||||||
|
boolean isDisabled = isBatteryOptimizationDisabled();
|
||||||
|
handler.post(() -> result.success(isDisabled));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
handler.post(() -> result.notImplemented());
|
handler.post(() -> result.notImplemented());
|
||||||
}
|
}
|
||||||
|
@ -88,4 +100,22 @@ public class MainActivity extends FlutterFragmentActivity {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void disableBatteryOptimization() {
|
||||||
|
String packageName = getPackageName();
|
||||||
|
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
|
||||||
|
if (!pm.isIgnoringBatteryOptimizations(packageName)) {
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
|
||||||
|
intent.setData(Uri.parse("package:" + packageName));
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isBatteryOptimizationDisabled() {
|
||||||
|
String packageName = getPackageName();
|
||||||
|
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
|
||||||
|
return pm.isIgnoringBatteryOptimizations(packageName);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -40,7 +40,7 @@ class BitcoinAddressRecord {
|
||||||
bool operator ==(Object o) => o is BitcoinAddressRecord && address == o.address;
|
bool operator ==(Object o) => o is BitcoinAddressRecord && address == o.address;
|
||||||
|
|
||||||
final String address;
|
final String address;
|
||||||
final bool isHidden;
|
bool isHidden;
|
||||||
final int index;
|
final int index;
|
||||||
int _txCount;
|
int _txCount;
|
||||||
int _balance;
|
int _balance;
|
||||||
|
|
|
@ -45,13 +45,25 @@ class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
|
||||||
Future<BitcoinWallet> openWallet(String name, String password) async {
|
Future<BitcoinWallet> openWallet(String name, String password) async {
|
||||||
final walletInfo = walletInfoSource.values
|
final walletInfo = walletInfoSource.values
|
||||||
.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
|
.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
|
||||||
final wallet = await BitcoinWalletBase.open(
|
try {
|
||||||
password: password,
|
final wallet = await BitcoinWalletBase.open(
|
||||||
name: name,
|
password: password,
|
||||||
walletInfo: walletInfo,
|
name: name,
|
||||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
walletInfo: walletInfo,
|
||||||
await wallet.init();
|
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||||
return wallet;
|
await wallet.init();
|
||||||
|
saveBackup(name);
|
||||||
|
return wallet;
|
||||||
|
} catch (_) {
|
||||||
|
await restoreWalletFilesFromBackup(name);
|
||||||
|
final wallet = await BitcoinWalletBase.open(
|
||||||
|
password: password,
|
||||||
|
name: name,
|
||||||
|
walletInfo: walletInfo,
|
||||||
|
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||||
|
await wallet.init();
|
||||||
|
return wallet;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -73,6 +85,7 @@ class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
|
||||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||||
|
|
||||||
await currentWallet.renameWalletFiles(newName);
|
await currentWallet.renameWalletFiles(newName);
|
||||||
|
await saveBackup(newName);
|
||||||
|
|
||||||
final newWalletInfo = currentWalletInfo;
|
final newWalletInfo = currentWalletInfo;
|
||||||
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
||||||
|
|
|
@ -275,7 +275,13 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
List<BitcoinAddressRecord> addrs;
|
List<BitcoinAddressRecord> addrs;
|
||||||
final matchingAddresses = _addresses.where((addr) => _addressMatchHidden(addr, isHidden, type));
|
final matchingAddresses = _addresses.where((addr) => _addressMatchHidden(addr, isHidden, type));
|
||||||
|
|
||||||
if (matchingAddresses.isNotEmpty) {
|
if (addresses.isNotEmpty) {
|
||||||
|
if (!isHidden) {
|
||||||
|
final receiveAddressesList =
|
||||||
|
_addresses.where((addr) => _addressMatchHidden(addr, isHidden, type)).toList();
|
||||||
|
validateSideHdAddresses(receiveAddressesList);
|
||||||
|
}
|
||||||
|
|
||||||
addrs = matchingAddresses.toList();
|
addrs = matchingAddresses.toList();
|
||||||
} else {
|
} else {
|
||||||
addrs = await _createNewAddresses(
|
addrs = await _createNewAddresses(
|
||||||
|
@ -365,6 +371,14 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
return transactionHistory.isNotEmpty;
|
return transactionHistory.isNotEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void validateSideHdAddresses(List<BitcoinAddressRecord> addrWithTransactions) {
|
||||||
|
addrWithTransactions.forEach((element) {
|
||||||
|
if (element.address !=
|
||||||
|
getAddress(index: element.index, hd: mainHd, addressType: element.type))
|
||||||
|
element.isHidden = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
Future<void> setAddressType(BitcoinAddressType type) async {
|
Future<void> setAddressType(BitcoinAddressType type) async {
|
||||||
_addressPageType = type;
|
_addressPageType = type;
|
||||||
|
@ -375,8 +389,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
}
|
}
|
||||||
|
|
||||||
bitcoin.HDWallet _getHd(bool isHidden) => isHidden ? sideHd : mainHd;
|
bitcoin.HDWallet _getHd(bool isHidden) => isHidden ? sideHd : mainHd;
|
||||||
bool _isAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) =>
|
bool _isAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) => addr.type == type;
|
||||||
addr.type == type || (addr.type == null && type == SegwitAddresType.p2wpkh);
|
|
||||||
bool _addressMatchHidden(BitcoinAddressRecord addr, bool isHidden, BitcoinAddressType type) =>
|
bool _addressMatchHidden(BitcoinAddressRecord addr, bool isHidden, BitcoinAddressType type) =>
|
||||||
_isAddressByType(addr, type) && addr.isHidden == isHidden;
|
_isAddressByType(addr, type) && addr.isHidden == isHidden;
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,11 +45,22 @@ class LitecoinWalletService extends WalletService<
|
||||||
Future<LitecoinWallet> openWallet(String name, String password) async {
|
Future<LitecoinWallet> openWallet(String name, String password) async {
|
||||||
final walletInfo = walletInfoSource.values.firstWhereOrNull(
|
final walletInfo = walletInfoSource.values.firstWhereOrNull(
|
||||||
(info) => info.id == WalletBase.idFor(name, getType()))!;
|
(info) => info.id == WalletBase.idFor(name, getType()))!;
|
||||||
final wallet = await LitecoinWalletBase.open(
|
|
||||||
password: password, name: name, walletInfo: walletInfo,
|
try {
|
||||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
final wallet = await LitecoinWalletBase.open(
|
||||||
await wallet.init();
|
password: password, name: name, walletInfo: walletInfo,
|
||||||
return wallet;
|
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||||
|
await wallet.init();
|
||||||
|
saveBackup(name);
|
||||||
|
return wallet;
|
||||||
|
} catch (_) {
|
||||||
|
await restoreWalletFilesFromBackup(name);
|
||||||
|
final wallet = await LitecoinWalletBase.open(
|
||||||
|
password: password, name: name, walletInfo: walletInfo,
|
||||||
|
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||||
|
await wallet.init();
|
||||||
|
return wallet;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -72,6 +83,7 @@ class LitecoinWalletService extends WalletService<
|
||||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||||
|
|
||||||
await currentWallet.renameWalletFiles(newName);
|
await currentWallet.renameWalletFiles(newName);
|
||||||
|
await saveBackup(newName);
|
||||||
|
|
||||||
final newWalletInfo = currentWalletInfo;
|
final newWalletInfo = currentWalletInfo;
|
||||||
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
||||||
|
|
|
@ -51,11 +51,22 @@ class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredent
|
||||||
Future<BitcoinCashWallet> openWallet(String name, String password) async {
|
Future<BitcoinCashWallet> openWallet(String name, String password) async {
|
||||||
final walletInfo = walletInfoSource.values.firstWhereOrNull(
|
final walletInfo = walletInfoSource.values.firstWhereOrNull(
|
||||||
(info) => info.id == WalletBase.idFor(name, getType()))!;
|
(info) => info.id == WalletBase.idFor(name, getType()))!;
|
||||||
final wallet = await BitcoinCashWalletBase.open(
|
|
||||||
password: password, name: name, walletInfo: walletInfo,
|
try {
|
||||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
final wallet = await BitcoinCashWalletBase.open(
|
||||||
await wallet.init();
|
password: password, name: name, walletInfo: walletInfo,
|
||||||
return wallet;
|
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||||
|
await wallet.init();
|
||||||
|
saveBackup(name);
|
||||||
|
return wallet;
|
||||||
|
} catch(_) {
|
||||||
|
await restoreWalletFilesFromBackup(name);
|
||||||
|
final wallet = await BitcoinCashWalletBase.open(
|
||||||
|
password: password, name: name, walletInfo: walletInfo,
|
||||||
|
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||||
|
await wallet.init();
|
||||||
|
return wallet;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -78,6 +89,7 @@ class BitcoinCashWalletService extends WalletService<BitcoinCashNewWalletCredent
|
||||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||||
|
|
||||||
await currentWallet.renameWalletFiles(newName);
|
await currentWallet.renameWalletFiles(newName);
|
||||||
|
await saveBackup(newName);
|
||||||
|
|
||||||
final newWalletInfo = currentWalletInfo;
|
final newWalletInfo = currentWalletInfo;
|
||||||
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
||||||
|
|
22
cw_core/lib/battery_optimization_native.dart
Normal file
22
cw_core/lib/battery_optimization_native.dart
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
const MethodChannel _channel = MethodChannel('com.cake_wallet/native_utils');
|
||||||
|
|
||||||
|
Future<void> requestDisableBatteryOptimization() async {
|
||||||
|
try {
|
||||||
|
await _channel.invokeMethod('disableBatteryOptimization');
|
||||||
|
} on PlatformException catch (e) {
|
||||||
|
print("Failed to disable battery optimization: '${e.message}'.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> isBatteryOptimizationDisabled() async {
|
||||||
|
try {
|
||||||
|
final bool isDisabled = await _channel.invokeMethod('isBatteryOptimizationDisabled') as bool;
|
||||||
|
print('It\'s actually disabled? $isDisabled');
|
||||||
|
return isDisabled;
|
||||||
|
} on PlatformException catch (e) {
|
||||||
|
print("Failed to check battery optimization status: '${e.message}'.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -54,6 +54,17 @@ Future<void> restoreWalletFiles(String name) async {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> resetCache(String name) async {
|
||||||
|
await removeCache(name);
|
||||||
|
|
||||||
|
final walletDirPath = await pathForWalletDir(name: name, type: WalletType.monero);
|
||||||
|
final cacheFilePath = '$walletDirPath/$name';
|
||||||
|
final backupCacheFile = File(backupFileName(cacheFilePath));
|
||||||
|
if (backupCacheFile.existsSync()) {
|
||||||
|
await backupCacheFile.copy(cacheFilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<bool> backupWalletFilesExists(String name) async {
|
Future<bool> backupWalletFilesExists(String name) async {
|
||||||
final walletDirPath = await pathForWalletDir(name: name, type: WalletType.monero);
|
final walletDirPath = await pathForWalletDir(name: name, type: WalletType.monero);
|
||||||
final cacheFilePath = '$walletDirPath/$name';
|
final cacheFilePath = '$walletDirPath/$name';
|
||||||
|
@ -63,9 +74,9 @@ Future<bool> backupWalletFilesExists(String name) async {
|
||||||
final backupKeysFile = File(backupFileName(keysFilePath));
|
final backupKeysFile = File(backupFileName(keysFilePath));
|
||||||
final backupAddressListFile = File(backupFileName(addressListFilePath));
|
final backupAddressListFile = File(backupFileName(addressListFilePath));
|
||||||
|
|
||||||
return backupCacheFile.existsSync()
|
return backupCacheFile.existsSync() &&
|
||||||
&& backupKeysFile.existsSync()
|
backupKeysFile.existsSync() &&
|
||||||
&& backupAddressListFile.existsSync();
|
backupAddressListFile.existsSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> removeCache(String name) async {
|
Future<void> removeCache(String name) async {
|
||||||
|
@ -85,4 +96,4 @@ Future<void> restoreOrResetWalletFiles(String name) async {
|
||||||
}
|
}
|
||||||
|
|
||||||
removeCache(name);
|
removeCache(name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:cw_core/pathForWallet.dart';
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:cw_core/wallet_credentials.dart';
|
import 'package:cw_core/wallet_credentials.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
|
@ -19,4 +22,22 @@ abstract class WalletService<N extends WalletCredentials, RFS extends WalletCred
|
||||||
Future<void> remove(String wallet);
|
Future<void> remove(String wallet);
|
||||||
|
|
||||||
Future<void> rename(String currentName, String password, String newName);
|
Future<void> rename(String currentName, String password, String newName);
|
||||||
|
|
||||||
|
Future<void> restoreWalletFilesFromBackup(String name) async {
|
||||||
|
final backupWalletDirPath = await pathForWalletDir(name: "$name.backup", type: getType());
|
||||||
|
final walletDirPath = await pathForWalletDir(name: name, type: getType());
|
||||||
|
|
||||||
|
if (File(backupWalletDirPath).existsSync()) {
|
||||||
|
await File(backupWalletDirPath).copy(walletDirPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> saveBackup(String name) async {
|
||||||
|
final backupWalletDirPath = await pathForWalletDir(name: "$name.backup", type: getType());
|
||||||
|
final walletDirPath = await pathForWalletDir(name: name, type: getType());
|
||||||
|
|
||||||
|
if (File(walletDirPath).existsSync()) {
|
||||||
|
await File(walletDirPath).copy(backupWalletDirPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,16 +39,31 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
|
||||||
Future<EthereumWallet> openWallet(String name, String password) async {
|
Future<EthereumWallet> openWallet(String name, String password) async {
|
||||||
final walletInfo =
|
final walletInfo =
|
||||||
walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
|
walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
|
||||||
final wallet = await EthereumWallet.open(
|
|
||||||
name: name,
|
|
||||||
password: password,
|
|
||||||
walletInfo: walletInfo,
|
|
||||||
);
|
|
||||||
|
|
||||||
await wallet.init();
|
try {
|
||||||
await wallet.save();
|
final wallet = await EthereumWallet.open(
|
||||||
|
name: name,
|
||||||
|
password: password,
|
||||||
|
walletInfo: walletInfo,
|
||||||
|
);
|
||||||
|
|
||||||
return wallet;
|
await wallet.init();
|
||||||
|
await wallet.save();
|
||||||
|
saveBackup(name);
|
||||||
|
return wallet;
|
||||||
|
} catch (_) {
|
||||||
|
|
||||||
|
await restoreWalletFilesFromBackup(name);
|
||||||
|
|
||||||
|
final wallet = await EthereumWallet.open(
|
||||||
|
name: name,
|
||||||
|
password: password,
|
||||||
|
walletInfo: walletInfo,
|
||||||
|
);
|
||||||
|
await wallet.init();
|
||||||
|
await wallet.save();
|
||||||
|
return wallet;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -59,6 +74,7 @@ class EthereumWalletService extends EVMChainWalletService<EthereumWallet> {
|
||||||
password: password, name: currentName, walletInfo: currentWalletInfo);
|
password: password, name: currentName, walletInfo: currentWalletInfo);
|
||||||
|
|
||||||
await currentWallet.renameWalletFiles(newName);
|
await currentWallet.renameWalletFiles(newName);
|
||||||
|
await saveBackup(newName);
|
||||||
|
|
||||||
final newWalletInfo = currentWalletInfo;
|
final newWalletInfo = currentWalletInfo;
|
||||||
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
||||||
|
|
|
@ -163,6 +163,7 @@ class HavenWalletService extends WalletService<
|
||||||
final currentWallet = HavenWallet(walletInfo: currentWalletInfo);
|
final currentWallet = HavenWallet(walletInfo: currentWalletInfo);
|
||||||
|
|
||||||
await currentWallet.renameWalletFiles(newName);
|
await currentWallet.renameWalletFiles(newName);
|
||||||
|
await saveBackup(newName);
|
||||||
|
|
||||||
final newWalletInfo = currentWalletInfo;
|
final newWalletInfo = currentWalletInfo;
|
||||||
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
||||||
|
|
|
@ -36,6 +36,8 @@ import 'package:mobx/mobx.dart';
|
||||||
part 'monero_wallet.g.dart';
|
part 'monero_wallet.g.dart';
|
||||||
|
|
||||||
const moneroBlockSize = 1000;
|
const moneroBlockSize = 1000;
|
||||||
|
// not sure if this should just be 0 but setting it higher feels safer / should catch more cases:
|
||||||
|
const MIN_RESTORE_HEIGHT = 1000;
|
||||||
|
|
||||||
class MoneroWallet = MoneroWalletBase with _$MoneroWallet;
|
class MoneroWallet = MoneroWalletBase with _$MoneroWallet;
|
||||||
|
|
||||||
|
@ -79,7 +81,7 @@ abstract class MoneroWalletBase
|
||||||
|
|
||||||
Box<UnspentCoinsInfo> unspentCoinsInfo;
|
Box<UnspentCoinsInfo> unspentCoinsInfo;
|
||||||
|
|
||||||
void Function(FlutterErrorDetails)? _onError;
|
void Function(FlutterErrorDetails)? onError;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
late MoneroWalletAddresses walletAddresses;
|
late MoneroWalletAddresses walletAddresses;
|
||||||
|
@ -171,7 +173,26 @@ abstract class MoneroWalletBase
|
||||||
Future<void> startSync() async {
|
Future<void> startSync() async {
|
||||||
try {
|
try {
|
||||||
_setInitialHeight();
|
_setInitialHeight();
|
||||||
} catch (_) {}
|
} catch (_) {
|
||||||
|
// our restore height wasn't correct, so lets see if using the backup works:
|
||||||
|
try {
|
||||||
|
await resetCache(name);
|
||||||
|
_setInitialHeight();
|
||||||
|
} catch (e) {
|
||||||
|
// we still couldn't get a valid height from the backup?!:
|
||||||
|
// try to use the date instead:
|
||||||
|
try {
|
||||||
|
_setHeightFromDate();
|
||||||
|
} catch (e, s) {
|
||||||
|
// we still couldn't get a valid sync height :/
|
||||||
|
onError?.call(FlutterErrorDetails(
|
||||||
|
exception: e,
|
||||||
|
stack: s,
|
||||||
|
library: this.runtimeType.toString(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
syncStatus = AttemptingSyncStatus();
|
syncStatus = AttemptingSyncStatus();
|
||||||
|
@ -339,6 +360,8 @@ abstract class MoneroWalletBase
|
||||||
if (currentAddressListFile.existsSync()) {
|
if (currentAddressListFile.existsSync()) {
|
||||||
await currentAddressListFile.rename('$newWalletPath.address.txt');
|
await currentAddressListFile.rename('$newWalletPath.address.txt');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await backupWalletFiles(newWalletName);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
final currentWalletPath = await pathForWallet(name: name, type: type);
|
final currentWalletPath = await pathForWallet(name: name, type: type);
|
||||||
|
|
||||||
|
@ -402,9 +425,7 @@ abstract class MoneroWalletBase
|
||||||
if (coin.spent == 0) {
|
if (coin.spent == 0) {
|
||||||
final unspent = MoneroUnspent.fromCoinsInfoRow(coin);
|
final unspent = MoneroUnspent.fromCoinsInfoRow(coin);
|
||||||
if (unspent.hash.isNotEmpty) {
|
if (unspent.hash.isNotEmpty) {
|
||||||
unspent.isChange = transaction_history
|
unspent.isChange = transaction_history.getTransaction(unspent.hash).direction == 1;
|
||||||
.getTransaction(unspent.hash)
|
|
||||||
.direction == 1;
|
|
||||||
}
|
}
|
||||||
unspentCoins.add(unspent);
|
unspentCoins.add(unspent);
|
||||||
}
|
}
|
||||||
|
@ -418,7 +439,7 @@ abstract class MoneroWalletBase
|
||||||
if (unspentCoins.isNotEmpty) {
|
if (unspentCoins.isNotEmpty) {
|
||||||
unspentCoins.forEach((coin) {
|
unspentCoins.forEach((coin) {
|
||||||
final coinInfoList = unspentCoinsInfo.values.where((element) =>
|
final coinInfoList = unspentCoinsInfo.values.where((element) =>
|
||||||
element.walletId.contains(id) &&
|
element.walletId.contains(id) &&
|
||||||
element.accountIndex == walletAddresses.account!.id &&
|
element.accountIndex == walletAddresses.account!.id &&
|
||||||
element.keyImage!.contains(coin.keyImage!));
|
element.keyImage!.contains(coin.keyImage!));
|
||||||
|
|
||||||
|
@ -438,7 +459,7 @@ abstract class MoneroWalletBase
|
||||||
_askForUpdateBalance();
|
_askForUpdateBalance();
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
print(e.toString());
|
print(e.toString());
|
||||||
_onError?.call(FlutterErrorDetails(
|
onError?.call(FlutterErrorDetails(
|
||||||
exception: e,
|
exception: e,
|
||||||
stack: s,
|
stack: s,
|
||||||
library: this.runtimeType.toString(),
|
library: this.runtimeType.toString(),
|
||||||
|
@ -534,18 +555,36 @@ abstract class MoneroWalletBase
|
||||||
_listener = monero_wallet.setListeners(_onNewBlock, _onNewTransaction);
|
_listener = monero_wallet.setListeners(_onNewBlock, _onNewTransaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if the height is correct:
|
||||||
void _setInitialHeight() {
|
void _setInitialHeight() {
|
||||||
if (walletInfo.isRecovery) {
|
if (walletInfo.isRecovery) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final currentHeight = monero_wallet.getCurrentHeight();
|
final height = monero_wallet.getCurrentHeight();
|
||||||
|
|
||||||
if (currentHeight <= 1) {
|
if (height > MIN_RESTORE_HEIGHT) {
|
||||||
final height = _getHeightByDate(walletInfo.date);
|
// the restore height is probably correct, so we do nothing:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exception("height isn't > $MIN_RESTORE_HEIGHT!");
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setHeightFromDate() {
|
||||||
|
if (walletInfo.isRecovery) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final height = _getHeightByDate(walletInfo.date);
|
||||||
|
|
||||||
|
if (height > MIN_RESTORE_HEIGHT) {
|
||||||
monero_wallet.setRecoveringFromSeed(isRecovery: true);
|
monero_wallet.setRecoveringFromSeed(isRecovery: true);
|
||||||
monero_wallet.setRefreshFromBlockHeight(height: height);
|
monero_wallet.setRefreshFromBlockHeight(height: height);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw Exception("height isn't > $MIN_RESTORE_HEIGHT!");
|
||||||
}
|
}
|
||||||
|
|
||||||
int _getHeightDistance(DateTime date) {
|
int _getHeightDistance(DateTime date) {
|
||||||
|
@ -561,7 +600,8 @@ abstract class MoneroWalletBase
|
||||||
final heightDistance = _getHeightDistance(date);
|
final heightDistance = _getHeightDistance(date);
|
||||||
|
|
||||||
if (nodeHeight <= 0) {
|
if (nodeHeight <= 0) {
|
||||||
return 0;
|
// the node returned 0 (an error state), so lets just restore from cache:
|
||||||
|
throw Exception("nodeHeight is <= 0!");
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodeHeight - heightDistance;
|
return nodeHeight - heightDistance;
|
||||||
|
@ -650,7 +690,7 @@ abstract class MoneroWalletBase
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void setExceptionHandler(void Function(FlutterErrorDetails) onError) => _onError = onError;
|
void setExceptionHandler(void Function(FlutterErrorDetails) e) => onError = e;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String signMessage(String message, {String? address}) {
|
String signMessage(String message, {String? address}) {
|
||||||
|
|
|
@ -11,11 +11,13 @@ import 'package:cw_core/get_height_by_date.dart';
|
||||||
import 'package:cw_monero/api/exceptions/wallet_opening_exception.dart';
|
import 'package:cw_monero/api/exceptions/wallet_opening_exception.dart';
|
||||||
import 'package:cw_monero/api/wallet_manager.dart' as monero_wallet_manager;
|
import 'package:cw_monero/api/wallet_manager.dart' as monero_wallet_manager;
|
||||||
import 'package:cw_monero/monero_wallet.dart';
|
import 'package:cw_monero/monero_wallet.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:polyseed/polyseed.dart';
|
import 'package:polyseed/polyseed.dart';
|
||||||
|
|
||||||
class MoneroNewWalletCredentials extends WalletCredentials {
|
class MoneroNewWalletCredentials extends WalletCredentials {
|
||||||
MoneroNewWalletCredentials({required String name, required this.language, required this.isPolyseed, String? password})
|
MoneroNewWalletCredentials(
|
||||||
|
{required String name, required this.language, required this.isPolyseed, String? password})
|
||||||
: super(name: name, password: password);
|
: super(name: name, password: password);
|
||||||
|
|
||||||
final String language;
|
final String language;
|
||||||
|
@ -52,10 +54,8 @@ class MoneroRestoreWalletFromKeysCredentials extends WalletCredentials {
|
||||||
final String spendKey;
|
final String spendKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
class MoneroWalletService extends WalletService<
|
class MoneroWalletService extends WalletService<MoneroNewWalletCredentials,
|
||||||
MoneroNewWalletCredentials,
|
MoneroRestoreWalletFromSeedCredentials, MoneroRestoreWalletFromKeysCredentials> {
|
||||||
MoneroRestoreWalletFromSeedCredentials,
|
|
||||||
MoneroRestoreWalletFromKeysCredentials> {
|
|
||||||
MoneroWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
MoneroWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
||||||
|
|
||||||
final Box<WalletInfo> walletInfoSource;
|
final Box<WalletInfo> walletInfoSource;
|
||||||
|
@ -112,6 +112,7 @@ class MoneroWalletService extends WalletService<
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<MoneroWallet> openWallet(String name, String password) async {
|
Future<MoneroWallet> openWallet(String name, String password) async {
|
||||||
|
MoneroWallet? wallet;
|
||||||
try {
|
try {
|
||||||
final path = await pathForWallet(name: name, type: getType());
|
final path = await pathForWallet(name: name, type: getType());
|
||||||
|
|
||||||
|
@ -119,11 +120,10 @@ class MoneroWalletService extends WalletService<
|
||||||
await repairOldAndroidWallet(name);
|
await repairOldAndroidWallet(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
await monero_wallet_manager
|
await monero_wallet_manager.openWalletAsync({'path': path, 'password': password});
|
||||||
.openWalletAsync({'path': path, 'password': password});
|
final walletInfo = walletInfoSource.values
|
||||||
final walletInfo = walletInfoSource.values.firstWhere(
|
.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
|
||||||
(info) => info.id == WalletBase.idFor(name, getType()));
|
wallet = MoneroWallet(walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource);
|
||||||
final wallet = MoneroWallet(walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource);
|
|
||||||
final isValid = wallet.walletAddresses.validate();
|
final isValid = wallet.walletAddresses.validate();
|
||||||
|
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
|
@ -135,7 +135,7 @@ class MoneroWalletService extends WalletService<
|
||||||
await wallet.init();
|
await wallet.init();
|
||||||
|
|
||||||
return wallet;
|
return wallet;
|
||||||
} catch (e) {
|
} catch (e, s) {
|
||||||
// TODO: Implement Exception for wallet list service.
|
// TODO: Implement Exception for wallet list service.
|
||||||
|
|
||||||
final bool isBadAlloc = e.toString().contains('bad_alloc') ||
|
final bool isBadAlloc = e.toString().contains('bad_alloc') ||
|
||||||
|
@ -156,16 +156,18 @@ class MoneroWalletService extends WalletService<
|
||||||
final bool invalidSignature = e.toString().contains('invalid signature') ||
|
final bool invalidSignature = e.toString().contains('invalid signature') ||
|
||||||
(e is WalletOpeningException && e.message.contains('invalid signature'));
|
(e is WalletOpeningException && e.message.contains('invalid signature'));
|
||||||
|
|
||||||
if (isBadAlloc ||
|
if (!isBadAlloc &&
|
||||||
doesNotCorrespond ||
|
!doesNotCorrespond &&
|
||||||
isMissingCacheFilesIOS ||
|
!isMissingCacheFilesIOS &&
|
||||||
isMissingCacheFilesAndroid ||
|
!isMissingCacheFilesAndroid &&
|
||||||
invalidSignature) {
|
!invalidSignature &&
|
||||||
await restoreOrResetWalletFiles(name);
|
wallet != null &&
|
||||||
return openWallet(name, password);
|
wallet.onError != null) {
|
||||||
|
wallet.onError!(FlutterErrorDetails(exception: e, stack: s));
|
||||||
}
|
}
|
||||||
|
|
||||||
rethrow;
|
await restoreOrResetWalletFiles(name);
|
||||||
|
return openWallet(name, password);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,10 +187,9 @@ class MoneroWalletService extends WalletService<
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> rename(
|
Future<void> rename(String currentName, String password, String newName) async {
|
||||||
String currentName, String password, String newName) async {
|
final currentWalletInfo = walletInfoSource.values
|
||||||
final currentWalletInfo = walletInfoSource.values.firstWhere(
|
.firstWhere((info) => info.id == WalletBase.idFor(currentName, getType()));
|
||||||
(info) => info.id == WalletBase.idFor(currentName, getType()));
|
|
||||||
final currentWallet =
|
final currentWallet =
|
||||||
MoneroWallet(walletInfo: currentWalletInfo, unspentCoinsInfo: unspentCoinsInfoSource);
|
MoneroWallet(walletInfo: currentWalletInfo, unspentCoinsInfo: unspentCoinsInfoSource);
|
||||||
|
|
||||||
|
@ -202,8 +203,8 @@ class MoneroWalletService extends WalletService<
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<MoneroWallet> restoreFromKeys(
|
Future<MoneroWallet> restoreFromKeys(MoneroRestoreWalletFromKeysCredentials credentials,
|
||||||
MoneroRestoreWalletFromKeysCredentials credentials, {bool? isTestnet}) async {
|
{bool? isTestnet}) async {
|
||||||
try {
|
try {
|
||||||
final path = await pathForWallet(name: credentials.name, type: getType());
|
final path = await pathForWallet(name: credentials.name, type: getType());
|
||||||
await monero_wallet_manager.restoreFromKeys(
|
await monero_wallet_manager.restoreFromKeys(
|
||||||
|
@ -227,9 +228,8 @@ class MoneroWalletService extends WalletService<
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<MoneroWallet> restoreFromSeed(
|
Future<MoneroWallet> restoreFromSeed(MoneroRestoreWalletFromSeedCredentials credentials,
|
||||||
MoneroRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async {
|
{bool? isTestnet}) async {
|
||||||
|
|
||||||
// Restore from Polyseed
|
// Restore from Polyseed
|
||||||
if (Polyseed.isValidSeed(credentials.mnemonic)) {
|
if (Polyseed.isValidSeed(credentials.mnemonic)) {
|
||||||
return restoreFromPolyseed(credentials);
|
return restoreFromPolyseed(credentials);
|
||||||
|
@ -254,14 +254,16 @@ class MoneroWalletService extends WalletService<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<MoneroWallet> restoreFromPolyseed(MoneroRestoreWalletFromSeedCredentials credentials) async {
|
Future<MoneroWallet> restoreFromPolyseed(
|
||||||
|
MoneroRestoreWalletFromSeedCredentials credentials) async {
|
||||||
try {
|
try {
|
||||||
final path = await pathForWallet(name: credentials.name, type: getType());
|
final path = await pathForWallet(name: credentials.name, type: getType());
|
||||||
final polyseedCoin = PolyseedCoin.POLYSEED_MONERO;
|
final polyseedCoin = PolyseedCoin.POLYSEED_MONERO;
|
||||||
final lang = PolyseedLang.getByPhrase(credentials.mnemonic);
|
final lang = PolyseedLang.getByPhrase(credentials.mnemonic);
|
||||||
final polyseed = Polyseed.decode(credentials.mnemonic, lang, polyseedCoin);
|
final polyseed = Polyseed.decode(credentials.mnemonic, lang, polyseedCoin);
|
||||||
|
|
||||||
return _restoreFromPolyseed(path, credentials.password!, polyseed, credentials.walletInfo!, lang);
|
return _restoreFromPolyseed(
|
||||||
|
path, credentials.password!, polyseed, credentials.walletInfo!, lang);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// TODO: Implement Exception for wallet list service.
|
// TODO: Implement Exception for wallet list service.
|
||||||
print('MoneroWalletsManager Error: $e');
|
print('MoneroWalletsManager Error: $e');
|
||||||
|
@ -269,11 +271,11 @@ class MoneroWalletService extends WalletService<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<MoneroWallet> _restoreFromPolyseed(String path, String password, Polyseed polyseed,
|
Future<MoneroWallet> _restoreFromPolyseed(
|
||||||
WalletInfo walletInfo, PolyseedLang lang,
|
String path, String password, Polyseed polyseed, WalletInfo walletInfo, PolyseedLang lang,
|
||||||
{PolyseedCoin coin = PolyseedCoin.POLYSEED_MONERO, int? overrideHeight}) async {
|
{PolyseedCoin coin = PolyseedCoin.POLYSEED_MONERO, int? overrideHeight}) async {
|
||||||
final height = overrideHeight ?? getMoneroHeigthByDate(
|
final height = overrideHeight ??
|
||||||
date: DateTime.fromMillisecondsSinceEpoch(polyseed.birthday * 1000));
|
getMoneroHeigthByDate(date: DateTime.fromMillisecondsSinceEpoch(polyseed.birthday * 1000));
|
||||||
final spendKey = polyseed.generateKey(coin, 32).toHexString();
|
final spendKey = polyseed.generateKey(coin, 32).toHexString();
|
||||||
final seed = polyseed.encode(lang, coin);
|
final seed = polyseed.encode(lang, coin);
|
||||||
|
|
||||||
|
@ -288,8 +290,7 @@ class MoneroWalletService extends WalletService<
|
||||||
restoreHeight: height,
|
restoreHeight: height,
|
||||||
spendKey: spendKey);
|
spendKey: spendKey);
|
||||||
|
|
||||||
final wallet = MoneroWallet(
|
final wallet = MoneroWallet(walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource);
|
||||||
walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource);
|
|
||||||
await wallet.init();
|
await wallet.init();
|
||||||
|
|
||||||
return wallet;
|
return wallet;
|
||||||
|
@ -301,16 +302,14 @@ class MoneroWalletService extends WalletService<
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final oldAndroidWalletDirPath =
|
final oldAndroidWalletDirPath = await outdatedAndroidPathForWalletDir(name: name);
|
||||||
await outdatedAndroidPathForWalletDir(name: name);
|
|
||||||
final dir = Directory(oldAndroidWalletDirPath);
|
final dir = Directory(oldAndroidWalletDirPath);
|
||||||
|
|
||||||
if (!dir.existsSync()) {
|
if (!dir.existsSync()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final newWalletDirPath =
|
final newWalletDirPath = await pathForWalletDir(name: name, type: getType());
|
||||||
await pathForWalletDir(name: name, type: getType());
|
|
||||||
|
|
||||||
dir.listSync().forEach((f) {
|
dir.listSync().forEach((f) {
|
||||||
final file = File(f.path);
|
final file = File(f.path);
|
||||||
|
|
|
@ -69,6 +69,7 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
||||||
NanoWallet(walletInfo: currentWalletInfo, password: password, mnemonic: randomWords);
|
NanoWallet(walletInfo: currentWalletInfo, password: password, mnemonic: randomWords);
|
||||||
|
|
||||||
await currentWallet.renameWalletFiles(newName);
|
await currentWallet.renameWalletFiles(newName);
|
||||||
|
await saveBackup(newName);
|
||||||
|
|
||||||
final newWalletInfo = currentWalletInfo;
|
final newWalletInfo = currentWalletInfo;
|
||||||
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
||||||
|
@ -150,14 +151,29 @@ class NanoWalletService extends WalletService<NanoNewWalletCredentials,
|
||||||
Future<NanoWallet> openWallet(String name, String password) async {
|
Future<NanoWallet> openWallet(String name, String password) async {
|
||||||
final walletInfo =
|
final walletInfo =
|
||||||
walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
|
walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
|
||||||
final wallet = await NanoWalletBase.open(
|
|
||||||
name: name,
|
|
||||||
password: password,
|
|
||||||
walletInfo: walletInfo,
|
|
||||||
);
|
|
||||||
|
|
||||||
await wallet.init();
|
try {
|
||||||
await wallet.save();
|
final wallet = await NanoWalletBase.open(
|
||||||
return wallet;
|
name: name,
|
||||||
|
password: password,
|
||||||
|
walletInfo: walletInfo,
|
||||||
|
);
|
||||||
|
|
||||||
|
await wallet.init();
|
||||||
|
await wallet.save();
|
||||||
|
saveBackup(name);
|
||||||
|
return wallet;
|
||||||
|
} catch (_) {
|
||||||
|
await restoreWalletFilesFromBackup(name);
|
||||||
|
final wallet = await NanoWalletBase.open(
|
||||||
|
name: name,
|
||||||
|
password: password,
|
||||||
|
walletInfo: walletInfo,
|
||||||
|
);
|
||||||
|
|
||||||
|
await wallet.init();
|
||||||
|
await wallet.save();
|
||||||
|
return wallet;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,16 +42,31 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
|
||||||
Future<PolygonWallet> openWallet(String name, String password) async {
|
Future<PolygonWallet> openWallet(String name, String password) async {
|
||||||
final walletInfo =
|
final walletInfo =
|
||||||
walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
|
walletInfoSource.values.firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
|
||||||
final wallet = await PolygonWallet.open(
|
|
||||||
name: name,
|
|
||||||
password: password,
|
|
||||||
walletInfo: walletInfo,
|
|
||||||
);
|
|
||||||
|
|
||||||
await wallet.init();
|
try {
|
||||||
await wallet.save();
|
final wallet = await PolygonWallet.open(
|
||||||
|
name: name,
|
||||||
|
password: password,
|
||||||
|
walletInfo: walletInfo,
|
||||||
|
);
|
||||||
|
|
||||||
return wallet;
|
await wallet.init();
|
||||||
|
await wallet.save();
|
||||||
|
saveBackup(name);
|
||||||
|
return wallet;
|
||||||
|
} catch (_) {
|
||||||
|
await restoreWalletFilesFromBackup(name);
|
||||||
|
|
||||||
|
final wallet = await PolygonWallet.open(
|
||||||
|
name: name,
|
||||||
|
password: password,
|
||||||
|
walletInfo: walletInfo,
|
||||||
|
);
|
||||||
|
|
||||||
|
await wallet.init();
|
||||||
|
await wallet.save();
|
||||||
|
return wallet;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -99,6 +114,7 @@ class PolygonWalletService extends EVMChainWalletService<PolygonWallet> {
|
||||||
password: password, name: currentName, walletInfo: currentWalletInfo);
|
password: password, name: currentName, walletInfo: currentWalletInfo);
|
||||||
|
|
||||||
await currentWallet.renameWalletFiles(newName);
|
await currentWallet.renameWalletFiles(newName);
|
||||||
|
await saveBackup(newName);
|
||||||
|
|
||||||
final newWalletInfo = currentWalletInfo;
|
final newWalletInfo = currentWalletInfo;
|
||||||
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
newWalletInfo.id = WalletBase.idFor(newName, getType());
|
||||||
|
|
|
@ -193,4 +193,4 @@ class CWBitcoin extends Bitcoin {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<BitcoinReceivePageOption> getBitcoinReceivePageOptions() => BitcoinReceivePageOption.all;
|
List<BitcoinReceivePageOption> getBitcoinReceivePageOptions() => BitcoinReceivePageOption.all;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:auto_size_text/auto_size_text.dart';
|
|
||||||
import 'package:cake_wallet/entities/qr_view_data.dart';
|
import 'package:cake_wallet/entities/qr_view_data.dart';
|
||||||
import 'package:cake_wallet/themes/extensions/qr_code_theme.dart';
|
import 'package:cake_wallet/themes/extensions/qr_code_theme.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
import 'package:cake_wallet/routes.dart';
|
||||||
|
@ -7,7 +6,6 @@ import 'package:cake_wallet/src/screens/receive/widgets/currency_input_field.dar
|
||||||
import 'package:cake_wallet/utils/brightness_util.dart';
|
import 'package:cake_wallet/utils/brightness_util.dart';
|
||||||
import 'package:cake_wallet/utils/show_bar.dart';
|
import 'package:cake_wallet/utils/show_bar.dart';
|
||||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||||
|
@ -146,10 +144,9 @@ class QRWidget extends StatelessWidget {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: AutoSizeText(
|
child: Text(
|
||||||
addressListViewModel.address.address,
|
addressListViewModel.address.address,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
maxLines: addressListViewModel.wallet.type == WalletType.monero ? 2 : 1,
|
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
import 'package:cake_wallet/reactions/wallet_connect.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart';
|
import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart';
|
import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart';
|
||||||
|
@ -8,6 +10,7 @@ import 'package:cake_wallet/utils/feature_flag.dart';
|
||||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/settings/sync_mode.dart';
|
import 'package:cake_wallet/view_model/settings/sync_mode.dart';
|
||||||
|
import 'package:cw_core/battery_optimization_native.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
import 'package:cake_wallet/routes.dart';
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
|
@ -45,12 +48,37 @@ class ConnectionSyncPage extends BasePage {
|
||||||
if (DeviceInfo.instance.isMobile) ...[
|
if (DeviceInfo.instance.isMobile) ...[
|
||||||
Observer(builder: (context) {
|
Observer(builder: (context) {
|
||||||
return SettingsPickerCell<SyncMode>(
|
return SettingsPickerCell<SyncMode>(
|
||||||
title: S.current.background_sync_mode,
|
title: S.current.background_sync_mode,
|
||||||
items: SyncMode.all,
|
items: SyncMode.all,
|
||||||
displayItem: (SyncMode syncMode) => syncMode.name,
|
displayItem: (SyncMode syncMode) => syncMode.name,
|
||||||
selectedItem: dashboardViewModel.syncMode,
|
selectedItem: dashboardViewModel.syncMode,
|
||||||
onItemSelected: dashboardViewModel.setSyncMode,
|
onItemSelected: (syncMode) async {
|
||||||
);
|
dashboardViewModel.setSyncMode(syncMode);
|
||||||
|
|
||||||
|
if (syncMode.type != SyncType.disabled) {
|
||||||
|
final isDisabled = await isBatteryOptimizationDisabled();
|
||||||
|
|
||||||
|
if (isDisabled || Platform.isIOS) return;
|
||||||
|
|
||||||
|
await showPopUp<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext dialogContext) {
|
||||||
|
return AlertWithTwoActions(
|
||||||
|
alertTitle: S.current.disableBatteryOptimization,
|
||||||
|
alertContent: S.current.disableBatteryOptimizationDescription,
|
||||||
|
leftButtonText: S.of(context).cancel,
|
||||||
|
rightButtonText: S.of(context).ok,
|
||||||
|
actionLeftButton: () => Navigator.of(dialogContext).pop(),
|
||||||
|
actionRightButton: () async {
|
||||||
|
await requestDisableBatteryOptimization();
|
||||||
|
|
||||||
|
Navigator.of(dialogContext).pop();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
}),
|
}),
|
||||||
const StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
|
const StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
|
||||||
Observer(builder: (context) {
|
Observer(builder: (context) {
|
||||||
|
@ -99,7 +127,6 @@ class ConnectionSyncPage extends BasePage {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Future<void> _presentReconnectAlert(BuildContext context) async {
|
Future<void> _presentReconnectAlert(BuildContext context) async {
|
||||||
await showPopUp<void>(
|
await showPopUp<void>(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
|
@ -10,6 +10,6 @@ class SyncMode {
|
||||||
static final all = [
|
static final all = [
|
||||||
SyncMode("Disabled", SyncType.disabled, Duration.zero),
|
SyncMode("Disabled", SyncType.disabled, Duration.zero),
|
||||||
SyncMode("Unobtrusive", SyncType.unobtrusive, Duration(days: 1)),
|
SyncMode("Unobtrusive", SyncType.unobtrusive, Duration(days: 1)),
|
||||||
SyncMode("Aggressive", SyncType.aggressive, Duration(hours: 6)),
|
SyncMode("Aggressive", SyncType.aggressive, Duration(hours: 1)),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -189,6 +189,8 @@
|
||||||
"disable_exchange": "تعطيل التبادل",
|
"disable_exchange": "تعطيل التبادل",
|
||||||
"disable_fiat": "تعطيل fiat",
|
"disable_fiat": "تعطيل fiat",
|
||||||
"disable_sell": "قم بتعطيل إجراء البيع",
|
"disable_sell": "قم بتعطيل إجراء البيع",
|
||||||
|
"disableBatteryOptimization": "تعطيل تحسين البطارية",
|
||||||
|
"disableBatteryOptimizationDescription": "هل تريد تعطيل تحسين البطارية من أجل جعل الخلفية مزامنة تعمل بحرية وسلاسة؟",
|
||||||
"disabled": "معطلة",
|
"disabled": "معطلة",
|
||||||
"discount": "وفر ${value}٪",
|
"discount": "وفر ${value}٪",
|
||||||
"display_settings": "اعدادات العرض",
|
"display_settings": "اعدادات العرض",
|
||||||
|
|
|
@ -189,6 +189,8 @@
|
||||||
"disable_exchange": "Деактивиране на борса",
|
"disable_exchange": "Деактивиране на борса",
|
||||||
"disable_fiat": "Деактивиране на fiat",
|
"disable_fiat": "Деактивиране на fiat",
|
||||||
"disable_sell": "Деактивирайте действието за продажба",
|
"disable_sell": "Деактивирайте действието за продажба",
|
||||||
|
"disableBatteryOptimization": "Деактивирайте оптимизацията на батерията",
|
||||||
|
"disableBatteryOptimizationDescription": "Искате ли да деактивирате оптимизацията на батерията, за да направите синхронизирането на фона да работи по -свободно и гладко?",
|
||||||
"disabled": "Деактивирано",
|
"disabled": "Деактивирано",
|
||||||
"discount": "Спестете ${value}%",
|
"discount": "Спестете ${value}%",
|
||||||
"display_settings": "Настройки на екрана",
|
"display_settings": "Настройки на екрана",
|
||||||
|
|
|
@ -189,6 +189,8 @@
|
||||||
"disable_exchange": "Zakázat směnárny",
|
"disable_exchange": "Zakázat směnárny",
|
||||||
"disable_fiat": "Zakázat fiat",
|
"disable_fiat": "Zakázat fiat",
|
||||||
"disable_sell": "Zakázat akci prodeje",
|
"disable_sell": "Zakázat akci prodeje",
|
||||||
|
"disableBatteryOptimization": "Zakázat optimalizaci baterie",
|
||||||
|
"disableBatteryOptimizationDescription": "Chcete deaktivovat optimalizaci baterie, aby se synchronizovala pozadí volně a hladce?",
|
||||||
"disabled": "Zakázáno",
|
"disabled": "Zakázáno",
|
||||||
"discount": "Ušetříte ${value}%",
|
"discount": "Ušetříte ${value}%",
|
||||||
"display_settings": "Nastavení zobrazení",
|
"display_settings": "Nastavení zobrazení",
|
||||||
|
|
|
@ -189,6 +189,8 @@
|
||||||
"disable_exchange": "Exchange deaktivieren",
|
"disable_exchange": "Exchange deaktivieren",
|
||||||
"disable_fiat": "Fiat deaktivieren",
|
"disable_fiat": "Fiat deaktivieren",
|
||||||
"disable_sell": "Verkaufsaktion deaktivieren",
|
"disable_sell": "Verkaufsaktion deaktivieren",
|
||||||
|
"disableBatteryOptimization": "Batterieoptimierung deaktivieren",
|
||||||
|
"disableBatteryOptimizationDescription": "Möchten Sie die Batterieoptimierung deaktivieren, um die Hintergrundsynchronisierung freier und reibungsloser zu gestalten?",
|
||||||
"disabled": "Deaktiviert",
|
"disabled": "Deaktiviert",
|
||||||
"discount": "${value} % sparen",
|
"discount": "${value} % sparen",
|
||||||
"display_settings": "Anzeigeeinstellungen",
|
"display_settings": "Anzeigeeinstellungen",
|
||||||
|
|
|
@ -189,6 +189,8 @@
|
||||||
"disable_exchange": "Disable exchange",
|
"disable_exchange": "Disable exchange",
|
||||||
"disable_fiat": "Disable fiat",
|
"disable_fiat": "Disable fiat",
|
||||||
"disable_sell": "Disable sell action",
|
"disable_sell": "Disable sell action",
|
||||||
|
"disableBatteryOptimization": "Disable Battery Optimization",
|
||||||
|
"disableBatteryOptimizationDescription": "Do you want to disable battery optimization in order to make background sync run more freely and smoothly?",
|
||||||
"disabled": "Disabled",
|
"disabled": "Disabled",
|
||||||
"discount": "Save ${value}%",
|
"discount": "Save ${value}%",
|
||||||
"display_settings": "Display settings",
|
"display_settings": "Display settings",
|
||||||
|
|
|
@ -189,6 +189,8 @@
|
||||||
"disable_exchange": "Deshabilitar intercambio",
|
"disable_exchange": "Deshabilitar intercambio",
|
||||||
"disable_fiat": "Deshabilitar fiat",
|
"disable_fiat": "Deshabilitar fiat",
|
||||||
"disable_sell": "Desactivar acción de venta",
|
"disable_sell": "Desactivar acción de venta",
|
||||||
|
"disableBatteryOptimization": "Deshabilitar la optimización de la batería",
|
||||||
|
"disableBatteryOptimizationDescription": "¿Desea deshabilitar la optimización de la batería para que la sincronización de fondo se ejecute más libremente y sin problemas?",
|
||||||
"disabled": "Desactivado",
|
"disabled": "Desactivado",
|
||||||
"discount": "Ahorra ${value}%",
|
"discount": "Ahorra ${value}%",
|
||||||
"display_settings": "Configuración de pantalla",
|
"display_settings": "Configuración de pantalla",
|
||||||
|
|
|
@ -189,6 +189,8 @@
|
||||||
"disable_exchange": "Désactiver l'échange",
|
"disable_exchange": "Désactiver l'échange",
|
||||||
"disable_fiat": "Désactiver les montants en fiat",
|
"disable_fiat": "Désactiver les montants en fiat",
|
||||||
"disable_sell": "Désactiver l'action de vente",
|
"disable_sell": "Désactiver l'action de vente",
|
||||||
|
"disableBatteryOptimization": "Désactiver l'optimisation de la batterie",
|
||||||
|
"disableBatteryOptimizationDescription": "Voulez-vous désactiver l'optimisation de la batterie afin de faire fonctionner la synchronisation d'arrière-plan plus librement et en douceur?",
|
||||||
"disabled": "Désactivé",
|
"disabled": "Désactivé",
|
||||||
"discount": "Économisez ${value}%",
|
"discount": "Économisez ${value}%",
|
||||||
"display_settings": "Paramètres d'affichage",
|
"display_settings": "Paramètres d'affichage",
|
||||||
|
|
|
@ -189,6 +189,8 @@
|
||||||
"disable_exchange": "Kashe musanya",
|
"disable_exchange": "Kashe musanya",
|
||||||
"disable_fiat": "Dakatar da fiat",
|
"disable_fiat": "Dakatar da fiat",
|
||||||
"disable_sell": "Kashe karbuwa",
|
"disable_sell": "Kashe karbuwa",
|
||||||
|
"disableBatteryOptimization": "Kashe ingantawa baturi",
|
||||||
|
"disableBatteryOptimizationDescription": "Shin kana son kashe ingantawa baturi don yin setnc bankwali gudu da yar kyauta da kyau?",
|
||||||
"disabled": "tsaya",
|
"disabled": "tsaya",
|
||||||
"discount": "Ajiye ${value}%",
|
"discount": "Ajiye ${value}%",
|
||||||
"display_settings": "Nuni saituna",
|
"display_settings": "Nuni saituna",
|
||||||
|
|
|
@ -189,6 +189,8 @@
|
||||||
"disable_exchange": "एक्सचेंज अक्षम करें",
|
"disable_exchange": "एक्सचेंज अक्षम करें",
|
||||||
"disable_fiat": "िएट को अक्षम करें",
|
"disable_fiat": "िएट को अक्षम करें",
|
||||||
"disable_sell": "बेचने की कार्रवाई अक्षम करें",
|
"disable_sell": "बेचने की कार्रवाई अक्षम करें",
|
||||||
|
"disableBatteryOptimization": "बैटरी अनुकूलन अक्षम करें",
|
||||||
|
"disableBatteryOptimizationDescription": "क्या आप बैकग्राउंड सिंक को अधिक स्वतंत्र और सुचारू रूप से चलाने के लिए बैटरी ऑप्टिमाइज़ेशन को अक्षम करना चाहते हैं?",
|
||||||
"disabled": "अक्षम",
|
"disabled": "अक्षम",
|
||||||
"discount": "${value}% बचाएं",
|
"discount": "${value}% बचाएं",
|
||||||
"display_settings": "प्रदर्शन सेटिंग्स",
|
"display_settings": "प्रदर्शन सेटिंग्स",
|
||||||
|
|
|
@ -189,6 +189,8 @@
|
||||||
"disable_exchange": "Onemogući exchange",
|
"disable_exchange": "Onemogući exchange",
|
||||||
"disable_fiat": "Isključi, fiat",
|
"disable_fiat": "Isključi, fiat",
|
||||||
"disable_sell": "Onemogući akciju prodaje",
|
"disable_sell": "Onemogući akciju prodaje",
|
||||||
|
"disableBatteryOptimization": "Onemogući optimizaciju baterije",
|
||||||
|
"disableBatteryOptimizationDescription": "Želite li onemogućiti optimizaciju baterije kako bi se pozadinska sinkronizacija radila slobodnije i glatko?",
|
||||||
"disabled": "Onemogućeno",
|
"disabled": "Onemogućeno",
|
||||||
"discount": "Uštedite ${value}%",
|
"discount": "Uštedite ${value}%",
|
||||||
"display_settings": "Postavke zaslona",
|
"display_settings": "Postavke zaslona",
|
||||||
|
|
|
@ -189,6 +189,8 @@
|
||||||
"disable_exchange": "Nonaktifkan pertukaran",
|
"disable_exchange": "Nonaktifkan pertukaran",
|
||||||
"disable_fiat": "Nonaktifkan fiat",
|
"disable_fiat": "Nonaktifkan fiat",
|
||||||
"disable_sell": "Nonaktifkan aksi jual",
|
"disable_sell": "Nonaktifkan aksi jual",
|
||||||
|
"disableBatteryOptimization": "Nonaktifkan optimasi baterai",
|
||||||
|
"disableBatteryOptimizationDescription": "Apakah Anda ingin menonaktifkan optimasi baterai untuk membuat sinkronisasi latar belakang berjalan lebih bebas dan lancar?",
|
||||||
"disabled": "Dinonaktifkan",
|
"disabled": "Dinonaktifkan",
|
||||||
"discount": "Hemat ${value}%",
|
"discount": "Hemat ${value}%",
|
||||||
"display_settings": "Pengaturan tampilan",
|
"display_settings": "Pengaturan tampilan",
|
||||||
|
|
|
@ -190,6 +190,8 @@
|
||||||
"disable_exchange": "Disabilita scambio",
|
"disable_exchange": "Disabilita scambio",
|
||||||
"disable_fiat": "Disabilita fiat",
|
"disable_fiat": "Disabilita fiat",
|
||||||
"disable_sell": "Disabilita l'azione di vendita",
|
"disable_sell": "Disabilita l'azione di vendita",
|
||||||
|
"disableBatteryOptimization": "Disabilita l'ottimizzazione della batteria",
|
||||||
|
"disableBatteryOptimizationDescription": "Vuoi disabilitare l'ottimizzazione della batteria per far funzionare la sincronizzazione in background più libera e senza intoppi?",
|
||||||
"disabled": "Disabilitato",
|
"disabled": "Disabilitato",
|
||||||
"discount": "Risparmia ${value}%",
|
"discount": "Risparmia ${value}%",
|
||||||
"display_settings": "Impostazioni di visualizzazione",
|
"display_settings": "Impostazioni di visualizzazione",
|
||||||
|
|
|
@ -189,6 +189,8 @@
|
||||||
"disable_exchange": "交換を無効にする",
|
"disable_exchange": "交換を無効にする",
|
||||||
"disable_fiat": "フィアットを無効にする",
|
"disable_fiat": "フィアットを無効にする",
|
||||||
"disable_sell": "販売アクションを無効にする",
|
"disable_sell": "販売アクションを無効にする",
|
||||||
|
"disableBatteryOptimization": "バッテリーの最適化を無効にします",
|
||||||
|
"disableBatteryOptimizationDescription": "バックグラウンドシンクをより自由かつスムーズに実行するために、バッテリーの最適化を無効にしたいですか?",
|
||||||
"disabled": "無効",
|
"disabled": "無効",
|
||||||
"discount": "${value}%を節約",
|
"discount": "${value}%を節約",
|
||||||
"display_settings": "表示設定",
|
"display_settings": "表示設定",
|
||||||
|
|
|
@ -189,6 +189,8 @@
|
||||||
"disable_exchange": "교환 비활성화",
|
"disable_exchange": "교환 비활성화",
|
||||||
"disable_fiat": "법정화폐 비활성화",
|
"disable_fiat": "법정화폐 비활성화",
|
||||||
"disable_sell": "판매 조치 비활성화",
|
"disable_sell": "판매 조치 비활성화",
|
||||||
|
"disableBatteryOptimization": "배터리 최적화를 비활성화합니다",
|
||||||
|
"disableBatteryOptimizationDescription": "백그라운드 동기화를보다 자유롭고 매끄럽게 실행하기 위해 배터리 최적화를 비활성화하고 싶습니까?",
|
||||||
"disabled": "장애가 있는",
|
"disabled": "장애가 있는",
|
||||||
"discount": "${value}% 절약",
|
"discount": "${value}% 절약",
|
||||||
"display_settings": "디스플레이 설정",
|
"display_settings": "디스플레이 설정",
|
||||||
|
|
|
@ -189,6 +189,8 @@
|
||||||
"disable_exchange": "လဲလှယ်မှုကို ပိတ်ပါ။",
|
"disable_exchange": "လဲလှယ်မှုကို ပိတ်ပါ။",
|
||||||
"disable_fiat": "Fiat ကိုပိတ်ပါ။",
|
"disable_fiat": "Fiat ကိုပိတ်ပါ။",
|
||||||
"disable_sell": "ရောင်းချခြင်းလုပ်ဆောင်ချက်ကို ပိတ်ပါ။",
|
"disable_sell": "ရောင်းချခြင်းလုပ်ဆောင်ချက်ကို ပိတ်ပါ။",
|
||||||
|
"disableBatteryOptimization": "ဘက်ထရီ optimization ကိုပိတ်ပါ",
|
||||||
|
"disableBatteryOptimizationDescription": "နောက်ခံထပ်တူပြုခြင်းနှင့်ချောချောမွေ့မွေ့ပြုလုပ်နိုင်ရန်ဘက်ထရီ optimization ကိုသင်ပိတ်ထားလိုပါသလား။",
|
||||||
"disabled": "မသန်စွမ်း",
|
"disabled": "မသန်စွမ်း",
|
||||||
"discount": "${value}% ချွေတာ",
|
"discount": "${value}% ချွေတာ",
|
||||||
"display_settings": "ပြသရန် ဆက်တင်များ",
|
"display_settings": "ပြသရန် ဆက်တင်များ",
|
||||||
|
|
|
@ -189,6 +189,8 @@
|
||||||
"disable_exchange": "Uitwisseling uitschakelen",
|
"disable_exchange": "Uitwisseling uitschakelen",
|
||||||
"disable_fiat": "Schakel Fiat uit",
|
"disable_fiat": "Schakel Fiat uit",
|
||||||
"disable_sell": "Verkoopactie uitschakelen",
|
"disable_sell": "Verkoopactie uitschakelen",
|
||||||
|
"disableBatteryOptimization": "Schakel de batterijoptimalisatie uit",
|
||||||
|
"disableBatteryOptimizationDescription": "Wilt u de optimalisatie van de batterij uitschakelen om achtergrondsynchronisatie te laten werken, vrijer en soepeler?",
|
||||||
"disabled": "Gehandicapt",
|
"disabled": "Gehandicapt",
|
||||||
"discount": "Bespaar ${value}%",
|
"discount": "Bespaar ${value}%",
|
||||||
"display_settings": "Weergave-instellingen",
|
"display_settings": "Weergave-instellingen",
|
||||||
|
|
|
@ -189,6 +189,8 @@
|
||||||
"disable_exchange": "Wyłącz wymianę",
|
"disable_exchange": "Wyłącz wymianę",
|
||||||
"disable_fiat": "Wyłącz waluty FIAT",
|
"disable_fiat": "Wyłącz waluty FIAT",
|
||||||
"disable_sell": "Wyłącz akcję sprzedaży",
|
"disable_sell": "Wyłącz akcję sprzedaży",
|
||||||
|
"disableBatteryOptimization": "Wyłącz optymalizację baterii",
|
||||||
|
"disableBatteryOptimizationDescription": "Czy chcesz wyłączyć optymalizację baterii, aby synchronizacja tła działała swobodniej i płynnie?",
|
||||||
"disabled": "Wyłączone",
|
"disabled": "Wyłączone",
|
||||||
"discount": "Zaoszczędź ${value}%",
|
"discount": "Zaoszczędź ${value}%",
|
||||||
"display_settings": "Ustawienia wyświetlania",
|
"display_settings": "Ustawienia wyświetlania",
|
||||||
|
|
|
@ -189,6 +189,8 @@
|
||||||
"disable_exchange": "Desativar troca",
|
"disable_exchange": "Desativar troca",
|
||||||
"disable_fiat": "Desativar fiat",
|
"disable_fiat": "Desativar fiat",
|
||||||
"disable_sell": "Desativar ação de venda",
|
"disable_sell": "Desativar ação de venda",
|
||||||
|
"disableBatteryOptimization": "Desative a otimização da bateria",
|
||||||
|
"disableBatteryOptimizationDescription": "Deseja desativar a otimização da bateria para fazer a sincronização de fundo funcionar de forma mais livre e suave?",
|
||||||
"disabled": "Desabilitado",
|
"disabled": "Desabilitado",
|
||||||
"discount": "Economize ${value}%",
|
"discount": "Economize ${value}%",
|
||||||
"display_settings": "Configurações de exibição",
|
"display_settings": "Configurações de exibição",
|
||||||
|
|
|
@ -189,6 +189,8 @@
|
||||||
"disable_exchange": "Отключить обмен",
|
"disable_exchange": "Отключить обмен",
|
||||||
"disable_fiat": "Отключить фиат",
|
"disable_fiat": "Отключить фиат",
|
||||||
"disable_sell": "Отключить действие продажи",
|
"disable_sell": "Отключить действие продажи",
|
||||||
|
"disableBatteryOptimization": "Отключить оптимизацию батареи",
|
||||||
|
"disableBatteryOptimizationDescription": "Вы хотите отключить оптимизацию батареи, чтобы сделать фона синхронизации более свободно и плавно?",
|
||||||
"disabled": "Отключено",
|
"disabled": "Отключено",
|
||||||
"discount": "Сэкономьте ${value}%",
|
"discount": "Сэкономьте ${value}%",
|
||||||
"display_settings": "Настройки отображения",
|
"display_settings": "Настройки отображения",
|
||||||
|
|
|
@ -189,6 +189,8 @@
|
||||||
"disable_exchange": "ปิดใช้งานการแลกเปลี่ยน",
|
"disable_exchange": "ปิดใช้งานการแลกเปลี่ยน",
|
||||||
"disable_fiat": "ปิดใช้งานสกุลเงินตรา",
|
"disable_fiat": "ปิดใช้งานสกุลเงินตรา",
|
||||||
"disable_sell": "ปิดการใช้งานการขาย",
|
"disable_sell": "ปิดการใช้งานการขาย",
|
||||||
|
"disableBatteryOptimization": "ปิดใช้งานการเพิ่มประสิทธิภาพแบตเตอรี่",
|
||||||
|
"disableBatteryOptimizationDescription": "คุณต้องการปิดใช้งานการเพิ่มประสิทธิภาพแบตเตอรี่เพื่อให้การซิงค์พื้นหลังทำงานได้อย่างอิสระและราบรื่นมากขึ้นหรือไม่?",
|
||||||
"disabled": "ปิดใช้งาน",
|
"disabled": "ปิดใช้งาน",
|
||||||
"discount": "ประหยัด ${value}%",
|
"discount": "ประหยัด ${value}%",
|
||||||
"display_settings": "การตั้งค่าการแสดงผล",
|
"display_settings": "การตั้งค่าการแสดงผล",
|
||||||
|
|
|
@ -189,6 +189,8 @@
|
||||||
"disable_exchange": "Huwag paganahin ang palitan",
|
"disable_exchange": "Huwag paganahin ang palitan",
|
||||||
"disable_fiat": "Huwag paganahin ang Fiat",
|
"disable_fiat": "Huwag paganahin ang Fiat",
|
||||||
"disable_sell": "Huwag paganahin ang pagkilos ng pagbebenta",
|
"disable_sell": "Huwag paganahin ang pagkilos ng pagbebenta",
|
||||||
|
"disableBatteryOptimization": "Huwag paganahin ang pag -optimize ng baterya",
|
||||||
|
"disableBatteryOptimizationDescription": "Nais mo bang huwag paganahin ang pag -optimize ng baterya upang gawing mas malaya at maayos ang pag -sync ng background?",
|
||||||
"disabled": "Hindi pinagana",
|
"disabled": "Hindi pinagana",
|
||||||
"discount": "Makatipid ng ${value}%",
|
"discount": "Makatipid ng ${value}%",
|
||||||
"display_settings": "Mga setting ng pagpapakita",
|
"display_settings": "Mga setting ng pagpapakita",
|
||||||
|
|
|
@ -189,6 +189,8 @@
|
||||||
"disable_exchange": "Borsayı devre dışı bırak",
|
"disable_exchange": "Borsayı devre dışı bırak",
|
||||||
"disable_fiat": "İtibari paraları devre dışı bırak",
|
"disable_fiat": "İtibari paraları devre dışı bırak",
|
||||||
"disable_sell": "Satış işlemini devre dışı bırak",
|
"disable_sell": "Satış işlemini devre dışı bırak",
|
||||||
|
"disableBatteryOptimization": "Pil optimizasyonunu devre dışı bırakın",
|
||||||
|
"disableBatteryOptimizationDescription": "Arka plan senkronizasyonunu daha özgür ve sorunsuz bir şekilde çalıştırmak için pil optimizasyonunu devre dışı bırakmak istiyor musunuz?",
|
||||||
"disabled": "Devre dışı",
|
"disabled": "Devre dışı",
|
||||||
"discount": "%${value} tasarruf et",
|
"discount": "%${value} tasarruf et",
|
||||||
"display_settings": "Görüntü ayarları",
|
"display_settings": "Görüntü ayarları",
|
||||||
|
|
|
@ -189,6 +189,8 @@
|
||||||
"disable_exchange": "Вимкнути exchange",
|
"disable_exchange": "Вимкнути exchange",
|
||||||
"disable_fiat": "Вимкнути фиат",
|
"disable_fiat": "Вимкнути фиат",
|
||||||
"disable_sell": "Вимкнути дію продажу",
|
"disable_sell": "Вимкнути дію продажу",
|
||||||
|
"disableBatteryOptimization": "Вимкнути оптимізацію акумулятора",
|
||||||
|
"disableBatteryOptimizationDescription": "Ви хочете відключити оптимізацію акумулятора, щоб зробити фонову синхронізацію більш вільно та плавно?",
|
||||||
"disabled": "Вимкнено",
|
"disabled": "Вимкнено",
|
||||||
"discount": "Зекономте ${value}%",
|
"discount": "Зекономте ${value}%",
|
||||||
"display_settings": "Налаштування дисплея",
|
"display_settings": "Налаштування дисплея",
|
||||||
|
|
|
@ -189,6 +189,8 @@
|
||||||
"disable_exchange": "تبادلے کو غیر فعال کریں۔",
|
"disable_exchange": "تبادلے کو غیر فعال کریں۔",
|
||||||
"disable_fiat": "فیاٹ کو غیر فعال کریں۔",
|
"disable_fiat": "فیاٹ کو غیر فعال کریں۔",
|
||||||
"disable_sell": "فروخت کی کارروائی کو غیر فعال کریں۔",
|
"disable_sell": "فروخت کی کارروائی کو غیر فعال کریں۔",
|
||||||
|
"disableBatteryOptimization": "بیٹری کی اصلاح کو غیر فعال کریں",
|
||||||
|
"disableBatteryOptimizationDescription": "کیا آپ پس منظر کی مطابقت پذیری کو زیادہ آزادانہ اور آسانی سے چلانے کے لئے بیٹری کی اصلاح کو غیر فعال کرنا چاہتے ہیں؟",
|
||||||
"disabled": "معذور",
|
"disabled": "معذور",
|
||||||
"discount": "${value}% بچائیں",
|
"discount": "${value}% بچائیں",
|
||||||
"display_settings": "ڈسپلے کی ترتیبات",
|
"display_settings": "ڈسپلے کی ترتیبات",
|
||||||
|
|
|
@ -189,6 +189,8 @@
|
||||||
"disable_exchange": "Pa ilé pàṣípààrọ̀",
|
"disable_exchange": "Pa ilé pàṣípààrọ̀",
|
||||||
"disable_fiat": "Pa owó tí ìjọba pàṣẹ wa lò",
|
"disable_fiat": "Pa owó tí ìjọba pàṣẹ wa lò",
|
||||||
"disable_sell": "Ko iṣọrọ iṣọrọ",
|
"disable_sell": "Ko iṣọrọ iṣọrọ",
|
||||||
|
"disableBatteryOptimization": "Mu Ifasi batiri",
|
||||||
|
"disableBatteryOptimizationDescription": "Ṣe o fẹ lati mu iṣapelo batiri si lati le ṣiṣe ayẹwo ẹhin ati laisiyonu?",
|
||||||
"disabled": "Wọ́n tí a ti pa",
|
"disabled": "Wọ́n tí a ti pa",
|
||||||
"discount": "Pamọ́ ${value}%",
|
"discount": "Pamọ́ ${value}%",
|
||||||
"display_settings": "Fihàn àwọn ààtò",
|
"display_settings": "Fihàn àwọn ààtò",
|
||||||
|
|
|
@ -189,6 +189,8 @@
|
||||||
"disable_exchange": "禁用交换",
|
"disable_exchange": "禁用交换",
|
||||||
"disable_fiat": "禁用法令",
|
"disable_fiat": "禁用法令",
|
||||||
"disable_sell": "禁用卖出操作",
|
"disable_sell": "禁用卖出操作",
|
||||||
|
"disableBatteryOptimization": "禁用电池优化",
|
||||||
|
"disableBatteryOptimizationDescription": "您是否要禁用电池优化以使背景同步更加自由,平稳地运行?",
|
||||||
"disabled": "禁用",
|
"disabled": "禁用",
|
||||||
"discount": "节省 ${value}%",
|
"discount": "节省 ${value}%",
|
||||||
"display_settings": "显示设置",
|
"display_settings": "显示设置",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue