fluffychat/lib/main.dart
krille-chan c7c801c695
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.
2023-08-19 09:50:03 +02:00

102 lines
3.6 KiB
Dart

import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
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';
import 'utils/background_push.dart';
import 'widgets/fluffy_chat_app.dart';
import 'widgets/lock_screen.dart';
void main() async {
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
// widget bindings are initialized already.
WidgetsFlutterBinding.ensureInitialized();
Logs().nativeColors = !PlatformInfos.isIOS;
final clients = await ClientManager.getClients();
// Preload first client
final firstClient = clients.firstOrNull;
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<void> startGui(List<Client> clients) async {
// Fetch the pin for the applock if existing for mobile applications.
String? pin;
if (PlatformInfos.isMobile) {
try {
pin =
await const FlutterSecureStorage().read(key: SettingKeys.appLockKey);
} catch (e, s) {
Logs().d('Unable to read PIN from Secure storage', e, s);
}
}
// 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(
builder: (args) => FluffyChatApp(clients: clients),
lockScreen: const LockScreen(),
enabled: pin?.isNotEmpty ?? false,
)
: FluffyChatApp(clients: clients),
);
}
/// Watches the lifecycle changes to start the application when it
/// is no longer detached.
class AppStarter with WidgetsBindingObserver {
final List<Client> 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;
}
}