works but is still buggy

This commit is contained in:
Matthew Fosse 2024-08-29 17:53:10 -04:00
parent 879c29fe7c
commit 9b96bde7f2
6 changed files with 97 additions and 214 deletions

View file

@ -300,10 +300,11 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
@action @action
@override @override
Future<void> stopSync() async { Future<void> stopSync() async {
print("STOPPING SYNC");
_syncTimer?.cancel(); _syncTimer?.cancel();
_utxoStream?.cancel(); _utxoStream?.cancel();
await CwMweb.stop(); await CwMweb.stop();
syncStatus = NotSyncingSyncStatus(); syncStatus = StoppedSyncingSyncStatus();
} }
Future<void> initMwebUtxosBox() async { Future<void> initMwebUtxosBox() async {
@ -888,7 +889,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
// generate inital mweb addresses: // generate inital mweb addresses:
(walletAddresses as LitecoinWalletAddresses).topUpMweb(0); (walletAddresses as LitecoinWalletAddresses).topUpMweb(0);
} }
stopSync();
startSync(); startSync();
} }

View file

@ -83,7 +83,8 @@ class LostConnectionSyncStatus extends NotConnectedSyncStatus {
String toString() => 'Reconnecting'; String toString() => 'Reconnecting';
} }
class NotSyncingSyncStatus extends SyncStatus { // when the application has been paused by the OS:
class StoppedSyncingSyncStatus extends SyncStatus {
@override @override
double progress() => 0.0; double progress() => 0.0;

View file

@ -187,77 +187,32 @@ void callbackDispatcher() {
Future<void> onStart(ServiceInstance service) async { Future<void> onStart(ServiceInstance service) async {
print("BACKGROUND SERVICE STARTED!"); print("BACKGROUND SERVICE STARTED!");
// Only available for flutter 3.0.0 and later // commented because the behavior appears to be bugged:
DartPluginRegistrant.ensureInitialized(); // DartPluginRegistrant.ensureInitialized();
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin(); FlutterLocalNotificationsPlugin();
// this will be used as notification channel id // this will be used as notification channel id
// this will be used for notification id, So you can update your custom notification with this id.
const notificationChannelId = 'my_foreground'; const notificationChannelId = 'my_foreground';
const notificationChannelName = 'MY FOREGROUND SERVICE';
// this will be used for notification id, So you can update your custom notification with this id.
const notificationId = 888; const notificationId = 888;
if (service is AndroidServiceInstance) {
service.on('setAsForeground').listen((event) {
service.setAsForegroundService();
});
service.on('setAsBackground').listen((event) {
service.setAsBackgroundService();
});
}
service.on('stopService').listen((event) { service.on('stopService').listen((event) {
service.stopSelf(); service.stopSelf();
}); });
// // bring to foreground
// Timer.periodic(const Duration(seconds: 1), (timer) async {
// if (service is AndroidServiceInstance) {
// if (await service.isForegroundService()) {
// flutterLocalNotificationsPlugin.show(
// notificationId,
// 'COOL SERVICE',
// 'Awesome ${DateTime.now()}',
// const NotificationDetails(
// android: AndroidNotificationDetails(
// notificationChannelId,
// 'MY FOREGROUND SERVICE',
// icon: 'ic_bg_service_small',
// ongoing: true,
// ),
// ),
// );
// } else {
// flutterLocalNotificationsPlugin.show(
// notificationId,
// 'BACKGROUND SERVICE?',
// 'Awesome ${DateTime.now()}',
// const NotificationDetails(
// android: AndroidNotificationDetails(
// notificationChannelId,
// 'MY FOREGROUND SERVICE',
// icon: 'ic_bg_service_small',
// ongoing: true,
// ),
// ),
// );
// }
// }
// });
// /// The work manager runs on a separate isolate from the main flutter isolate.
// /// thus we initialize app configs first; hive, getIt, etc...
// await initializeAppConfigs();
service.on('status').listen((event) async { service.on('status').listen((event) async {
print(event); print(event);
}); });
bool bgSyncStarted = false; bool bgSyncStarted = false;
service.on('foreground').listen((event) async {
bgSyncStarted = false;
});
service.on('startBgSync').listen((event) async { service.on('startBgSync').listen((event) async {
if (bgSyncStarted) { if (bgSyncStarted) {
return; return;
@ -271,22 +226,29 @@ Future<void> onStart(ServiceInstance service) async {
Timer.periodic(const Duration(milliseconds: 1500), (timer) { Timer.periodic(const Duration(milliseconds: 1500), (timer) {
final wallet = getIt.get<AppStore>().wallet; final wallet = getIt.get<AppStore>().wallet;
final syncProgress = wallet?.syncStatus.progress(); final syncProgress = ((wallet?.syncStatus.progress() ?? 0) * 100).toStringAsPrecision(3);
flutterLocalNotificationsPlugin.show( flutterLocalNotificationsPlugin.show(
notificationId, notificationId,
"${syncProgress}", "${syncProgress}% Synced",
'Awesome ${DateTime.now()}', 'Mweb background sync - ${DateTime.now()}',
const NotificationDetails( const NotificationDetails(
android: AndroidNotificationDetails( android: AndroidNotificationDetails(
notificationChannelId, notificationChannelId,
'MY FOREGROUND SERVICE', notificationChannelName,
icon: 'ic_bg_service_small', icon: 'ic_bg_service_small',
ongoing: true, ongoing: true,
), ),
), ),
); );
}); });
// print("stopping in ten seconds!");
// Timer(const Duration(seconds: 10), () {
// // stop the service after 10 seconds
// print("stopping now!");
// service.stopSelf();
// });
}); });
// final List<WalletListItem> ltcWallets = getIt // final List<WalletListItem> ltcWallets = getIt
@ -385,23 +347,6 @@ Future<bool> onIosBackground(ServiceInstance service) async {
} }
Future<void> initializeService(FlutterBackgroundService bgService) async { Future<void> initializeService(FlutterBackgroundService bgService) async {
// final service = FlutterBackgroundService();
// await service.configure(
// iosConfiguration: IosConfiguration(
// autoStart: true,
// onForeground: onStart,
// onBackground: onIosBackground,
// ),
// androidConfiguration: AndroidConfiguration(
// autoStart: true,
// onStart: onStart,
// isForegroundMode: false,
// autoStartOnBoot: true,
// ),
// );
/// OPTIONAL, using custom notification channel id
const AndroidNotificationChannel channel = AndroidNotificationChannel( const AndroidNotificationChannel channel = AndroidNotificationChannel(
'my_foreground', // id 'my_foreground', // id
'MY FOREGROUND SERVICE', // title 'MY FOREGROUND SERVICE', // title
@ -429,40 +374,52 @@ Future<void> initializeService(FlutterBackgroundService bgService) async {
.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>() .resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()
?.requestNotificationsPermission(); ?.requestNotificationsPermission();
const initialNotificationTitle = 'Cake Background Sync';
const initialNotificationContent = 'On standby - app is in the foreground';
const notificationId = 888;
const notificationChannelId = 'my_foreground';
const notificationChannelName = 'MY FOREGROUND SERVICE';
flutterLocalNotificationsPlugin.show(
notificationId,
initialNotificationTitle,
initialNotificationContent,
const NotificationDetails(
android: AndroidNotificationDetails(
notificationChannelId,
notificationChannelName,
icon: 'ic_bg_service_small',
ongoing: true,
),
),
);
await bgService.configure( await bgService.configure(
androidConfiguration: AndroidConfiguration( androidConfiguration: AndroidConfiguration(
// this will be executed when app is in foreground or background in separated isolate
onStart: onStart, onStart: onStart,
// auto start service
autoStart: true, autoStart: true,
isForegroundMode: true, isForegroundMode: true,
notificationChannelId: notificationChannelId,
notificationChannelId: 'my_foreground', initialNotificationTitle: initialNotificationTitle,
initialNotificationTitle: 'AWESOME SERVICE', initialNotificationContent: initialNotificationContent,
initialNotificationContent: 'Initializing', foregroundServiceNotificationId: notificationId,
foregroundServiceNotificationId: 888,
foregroundServiceTypes: [AndroidForegroundType.dataSync], foregroundServiceTypes: [AndroidForegroundType.dataSync],
), ),
iosConfiguration: IosConfiguration( iosConfiguration: IosConfiguration(
// auto start service
autoStart: true, autoStart: true,
// this will be executed when app is in foreground in separated isolate
onForeground: onStart, onForeground: onStart,
// you have to enable background fetch capability on xcode project
onBackground: onIosBackground, onBackground: onIosBackground,
), ),
); );
var wallet = getIt.get<AppStore>().wallet; bgService.invoke("foreground");
Timer.periodic(const Duration(milliseconds: 1000), (timer) { Timer.periodic(const Duration(milliseconds: 1000), (timer) {
var wallet = getIt.get<AppStore>().wallet;
if (wallet?.syncStatus.toString() == "stopped") { if (wallet?.syncStatus.toString() == "stopped") {
bgService.invoke("startBgSync"); bgService.invoke("startBgSync");
timer.cancel();
} }
// bgService.invoke("status", {"status": wallet?.syncStatus.toString()});
}); });
} }
@ -496,93 +453,19 @@ class BackgroundTasks {
return; return;
} }
// try {
// bool isServiceRunning = await bgService.isRunning();
// if (isServiceRunning) {
// return;
// // print("Service is ALREADY running!");
// // bgService.invoke('stopService');
// }
// } catch (_) {}
await initializeService(bgService); await initializeService(bgService);
// await service.configure(
// androidConfiguration: AndroidConfiguration(
// // this will be executed when app is in foreground or background in separated isolate
// onStart: onStart,
// // auto start service
// autoStart: true,
// isForegroundMode: true,
// notificationChannelId: notificationChannelId, // this must match with notification channel you created above.
// initialNotificationTitle: 'AWESOME SERVICE',
// initialNotificationContent: 'Initializing',
// foregroundServiceNotificationId: notificationId,
// );
// await Workmanager().initialize(
// callbackDispatcher,
// isInDebugMode: true,
// );
// final inputData = <String, dynamic>{"sync_all": syncAll};
// final constraints = Constraints(
// networkType:
// syncMode.type == SyncType.unobtrusive ? NetworkType.unmetered : NetworkType.connected,
// requiresBatteryNotLow: syncMode.type == SyncType.unobtrusive,
// requiresCharging: syncMode.type == SyncType.unobtrusive,
// requiresDeviceIdle: syncMode.type == SyncType.unobtrusive,
// );
// if (Platform.isIOS && syncMode.type == SyncType.unobtrusive) {
// // await Workmanager().registerOneOffTask(
// // moneroSyncTaskKey,
// // moneroSyncTaskKey,
// // initialDelay: syncMode.frequency,
// // existingWorkPolicy: ExistingWorkPolicy.replace,
// // inputData: inputData,
// // constraints: constraints,
// // );
// await Workmanager().registerOneOffTask(
// mwebSyncTaskKey,
// mwebSyncTaskKey,
// initialDelay: Duration(seconds: 30),
// existingWorkPolicy: ExistingWorkPolicy.replace,
// inputData: inputData,
// constraints: constraints,
// );
// return;
// }
// await Workmanager().registerPeriodicTask(
// moneroSyncTaskKey,
// moneroSyncTaskKey,
// initialDelay: syncMode.frequency,
// frequency: syncMode.frequency,
// existingWorkPolicy: changeExisting ? ExistingWorkPolicy.replace : ExistingWorkPolicy.keep,
// inputData: inputData,
// constraints: constraints,
// );
// await Workmanager().registerPeriodicTask(
// mwebSyncTaskKey,
// mwebSyncTaskKey,
// initialDelay: syncMode.frequency,
// frequency: syncMode.frequency,
// existingWorkPolicy: changeExisting ? ExistingWorkPolicy.replace : ExistingWorkPolicy.keep,
// inputData: inputData,
// constraints: constraints,
// );
} catch (error, stackTrace) { } catch (error, stackTrace) {
print(error); print(error);
print(stackTrace); print(stackTrace);
} }
} }
void cancelSyncTask() {
// try {
// Workmanager().cancelByUniqueName(moneroSyncTaskKey);
// } catch (error, stackTrace) {
// print(error);
// print(stackTrace);
// }
// try {
// Workmanager().cancelByUniqueName(mwebSyncTaskKey);
// } catch (error, stackTrace) {
// print(error);
// print(stackTrace);
// }
}
} }

View file

@ -1,3 +1,5 @@
import 'dart:isolate';
import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/di.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:cake_wallet/store/app_store.dart'; import 'package:cake_wallet/store/app_store.dart';
@ -8,12 +10,8 @@ import 'package:cake_wallet/core/wallet_loading_service.dart';
Future<void> loadCurrentWallet({String? password}) async { Future<void> loadCurrentWallet({String? password}) async {
final appStore = getIt.get<AppStore>(); final appStore = getIt.get<AppStore>();
final name = getIt final name = getIt.get<SharedPreferences>().getString(PreferencesKey.currentWalletName);
.get<SharedPreferences>() final typeRaw = getIt.get<SharedPreferences>().getInt(PreferencesKey.currentWalletType) ?? 0;
.getString(PreferencesKey.currentWalletName);
final typeRaw =
getIt.get<SharedPreferences>().getInt(PreferencesKey.currentWalletType) ??
0;
if (name == null) { if (name == null) {
throw Exception('Incorrect current wallet name: $name'); throw Exception('Incorrect current wallet name: $name');
@ -21,12 +19,9 @@ Future<void> loadCurrentWallet({String? password}) async {
final type = deserializeFromInt(typeRaw); final type = deserializeFromInt(typeRaw);
final walletLoadingService = getIt.get<WalletLoadingService>(); final walletLoadingService = getIt.get<WalletLoadingService>();
final wallet = await walletLoadingService.load( final wallet = await walletLoadingService.load(type, name, password: password);
type,
name,
password: password);
await appStore.changeCurrentWallet(wallet); await appStore.changeCurrentWallet(wallet);
// TODO: potential for infinite loop here since this is run when the wallet is loaded for syncing from the bg: // TODO: potential for infinite loop here since this is run when the wallet is loaded for syncing from the bg:
// getIt.get<BackgroundTasks>().registerSyncTask(); getIt.get<BackgroundTasks>().registerSyncTask();
} }

View file

@ -43,13 +43,15 @@ class RootState extends State<Root> with WidgetsBindingObserver {
: _isInactiveController = StreamController<bool>.broadcast(), : _isInactiveController = StreamController<bool>.broadcast(),
_isInactive = false, _isInactive = false,
_requestAuth = true, _requestAuth = true,
_postFrameCallback = false; _postFrameCallback = false,
_previousState = AppLifecycleState.resumed;
Stream<bool> get isInactive => _isInactiveController.stream; Stream<bool> get isInactive => _isInactiveController.stream;
StreamController<bool> _isInactiveController; StreamController<bool> _isInactiveController;
bool _isInactive; bool _isInactive;
bool _postFrameCallback; bool _postFrameCallback;
bool _requestAuth; bool _requestAuth;
AppLifecycleState _previousState;
StreamSubscription<Uri?>? stream; StreamSubscription<Uri?>? stream;
ReactionDisposer? _walletReactionDisposer; ReactionDisposer? _walletReactionDisposer;
@ -148,13 +150,18 @@ class RootState extends State<Root> with WidgetsBindingObserver {
}); });
} }
}); });
if (widget.appStore.wallet?.type == WalletType.litecoin) {
widget.appStore.wallet?.startSync(); // prevent triggering startSync from the notifications menu on android / other non-paused states:
if (_previousState == AppLifecycleState.paused) {
if (widget.appStore.wallet?.type == WalletType.litecoin) {
widget.appStore.wallet?.startSync();
}
} }
break; break;
default: default:
break; break;
} }
_previousState = state;
} }
@override @override

View file

@ -56,29 +56,26 @@ class ConnectionSyncPage extends BasePage {
if (Platform.isIOS) return; if (Platform.isIOS) return;
if (syncMode.type != SyncType.disabled) { // if (syncMode.type != SyncType.disabled) {
final isDisabled = await isBatteryOptimizationDisabled(); // final isDisabled = await isBatteryOptimizationDisabled();
// if (isDisabled) return;
if (isDisabled) return; // await showPopUp<void>(
// context: context,
await showPopUp<void>( // builder: (BuildContext dialogContext) {
context: context, // return AlertWithTwoActions(
builder: (BuildContext dialogContext) { // alertTitle: S.current.disableBatteryOptimization,
return AlertWithTwoActions( // alertContent: S.current.disableBatteryOptimizationDescription,
alertTitle: S.current.disableBatteryOptimization, // leftButtonText: S.of(context).cancel,
alertContent: S.current.disableBatteryOptimizationDescription, // rightButtonText: S.of(context).ok,
leftButtonText: S.of(context).cancel, // actionLeftButton: () => Navigator.of(dialogContext).pop(),
rightButtonText: S.of(context).ok, // actionRightButton: () async {
actionLeftButton: () => Navigator.of(dialogContext).pop(), // await requestDisableBatteryOptimization();
actionRightButton: () async { // Navigator.of(dialogContext).pop();
await requestDisableBatteryOptimization(); // },
// );
Navigator.of(dialogContext).pop(); // },
}, // );
); // }
},
);
}
}); });
}), }),
Observer(builder: (context) { Observer(builder: (context) {