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; + } +}