diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index b812907ad..2fa8d4159 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -456,7 +456,8 @@ abstract class ElectrumWalletBase await updateBalance(); await updateFeeRates(); - _updateFeeRateTimer ??= + _updateFeeRateTimer?.cancel(); + _updateFeeRateTimer = Timer.periodic(const Duration(minutes: 1), (timer) async => await updateFeeRates()); if (alwaysScan == true) { @@ -471,6 +472,13 @@ abstract class ElectrumWalletBase } } + @action + @override + Future stopSync() async { + syncStatus = StoppedSyncingSyncStatus(); + _updateFeeRateTimer?.cancel(); + } + @action Future updateFeeRates() async { if (await checkIfMempoolAPIIsEnabled()) { @@ -1113,7 +1121,8 @@ abstract class ElectrumWalletBase }); } - unspentCoins.removeWhere((utxo) => estimatedTx.utxos.any((e) => e.utxo.txHash == utxo.hash)); + unspentCoins + .removeWhere((utxo) => estimatedTx.utxos.any((e) => e.utxo.txHash == utxo.hash)); await updateBalance(); }); @@ -2069,7 +2078,8 @@ abstract class ElectrumWalletBase _isTryingToConnect = true; Timer(Duration(seconds: 5), () { - if (this.syncStatus is NotConnectedSyncStatus || this.syncStatus is LostConnectionSyncStatus) { + if (this.syncStatus is NotConnectedSyncStatus || + this.syncStatus is LostConnectionSyncStatus) { this.electrumClient.connectToUri( node!.uri, useSSL: node!.useSSL ?? false, diff --git a/lib/entities/background_tasks.dart b/lib/entities/background_tasks.dart index 6cb3ba079..1ec74b655 100644 --- a/lib/entities/background_tasks.dart +++ b/lib/entities/background_tasks.dart @@ -32,6 +32,7 @@ const notificationChannelDescription = 'Cake Wallet Background Service'; const DELAY_SECONDS_BEFORE_SYNC_START = 15; void setNotificationStandby(FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin) async { + flutterLocalNotificationsPlugin.cancelAll(); flutterLocalNotificationsPlugin.show( notificationId, initialNotificationTitle, @@ -181,22 +182,37 @@ Future onStart(ServiceInstance service) async { for (int i = 0; i < syncingWallets.length; i++) { final wallet = syncingWallets[i]; final syncProgress = ((wallet!.syncStatus.progress()) * 100).toStringAsPrecision(5); - title += "${wallet.name}-${syncProgress}% "; + // title += "${wallet.name}-${syncProgress}% "; + String title = "${wallet.type} - ${wallet.name} - ${syncProgress}% Synced"; + + flutterLocalNotificationsPlugin.show( + notificationId, + title, + 'Background sync - ${DateTime.now()}', + NotificationDetails( + android: AndroidNotificationDetails( + "${notificationChannelId}_$i", + "${notificationChannelName}_$i", + icon: 'ic_bg_service_small', + ongoing: true, + ), + ), + ); } - flutterLocalNotificationsPlugin.show( - notificationId, - title, - 'Background sync - ${DateTime.now()}', - const NotificationDetails( - android: AndroidNotificationDetails( - notificationChannelId, - notificationChannelName, - icon: 'ic_bg_service_small', - ongoing: true, - ), - ), - ); + // flutterLocalNotificationsPlugin.show( + // notificationId, + // title, + // 'Background sync - ${DateTime.now()}', + // const NotificationDetails( + // android: AndroidNotificationDetails( + // notificationChannelId, + // notificationChannelName, + // icon: 'ic_bg_service_small', + // ongoing: true, + // ), + // ), + // ); }); }); } @@ -211,14 +227,7 @@ Future onIosBackground(ServiceInstance service) async { Future initializeService(FlutterBackgroundService bgService, bool useNotifications) async { if (useNotifications) { - const AndroidNotificationChannel channel = AndroidNotificationChannel( - notificationChannelId, - notificationChannelName, - description: notificationChannelDescription, - importance: Importance.low, - ); - - final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = + FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); if (Platform.isIOS || Platform.isAndroid) { @@ -230,13 +239,17 @@ Future initializeService(FlutterBackgroundService bgService, bool useNotif ); } - await flutterLocalNotificationsPlugin - .resolvePlatformSpecificImplementation() - ?.createNotificationChannel(channel); - - flutterLocalNotificationsPlugin - .resolvePlatformSpecificImplementation() - ?.requestNotificationsPermission(); + for (int i = 0; i < 10; i++) { + AndroidNotificationChannel channel = AndroidNotificationChannel( + "${notificationChannelId}_$i", + "${notificationChannelName}_$i", + description: notificationChannelDescription, + importance: Importance.low, + ); + await flutterLocalNotificationsPlugin + .resolvePlatformSpecificImplementation() + ?.createNotificationChannel(channel); + } setNotificationStandby(flutterLocalNotificationsPlugin); } @@ -273,10 +286,13 @@ Future initializeService(FlutterBackgroundService bgService, bool useNotif class BackgroundTasks { FlutterBackgroundService bgService = FlutterBackgroundService(); + FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = + FlutterLocalNotificationsPlugin(); void updateServiceState(bool foreground, bool useNotifications) async { if (foreground) { bgService.invoke('stopService'); + await Future.delayed(const Duration(seconds: 2)); initializeService(bgService, useNotifications); } else { bgService.invoke("setBackground"); @@ -313,10 +329,15 @@ class BackgroundTasks { return; } - final SyncMode syncMode = settingsStore.currentSyncMode; final bool syncAll = settingsStore.currentSyncAll; + if (settingsStore.showSyncNotification) { + flutterLocalNotificationsPlugin + .resolvePlatformSpecificImplementation() + ?.requestNotificationsPermission(); + } + if (syncMode.type == SyncType.disabled || !FeatureFlag.isBackgroundSyncEnabled) { bgService.invoke('stopService'); return; diff --git a/lib/src/screens/root/root.dart b/lib/src/screens/root/root.dart index 9c45a9842..56456e1e0 100644 --- a/lib/src/screens/root/root.dart +++ b/lib/src/screens/root/root.dart @@ -132,6 +132,7 @@ class RootState extends State with WidgetsBindingObserver { @override void didChangeAppLifecycleState(AppLifecycleState state) { + final syncingWalletTypes = [WalletType.litecoin, WalletType.monero, WalletType.bitcoin]; switch (state) { case AppLifecycleState.paused: if (isQrScannerShown) { @@ -142,7 +143,7 @@ class RootState extends State with WidgetsBindingObserver { setState(() => _setInactive(true)); } - if (widget.appStore.wallet?.type == WalletType.litecoin) { + if (syncingWalletTypes.contains(widget.appStore.wallet?.type)) { widget.appStore.wallet?.stopSync(); } @@ -172,7 +173,7 @@ class RootState extends State with WidgetsBindingObserver { return; } wasInBackground = false; - if (widget.appStore.wallet?.type == WalletType.litecoin) { + if (syncingWalletTypes.contains(widget.appStore.wallet?.type)) { // wait a few seconds before starting the sync make sure the background service is fully exited: Future.delayed(const Duration(seconds: 3), () { widget.appStore.wallet?.startSync(); @@ -180,13 +181,15 @@ class RootState extends State with WidgetsBindingObserver { } break; case AppLifecycleState.paused: - getIt.get().updateServiceState(false, showNotifications); + // TODO: experimental: maybe should uncomment this: + // getIt.get().updateServiceState(false, showNotifications); case AppLifecycleState.inactive: case AppLifecycleState.detached: default: // if we enter any state other than resumed start a timer for 30 seconds // after which we'll consider the app to be in the background _stateTimer?.cancel(); + // TODO: bump this to > 30 seconds when testing is done: _stateTimer = Timer(const Duration(seconds: 10), () async { wasInBackground = true; getIt.get().updateServiceState(false, showNotifications); diff --git a/lib/src/screens/settings/connection_sync_page.dart b/lib/src/screens/settings/connection_sync_page.dart index 0076c1caf..914070ba7 100644 --- a/lib/src/screens/settings/connection_sync_page.dart +++ b/lib/src/screens/settings/connection_sync_page.dart @@ -78,6 +78,34 @@ class ConnectionSyncPage extends BasePage { // } }); }), + Observer(builder: (context) { + return SettingsSwitcherCell( + title: "T: show sync% in notification", + value: dashboardViewModel.showSyncNotification, + onValueChange: (BuildContext _, bool isEnabled) async { + dashboardViewModel.setShowSyncNotification(isEnabled); + // if (!isEnabled) { + // final bool confirmation = await showPopUp( + // context: context, + // builder: (BuildContext context) { + // return AlertWithTwoActions( + // alertTitle: S.of(context).warning, + // alertContent: S.of(context).disable_fee_api_warning, + // rightButtonText: S.of(context).confirm, + // leftButtonText: S.of(context).cancel, + // actionRightButton: () => Navigator.of(context).pop(true), + // actionLeftButton: () => Navigator.of(context).pop(false)); + // }) ?? + // false; + // if (confirmation) { + // _privacySettingsViewModel.setUseMempoolFeeAPI(isEnabled); + // } + // return; + // } + + }, + ); + }), Observer(builder: (context) { return SettingsSwitcherCell( title: S.current.sync_all_wallets, @@ -106,7 +134,8 @@ class ConnectionSyncPage extends BasePage { }, ), if (isWalletConnectCompatibleChain(dashboardViewModel.wallet.type) && - !dashboardViewModel.wallet.isHardwareWallet) ...[ // ToDo: Remove this line once WalletConnect is implemented + !dashboardViewModel.wallet.isHardwareWallet) ...[ + // ToDo: Remove this line once WalletConnect is implemented WalletConnectTile( onTap: () => Navigator.of(context).pushNamed(Routes.walletConnectConnectionsListing), ), @@ -138,4 +167,4 @@ class ConnectionSyncPage extends BasePage { }, ); } -} \ No newline at end of file +} diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 6e07afb3a..ecb0a47d5 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -714,6 +714,12 @@ abstract class DashboardViewModelBase with Store { @computed bool get syncAll => settingsStore.currentSyncAll; + @action + void setShowSyncNotification(bool value) => settingsStore.showSyncNotification = value; + + @computed + bool get showSyncNotification => settingsStore.showSyncNotification; + @action void setSyncAll(bool value) => settingsStore.currentSyncAll = value;