From c7c801c6953d4c2f8cf8a0babd7394b246d8d255 Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sat, 19 Aug 2023 09:38:58 +0200 Subject: [PATCH] feat: Background fetch mode on Android This adds a background fetch mode on android which makes sure that the flutter engine is not rendered when the app started to process background push notifications only. This should safe some resources like battery and performance and should make processing background notifications faster. --- lib/main.dart | 55 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 4db62dfa0..5f451c0a6 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,6 +5,7 @@ import 'package:flutter_app_lock/flutter_app_lock.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:matrix/matrix.dart'; +import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/utils/client_manager.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'config/setting_keys.dart'; @@ -13,7 +14,7 @@ import 'widgets/fluffy_chat_app.dart'; import 'widgets/lock_screen.dart'; void main() async { - Logs().i('Welcome to FluffyChat'); + Logs().i('Welcome to ${AppConfig.applicationName} <3'); // Our background push shared isolate accesses flutter-internal things very early in the startup proccess // To make sure that the parts of flutter needed are started up already, we need to ensure that the @@ -28,9 +29,34 @@ void main() async { await firstClient?.roomsLoading; await firstClient?.accountDataLoading; + // If the app starts in detached mode, we assume that it is in + // background fetch mode for processing push notifications. This is + // currently only supported on Android. + if (PlatformInfos.isAndroid && + AppLifecycleState.detached == WidgetsBinding.instance.lifecycleState) { + // In the background fetch mode we do not want to waste ressources with + // starting the Flutter engine but process incoming push notifications. + BackgroundPush.clientOnly(clients.first); + // To start the flutter engine afterwards we add an custom observer. + WidgetsBinding.instance.addObserver(AppStarter(clients)); + Logs().i( + '${AppConfig.applicationName} started in background-fetch mode. No GUI will be created unless the app is no longer detached.', + ); + return; + } + + // Started in foreground mode. + Logs().i( + '${AppConfig.applicationName} started in foreground mode. Rendering GUI...', + ); + await startGui(clients); +} + +/// Fetch the pincode for the applock and start the flutter engine. +Future startGui(List clients) async { + // Fetch the pin for the applock if existing for mobile applications. String? pin; if (PlatformInfos.isMobile) { - BackgroundPush.clientOnly(clients.first); try { pin = await const FlutterSecureStorage().read(key: SettingKeys.appLockKey); @@ -39,6 +65,9 @@ void main() async { } } + // Start rendering the Flutter app and wrap it in an Applock. + // We do this only for mobile applications as we saw routing + // problems on other platforms if we wrap it always. runApp( PlatformInfos.isMobile ? AppLock( @@ -49,3 +78,25 @@ void main() async { : FluffyChatApp(clients: clients), ); } + +/// Watches the lifecycle changes to start the application when it +/// is no longer detached. +class AppStarter with WidgetsBindingObserver { + final List clients; + bool guiStarted = false; + + AppStarter(this.clients); + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (guiStarted) return; + if (state == AppLifecycleState.detached) return; + + Logs().i( + '${AppConfig.applicationName} switches from the detached background-fetch mode to ${state.name} mode. Rendering GUI...', + ); + startGui(clients); + // We must make sure that the GUI is only started once. + guiStarted = true; + } +}