mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-22 11:39:22 +00:00
Merge branch 'main' into CW-522-Enable-On-Off-ramp-activities-to-show-on-transaction-history-screen
This commit is contained in:
commit
786baab173
62 changed files with 20057 additions and 19530 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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,3 +1,3 @@
|
||||||
Security and Privacy enhancements
|
Improve wallet recovery and error tolerance
|
||||||
Usability enhancements
|
Enhance Background sync for Monero wallets
|
||||||
Bug fixes
|
Bug fixes
|
|
@ -1,4 +1,5 @@
|
||||||
List previously used Bitcoin addresses
|
Bitcoin transactions fixes and enhancements
|
||||||
Security and Privacy enhancements
|
EVM wallets enhancements (Ethereum and Polygon)
|
||||||
Usability enhancements
|
Improve wallet recovery and error tolerance
|
||||||
|
Enhance Background sync for Monero wallets
|
||||||
Bug fixes
|
Bug fixes
|
|
@ -28,7 +28,7 @@ class BitcoinAddressRecord {
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
|
|
@ -12,10 +12,8 @@ import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
|
||||||
class BitcoinWalletService extends WalletService<
|
class BitcoinWalletService extends WalletService<BitcoinNewWalletCredentials,
|
||||||
BitcoinNewWalletCredentials,
|
BitcoinRestoreWalletFromSeedCredentials, BitcoinRestoreWalletFromWIFCredentials> {
|
||||||
BitcoinRestoreWalletFromSeedCredentials,
|
|
||||||
BitcoinRestoreWalletFromWIFCredentials> {
|
|
||||||
BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
BitcoinWalletService(this.walletInfoSource, this.unspentCoinsInfoSource);
|
||||||
|
|
||||||
final Box<WalletInfo> walletInfoSource;
|
final Box<WalletInfo> walletInfoSource;
|
||||||
|
@ -42,28 +40,41 @@ class BitcoinWalletService extends WalletService<
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<BitcoinWallet> openWallet(String name, String password) async {
|
Future<BitcoinWallet> openWallet(String name, String password) async {
|
||||||
final walletInfo = walletInfoSource.values.firstWhereOrNull(
|
final walletInfo = walletInfoSource.values
|
||||||
(info) => info.id == WalletBase.idFor(name, getType()))!;
|
.firstWhereOrNull((info) => info.id == WalletBase.idFor(name, getType()))!;
|
||||||
final wallet = await BitcoinWalletBase.open(
|
try {
|
||||||
password: password, name: name, walletInfo: walletInfo,
|
final wallet = await BitcoinWalletBase.open(
|
||||||
unspentCoinsInfo: unspentCoinsInfoSource);
|
password: password,
|
||||||
await wallet.init();
|
name: name,
|
||||||
return wallet;
|
walletInfo: walletInfo,
|
||||||
|
unspentCoinsInfo: unspentCoinsInfoSource);
|
||||||
|
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
|
||||||
Future<void> remove(String wallet) async {
|
Future<void> remove(String wallet) async {
|
||||||
File(await pathForWalletDir(name: wallet, type: getType()))
|
File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true);
|
||||||
.delete(recursive: true);
|
final walletInfo = walletInfoSource.values
|
||||||
final walletInfo = walletInfoSource.values.firstWhereOrNull(
|
.firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!;
|
||||||
(info) => info.id == WalletBase.idFor(wallet, getType()))!;
|
|
||||||
await walletInfoSource.delete(walletInfo.key);
|
await walletInfoSource.delete(walletInfo.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> rename(String currentName, String password, String newName) async {
|
Future<void> rename(String currentName, String password, String newName) async {
|
||||||
final currentWalletInfo = walletInfoSource.values.firstWhereOrNull(
|
final currentWalletInfo = walletInfoSource.values
|
||||||
(info) => info.id == WalletBase.idFor(currentName, getType()))!;
|
.firstWhereOrNull((info) => info.id == WalletBase.idFor(currentName, getType()))!;
|
||||||
final currentWallet = await BitcoinWalletBase.open(
|
final currentWallet = await BitcoinWalletBase.open(
|
||||||
password: password,
|
password: password,
|
||||||
name: currentName,
|
name: currentName,
|
||||||
|
@ -71,6 +82,7 @@ class BitcoinWalletService 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());
|
||||||
|
@ -80,13 +92,11 @@ class BitcoinWalletService extends WalletService<
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<BitcoinWallet> restoreFromKeys(
|
Future<BitcoinWallet> restoreFromKeys(BitcoinRestoreWalletFromWIFCredentials credentials) async =>
|
||||||
BitcoinRestoreWalletFromWIFCredentials credentials) async =>
|
|
||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<BitcoinWallet> restoreFromSeed(
|
Future<BitcoinWallet> restoreFromSeed(BitcoinRestoreWalletFromSeedCredentials credentials) async {
|
||||||
BitcoinRestoreWalletFromSeedCredentials credentials) async {
|
|
||||||
if (!validateMnemonic(credentials.mnemonic)) {
|
if (!validateMnemonic(credentials.mnemonic)) {
|
||||||
throw BitcoinMnemonicIsIncorrectException();
|
throw BitcoinMnemonicIsIncorrectException();
|
||||||
}
|
}
|
||||||
|
@ -100,4 +110,4 @@ class BitcoinWalletService extends WalletService<
|
||||||
await wallet.init();
|
await wallet.init();
|
||||||
return wallet;
|
return wallet;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -601,8 +601,6 @@ abstract class ElectrumWalletBase
|
||||||
electrumClient.getHistory(scriptHash).then((history) => {scriptHash: history}));
|
electrumClient.getHistory(scriptHash).then((history) => {scriptHash: history}));
|
||||||
final historyResults = await Future.wait(histories);
|
final historyResults = await Future.wait(histories);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
historyResults.forEach((history) {
|
historyResults.forEach((history) {
|
||||||
history.entries.forEach((historyItem) {
|
history.entries.forEach((historyItem) {
|
||||||
if (historyItem.value.isNotEmpty) {
|
if (historyItem.value.isNotEmpty) {
|
||||||
|
@ -622,7 +620,6 @@ abstract class ElectrumWalletBase
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
addressHashes.forEach((sh, addressRecord) {
|
addressHashes.forEach((sh, addressRecord) {
|
||||||
addressRecord.txCount = newTxCounts[sh] ?? 0;
|
addressRecord.txCount = newTxCounts[sh] ?? 0;
|
||||||
});
|
});
|
||||||
|
|
|
@ -53,7 +53,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
String get address {
|
String get address {
|
||||||
if (isEnabledAutoGenerateSubaddress) {
|
if (isEnabledAutoGenerateSubaddress) {
|
||||||
if (receiveAddresses.isEmpty) {
|
if (receiveAddresses.isEmpty) {
|
||||||
final newAddress = generateNewAddress().address;
|
final newAddress = generateNewAddress(hd: mainHd).address;
|
||||||
return walletInfo.type == WalletType.bitcoinCash ? toCashAddr(newAddress) : newAddress;
|
return walletInfo.type == WalletType.bitcoinCash ? toCashAddr(newAddress) : newAddress;
|
||||||
}
|
}
|
||||||
final receiveAddress = receiveAddresses.first.address;
|
final receiveAddress = receiveAddresses.first.address;
|
||||||
|
@ -215,6 +215,13 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
List<BitcoinAddressRecord> addrs;
|
List<BitcoinAddressRecord> addrs;
|
||||||
|
|
||||||
if (addresses.isNotEmpty) {
|
if (addresses.isNotEmpty) {
|
||||||
|
|
||||||
|
|
||||||
|
if(!isHidden) {
|
||||||
|
final receiveAddressesList = addresses.where((addr) => !addr.isHidden).toList();
|
||||||
|
validateSideHdAddresses(receiveAddressesList);
|
||||||
|
}
|
||||||
|
|
||||||
addrs = addresses.where((addr) => addr.isHidden == isHidden).toList();
|
addrs = addresses.where((addr) => addr.isHidden == isHidden).toList();
|
||||||
} else {
|
} else {
|
||||||
addrs = await _createNewAddresses(
|
addrs = await _createNewAddresses(
|
||||||
|
@ -296,4 +303,10 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||||
final transactionHistory = await electrumClient.getHistory(sh);
|
final transactionHistory = await electrumClient.getHistory(sh);
|
||||||
return transactionHistory.isNotEmpty;
|
return transactionHistory.isNotEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void validateSideHdAddresses(List<BitcoinAddressRecord> addrWithTransactions) {
|
||||||
|
addrWithTransactions.forEach((element) {
|
||||||
|
if (element.address != getAddress(index: element.index, hd: mainHd)) element.isHidden = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,7 +1,8 @@
|
||||||
import 'package:cw_core/node.dart';
|
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_info.dart';
|
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
|
|
||||||
abstract class WalletService<N extends WalletCredentials, RFS extends WalletCredentials,
|
abstract class WalletService<N extends WalletCredentials, RFS extends WalletCredentials,
|
||||||
|
@ -21,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,7 @@ class MoneroWalletService extends WalletService<
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<MoneroWallet> restoreFromKeys(
|
Future<MoneroWallet> restoreFromKeys(MoneroRestoreWalletFromKeysCredentials credentials) async {
|
||||||
MoneroRestoreWalletFromKeysCredentials credentials) 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 +227,7 @@ class MoneroWalletService extends WalletService<
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<MoneroWallet> restoreFromSeed(
|
Future<MoneroWallet> restoreFromSeed(MoneroRestoreWalletFromSeedCredentials credentials) async {
|
||||||
MoneroRestoreWalletFromSeedCredentials credentials) 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 +252,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 +269,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 +288,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 +300,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
|
||||||
|
@ -100,6 +115,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());
|
||||||
|
|
|
@ -191,7 +191,7 @@
|
||||||
<true/>
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
<key>NSCameraUsageDescription</key>
|
<key>NSCameraUsageDescription</key>
|
||||||
<string>Used for scan QR code</string>
|
<string>Used for scanning QR code and can be used to capture images for identification purposes by third-party providers.</string>
|
||||||
<key>NSDocumentsFolderUsageDescription</key>
|
<key>NSDocumentsFolderUsageDescription</key>
|
||||||
<string>We need access to documents folder for getting access to open/save backup file</string>
|
<string>We need access to documents folder for getting access to open/save backup file</string>
|
||||||
<key>NSFaceIDUsageDescription</key>
|
<key>NSFaceIDUsageDescription</key>
|
||||||
|
|
|
@ -65,7 +65,7 @@ class CWBitcoin extends Bitcoin {
|
||||||
@override
|
@override
|
||||||
Future<void> generateNewAddress(Object wallet, String label) async {
|
Future<void> generateNewAddress(Object wallet, String label) async {
|
||||||
final bitcoinWallet = wallet as ElectrumWallet;
|
final bitcoinWallet = wallet as ElectrumWallet;
|
||||||
await bitcoinWallet.walletAddresses.generateNewAddress(label: label);
|
await bitcoinWallet.walletAddresses.generateNewAddress(label: label, hd: bitcoinWallet.hd);
|
||||||
await wallet.save();
|
await wallet.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -189,7 +189,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||||
output.loadContact(contact);
|
output.loadContact(contact);
|
||||||
},
|
},
|
||||||
validator: validator,
|
validator: validator,
|
||||||
selectedCurrency: sendViewModel.currency,
|
selectedCurrency: sendViewModel.selectedCryptoCurrency,
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
if (output.isParsedAddress)
|
if (output.isParsedAddress)
|
||||||
|
|
|
@ -69,40 +69,44 @@ class SendTemplateCard extends StatelessWidget {
|
||||||
validator: sendTemplateViewModel.templateValidator),
|
validator: sendTemplateViewModel.templateValidator),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(top: 20),
|
padding: EdgeInsets.only(top: 20),
|
||||||
child: AddressTextField(
|
child: Observer(
|
||||||
selectedCurrency: sendTemplateViewModel.cryptoCurrency,
|
builder: (context) {
|
||||||
controller: _addressController,
|
return AddressTextField(
|
||||||
onURIScanned: (uri) {
|
selectedCurrency: template.selectedCurrency,
|
||||||
final paymentRequest = PaymentRequest.fromUri(uri);
|
controller: _addressController,
|
||||||
_addressController.text = paymentRequest.address;
|
onURIScanned: (uri) {
|
||||||
_cryptoAmountController.text = paymentRequest.amount;
|
final paymentRequest = PaymentRequest.fromUri(uri);
|
||||||
},
|
_addressController.text = paymentRequest.address;
|
||||||
options: [
|
_cryptoAmountController.text = paymentRequest.amount;
|
||||||
AddressTextFieldOption.paste,
|
},
|
||||||
AddressTextFieldOption.qrCode,
|
options: [
|
||||||
AddressTextFieldOption.addressBook
|
AddressTextFieldOption.paste,
|
||||||
],
|
AddressTextFieldOption.qrCode,
|
||||||
onPushPasteButton: (context) async {
|
AddressTextFieldOption.addressBook
|
||||||
template.output.resetParsedAddress();
|
],
|
||||||
await template.output.fetchParsedAddress(context);
|
onPushPasteButton: (context) async {
|
||||||
},
|
template.output.resetParsedAddress();
|
||||||
onPushAddressBookButton: (context) async {
|
await template.output.fetchParsedAddress(context);
|
||||||
template.output.resetParsedAddress();
|
},
|
||||||
await template.output.fetchParsedAddress(context);
|
onPushAddressBookButton: (context) async {
|
||||||
},
|
template.output.resetParsedAddress();
|
||||||
buttonColor: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor,
|
await template.output.fetchParsedAddress(context);
|
||||||
borderColor: Theme.of(context).extension<SendPageTheme>()!.textFieldBorderColor,
|
},
|
||||||
textStyle: TextStyle(
|
buttonColor: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonColor,
|
||||||
fontSize: 14,
|
borderColor: Theme.of(context).extension<SendPageTheme>()!.textFieldBorderColor,
|
||||||
fontWeight: FontWeight.w500,
|
textStyle: TextStyle(
|
||||||
color: Colors.white,
|
fontSize: 14,
|
||||||
),
|
fontWeight: FontWeight.w500,
|
||||||
hintStyle: TextStyle(
|
color: Colors.white,
|
||||||
fontSize: 14,
|
),
|
||||||
fontWeight: FontWeight.w500,
|
hintStyle: TextStyle(
|
||||||
color: Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor,
|
fontSize: 14,
|
||||||
),
|
fontWeight: FontWeight.w500,
|
||||||
validator: sendTemplateViewModel.addressValidator,
|
color: Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor,
|
||||||
|
),
|
||||||
|
validator: sendTemplateViewModel.addressValidator,
|
||||||
|
);
|
||||||
|
}
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
|
|
|
@ -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,39 @@ 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 (Platform.isIOS) return;
|
||||||
|
|
||||||
|
if (syncMode.type != SyncType.disabled) {
|
||||||
|
final isDisabled = await isBatteryOptimizationDisabled();
|
||||||
|
|
||||||
|
if (isDisabled) 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 +129,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,
|
||||||
|
|
|
@ -16,11 +16,15 @@ import 'package:shared_preferences/shared_preferences.dart';
|
||||||
class ExceptionHandler {
|
class ExceptionHandler {
|
||||||
static bool _hasError = false;
|
static bool _hasError = false;
|
||||||
static const _coolDownDurationInDays = 7;
|
static const _coolDownDurationInDays = 7;
|
||||||
|
static File? _file;
|
||||||
|
|
||||||
static void _saveException(String? error, StackTrace? stackTrace, {String? library}) async {
|
static void _saveException(String? error, StackTrace? stackTrace, {String? library}) async {
|
||||||
final appDocDir = await getApplicationDocumentsDirectory();
|
if (_file == null) {
|
||||||
|
final appDocDir = await getApplicationDocumentsDirectory();
|
||||||
|
|
||||||
|
_file = File('${appDocDir.path}/error.txt');
|
||||||
|
}
|
||||||
|
|
||||||
final file = File('${appDocDir.path}/error.txt');
|
|
||||||
final exception = {
|
final exception = {
|
||||||
"${DateTime.now()}": {
|
"${DateTime.now()}": {
|
||||||
"Error": "$error\n\n",
|
"Error": "$error\n\n",
|
||||||
|
@ -33,14 +37,14 @@ class ExceptionHandler {
|
||||||
==========================================================\n\n''';
|
==========================================================\n\n''';
|
||||||
|
|
||||||
/// don't save existing errors
|
/// don't save existing errors
|
||||||
if (file.existsSync()) {
|
if (_file!.existsSync()) {
|
||||||
final String fileContent = await file.readAsString();
|
final String fileContent = await _file!.readAsString();
|
||||||
if (fileContent.contains("${exception.values.first}")) {
|
if (fileContent.contains("${exception.values.first}")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
file.writeAsStringSync(
|
_file!.writeAsStringSync(
|
||||||
"$exception $separator",
|
"$exception $separator",
|
||||||
mode: FileMode.append,
|
mode: FileMode.append,
|
||||||
);
|
);
|
||||||
|
@ -48,16 +52,18 @@ class ExceptionHandler {
|
||||||
|
|
||||||
static void _sendExceptionFile() async {
|
static void _sendExceptionFile() async {
|
||||||
try {
|
try {
|
||||||
final appDocDir = await getApplicationDocumentsDirectory();
|
if (_file == null) {
|
||||||
|
final appDocDir = await getApplicationDocumentsDirectory();
|
||||||
|
|
||||||
final file = File('${appDocDir.path}/error.txt');
|
_file = File('${appDocDir.path}/error.txt');
|
||||||
|
}
|
||||||
|
|
||||||
await _addDeviceInfo(file);
|
await _addDeviceInfo(_file!);
|
||||||
|
|
||||||
final MailOptions mailOptions = MailOptions(
|
final MailOptions mailOptions = MailOptions(
|
||||||
subject: 'Mobile App Issue',
|
subject: 'Mobile App Issue',
|
||||||
recipients: ['support@cakewallet.com'],
|
recipients: ['support@cakewallet.com'],
|
||||||
attachments: [file.path],
|
attachments: [_file!.path],
|
||||||
);
|
);
|
||||||
|
|
||||||
final result = await FlutterMailer.send(mailOptions);
|
final result = await FlutterMailer.send(mailOptions);
|
||||||
|
@ -67,7 +73,7 @@ class ExceptionHandler {
|
||||||
if (result.name == MailerResponse.sent.name ||
|
if (result.name == MailerResponse.sent.name ||
|
||||||
result.name == MailerResponse.saved.name ||
|
result.name == MailerResponse.saved.name ||
|
||||||
result.name == MailerResponse.android.name) {
|
result.name == MailerResponse.android.name) {
|
||||||
file.writeAsString("", mode: FileMode.write);
|
_file!.writeAsString("", mode: FileMode.write);
|
||||||
}
|
}
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
_saveException(e.toString(), s);
|
_saveException(e.toString(), s);
|
||||||
|
|
|
@ -9,7 +9,7 @@ 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(hours: 12)),
|
||||||
SyncMode("Aggressive", SyncType.aggressive, Duration(hours: 6)),
|
SyncMode("Aggressive", SyncType.aggressive, Duration(hours: 3)),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -15,15 +15,15 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN)
|
||||||
APP_ANDROID_TYPE=$1
|
APP_ANDROID_TYPE=$1
|
||||||
|
|
||||||
MONERO_COM_NAME="Monero.com"
|
MONERO_COM_NAME="Monero.com"
|
||||||
MONERO_COM_VERSION="1.10.2"
|
MONERO_COM_VERSION="1.10.3"
|
||||||
MONERO_COM_BUILD_NUMBER=74
|
MONERO_COM_BUILD_NUMBER=75
|
||||||
MONERO_COM_BUNDLE_ID="com.monero.app"
|
MONERO_COM_BUNDLE_ID="com.monero.app"
|
||||||
MONERO_COM_PACKAGE="com.monero.app"
|
MONERO_COM_PACKAGE="com.monero.app"
|
||||||
MONERO_COM_SCHEME="monero.com"
|
MONERO_COM_SCHEME="monero.com"
|
||||||
|
|
||||||
CAKEWALLET_NAME="Cake Wallet"
|
CAKEWALLET_NAME="Cake Wallet"
|
||||||
CAKEWALLET_VERSION="4.13.2"
|
CAKEWALLET_VERSION="4.13.3"
|
||||||
CAKEWALLET_BUILD_NUMBER=191
|
CAKEWALLET_BUILD_NUMBER=192
|
||||||
CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet"
|
CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet"
|
||||||
CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet"
|
CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet"
|
||||||
CAKEWALLET_SCHEME="cakewallet"
|
CAKEWALLET_SCHEME="cakewallet"
|
||||||
|
|
|
@ -13,13 +13,13 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN)
|
||||||
APP_IOS_TYPE=$1
|
APP_IOS_TYPE=$1
|
||||||
|
|
||||||
MONERO_COM_NAME="Monero.com"
|
MONERO_COM_NAME="Monero.com"
|
||||||
MONERO_COM_VERSION="1.10.2"
|
MONERO_COM_VERSION="1.10.3"
|
||||||
MONERO_COM_BUILD_NUMBER=72
|
MONERO_COM_BUILD_NUMBER=73
|
||||||
MONERO_COM_BUNDLE_ID="com.cakewallet.monero"
|
MONERO_COM_BUNDLE_ID="com.cakewallet.monero"
|
||||||
|
|
||||||
CAKEWALLET_NAME="Cake Wallet"
|
CAKEWALLET_NAME="Cake Wallet"
|
||||||
CAKEWALLET_VERSION="4.13.2"
|
CAKEWALLET_VERSION="4.13.3"
|
||||||
CAKEWALLET_BUILD_NUMBER=210
|
CAKEWALLET_BUILD_NUMBER=212
|
||||||
CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet"
|
CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet"
|
||||||
|
|
||||||
HAVEN_NAME="Haven"
|
HAVEN_NAME="Haven"
|
||||||
|
|
|
@ -16,13 +16,13 @@ if [ -n "$1" ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
MONERO_COM_NAME="Monero.com"
|
MONERO_COM_NAME="Monero.com"
|
||||||
MONERO_COM_VERSION="1.0.2"
|
MONERO_COM_VERSION="1.0.3"
|
||||||
MONERO_COM_BUILD_NUMBER=4
|
MONERO_COM_BUILD_NUMBER=5
|
||||||
MONERO_COM_BUNDLE_ID="com.cakewallet.monero"
|
MONERO_COM_BUNDLE_ID="com.cakewallet.monero"
|
||||||
|
|
||||||
CAKEWALLET_NAME="Cake Wallet"
|
CAKEWALLET_NAME="Cake Wallet"
|
||||||
CAKEWALLET_VERSION="1.6.2"
|
CAKEWALLET_VERSION="1.6.3"
|
||||||
CAKEWALLET_BUILD_NUMBER=52
|
CAKEWALLET_BUILD_NUMBER=53
|
||||||
CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet"
|
CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet"
|
||||||
|
|
||||||
if ! [[ " ${TYPES[*]} " =~ " ${APP_MACOS_TYPE} " ]]; then
|
if ! [[ " ${TYPES[*]} " =~ " ${APP_MACOS_TYPE} " ]]; then
|
||||||
|
|
|
@ -13,10 +13,20 @@ void main(List<String> args) async {
|
||||||
|
|
||||||
print('Appending "$name": "$text"');
|
print('Appending "$name": "$text"');
|
||||||
|
|
||||||
|
// add translation to all languages:
|
||||||
for (var lang in langs) {
|
for (var lang in langs) {
|
||||||
final fileName = getArbFileName(lang);
|
final fileName = getArbFileName(lang);
|
||||||
final translation = await getTranslation(text, lang);
|
final translation = await getTranslation(text, lang);
|
||||||
|
|
||||||
appendStringToArbFile(fileName, name, translation);
|
appendStringToArbFile(fileName, name, translation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
print('Alphabetizing all files...');
|
||||||
|
|
||||||
|
for (var lang in langs) {
|
||||||
|
final fileName = getArbFileName(lang);
|
||||||
|
alphabetizeArbFile(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
print('Done!');
|
||||||
}
|
}
|
|
@ -525,6 +525,8 @@ 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_info.dart';
|
import 'package:cw_core/wallet_info.dart';
|
||||||
import 'package:cw_core/wallet_service.dart';
|
import 'package:cw_core/wallet_service.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:web3dart/web3dart.dart';
|
||||||
|
|
||||||
""";
|
""";
|
||||||
const ethereumCWHeaders = """
|
const ethereumCWHeaders = """
|
||||||
|
@ -540,8 +542,6 @@ import 'package:cw_ethereum/ethereum_wallet.dart';
|
||||||
import 'package:cw_ethereum/ethereum_wallet_service.dart';
|
import 'package:cw_ethereum/ethereum_wallet_service.dart';
|
||||||
|
|
||||||
import 'package:eth_sig_util/util/utils.dart';
|
import 'package:eth_sig_util/util/utils.dart';
|
||||||
import 'package:hive/hive.dart';
|
|
||||||
import 'package:web3dart/web3dart.dart';
|
|
||||||
|
|
||||||
""";
|
""";
|
||||||
const ethereumCwPart = "part 'cw_ethereum.dart';";
|
const ethereumCwPart = "part 'cw_ethereum.dart';";
|
||||||
|
@ -617,6 +617,8 @@ 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_info.dart';
|
import 'package:cw_core/wallet_info.dart';
|
||||||
import 'package:cw_core/wallet_service.dart';
|
import 'package:cw_core/wallet_service.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:web3dart/web3dart.dart';
|
||||||
|
|
||||||
""";
|
""";
|
||||||
const polygonCWHeaders = """
|
const polygonCWHeaders = """
|
||||||
|
@ -631,8 +633,6 @@ import 'package:cw_polygon/polygon_client.dart';
|
||||||
import 'package:cw_polygon/polygon_wallet.dart';
|
import 'package:cw_polygon/polygon_wallet.dart';
|
||||||
import 'package:cw_polygon/polygon_wallet_service.dart';
|
import 'package:cw_polygon/polygon_wallet_service.dart';
|
||||||
|
|
||||||
import 'package:hive/hive.dart';
|
|
||||||
import 'package:web3dart/web3dart.dart';
|
|
||||||
import 'package:eth_sig_util/util/utils.dart';
|
import 'package:eth_sig_util/util/utils.dart';
|
||||||
|
|
||||||
""";
|
""";
|
||||||
|
|
|
@ -30,8 +30,10 @@ void main(List<String> args) async {
|
||||||
missingDefaults[key] = arbObj[key] as String;
|
missingDefaults[key] = arbObj[key] as String;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (missingDefaults.isNotEmpty)
|
if (missingDefaults.isNotEmpty) {
|
||||||
await appendTranslations(lang, missingDefaults);
|
await appendTranslations(lang, missingDefaults);
|
||||||
|
alphabetizeArbFile(fileName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,9 +47,7 @@ Map<String, dynamic> readArbFile(File file) {
|
||||||
}
|
}
|
||||||
|
|
||||||
String getArbFileName(String lang) {
|
String getArbFileName(String lang) {
|
||||||
final shortLang = lang
|
final shortLang = lang.split("-").first;
|
||||||
.split("-")
|
|
||||||
.first;
|
|
||||||
return "./res/values/strings_$shortLang.arb";
|
return "./res/values/strings_$shortLang.arb";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,3 +64,25 @@ List<String> getMissingKeysInArbFile(String fileName, Iterable<String> langKeys)
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void alphabetizeArbFile(String fileName) {
|
||||||
|
final file = File(fileName);
|
||||||
|
final arbObj = readArbFile(file);
|
||||||
|
|
||||||
|
final sortedKeys = arbObj.keys.toList()
|
||||||
|
..sort((a, b) => a.toLowerCase().compareTo(b.toLowerCase()));
|
||||||
|
final Map<String, dynamic> sortedArbObj = {};
|
||||||
|
for (var key in sortedKeys) {
|
||||||
|
sortedArbObj[key] = arbObj[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
final outputContent = json
|
||||||
|
.encode(sortedArbObj)
|
||||||
|
.replaceAll('","', '",\n "')
|
||||||
|
.replaceAll('{"', '{\n "')
|
||||||
|
.replaceAll('"}', '"\n}')
|
||||||
|
.replaceAll('":"', '": "')
|
||||||
|
.replaceAll('\$ {', '\${');
|
||||||
|
|
||||||
|
file.writeAsStringSync(outputContent);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue