main.dart 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. import 'dart:io';
  2. import 'package:device_info_plus/device_info_plus.dart';
  3. import 'package:easy_localization/easy_localization.dart';
  4. import 'package:flutter/foundation.dart';
  5. import 'package:flutter/material.dart';
  6. import 'package:flutter/services.dart';
  7. import 'package:flutter_displaymode/flutter_displaymode.dart';
  8. import 'package:hooks_riverpod/hooks_riverpod.dart';
  9. import 'package:timezone/data/latest.dart';
  10. import 'package:immich_mobile/constants/locales.dart';
  11. import 'package:immich_mobile/modules/backup/background_service/background.service.dart';
  12. import 'package:immich_mobile/modules/backup/models/backup_album.model.dart';
  13. import 'package:immich_mobile/modules/backup/models/duplicated_asset.model.dart';
  14. import 'package:immich_mobile/routing/router.dart';
  15. import 'package:immich_mobile/routing/tab_navigation_observer.dart';
  16. import 'package:immich_mobile/shared/cache/widgets_binding.dart';
  17. import 'package:immich_mobile/shared/models/album.dart';
  18. import 'package:immich_mobile/shared/models/android_device_asset.dart';
  19. import 'package:immich_mobile/shared/models/asset.dart';
  20. import 'package:immich_mobile/shared/models/etag.dart';
  21. import 'package:immich_mobile/shared/models/exif_info.dart';
  22. import 'package:immich_mobile/shared/models/ios_device_asset.dart';
  23. import 'package:immich_mobile/shared/models/logger_message.model.dart';
  24. import 'package:immich_mobile/shared/models/store.dart';
  25. import 'package:immich_mobile/shared/models/user.dart';
  26. import 'package:immich_mobile/shared/providers/app_state.provider.dart';
  27. import 'package:immich_mobile/shared/providers/db.provider.dart';
  28. import 'package:immich_mobile/shared/services/immich_logger.service.dart';
  29. import 'package:immich_mobile/shared/services/local_notification.service.dart';
  30. import 'package:immich_mobile/shared/views/immich_loading_overlay.dart';
  31. import 'package:immich_mobile/utils/http_ssl_cert_override.dart';
  32. import 'package:immich_mobile/utils/immich_app_theme.dart';
  33. import 'package:immich_mobile/utils/migration.dart';
  34. import 'package:isar/isar.dart';
  35. import 'package:logging/logging.dart';
  36. import 'package:path_provider/path_provider.dart';
  37. void main() async {
  38. ImmichWidgetsBinding();
  39. final db = await loadDb();
  40. await initApp();
  41. await migrateDatabaseIfNeeded(db);
  42. HttpOverrides.global = HttpSSLCertOverride();
  43. runApp(getMainWidget(db));
  44. }
  45. Future<void> initApp() async {
  46. await EasyLocalization.ensureInitialized();
  47. if (kReleaseMode && Platform.isAndroid) {
  48. try {
  49. await FlutterDisplayMode.setHighRefreshRate();
  50. debugPrint("Enabled high refresh mode");
  51. } catch (e) {
  52. debugPrint("Error setting high refresh rate: $e");
  53. }
  54. }
  55. // Initialize Immich Logger Service
  56. ImmichLogger();
  57. var log = Logger("ImmichErrorLogger");
  58. FlutterError.onError = (details) {
  59. FlutterError.presentError(details);
  60. log.severe(
  61. 'Catch all error: ${details.toString()} - ${details.exception} - ${details.library} - ${details.context} - ${details.stack}',
  62. details,
  63. details.stack,
  64. );
  65. };
  66. PlatformDispatcher.instance.onError = (error, stack) {
  67. log.severe('Catch all error: ${error.toString()} - $error', error, stack);
  68. return true;
  69. };
  70. initializeTimeZones();
  71. }
  72. Future<Isar> loadDb() async {
  73. final dir = await getApplicationDocumentsDirectory();
  74. Isar db = await Isar.open(
  75. [
  76. StoreValueSchema,
  77. ExifInfoSchema,
  78. AssetSchema,
  79. AlbumSchema,
  80. UserSchema,
  81. BackupAlbumSchema,
  82. DuplicatedAssetSchema,
  83. LoggerMessageSchema,
  84. ETagSchema,
  85. if (Platform.isAndroid) AndroidDeviceAssetSchema,
  86. if (Platform.isIOS) IOSDeviceAssetSchema,
  87. ],
  88. directory: dir.path,
  89. maxSizeMiB: 256,
  90. );
  91. Store.init(db);
  92. return db;
  93. }
  94. Widget getMainWidget(Isar db) {
  95. return EasyLocalization(
  96. supportedLocales: locales,
  97. path: translationsPath,
  98. useFallbackTranslations: true,
  99. fallbackLocale: locales.first,
  100. child: ProviderScope(
  101. overrides: [dbProvider.overrideWithValue(db)],
  102. child: const ImmichApp(),
  103. ),
  104. );
  105. }
  106. class ImmichApp extends ConsumerStatefulWidget {
  107. const ImmichApp({super.key});
  108. @override
  109. ImmichAppState createState() => ImmichAppState();
  110. }
  111. class ImmichAppState extends ConsumerState<ImmichApp>
  112. with WidgetsBindingObserver {
  113. @override
  114. void didChangeAppLifecycleState(AppLifecycleState state) {
  115. switch (state) {
  116. case AppLifecycleState.resumed:
  117. debugPrint("[APP STATE] resumed");
  118. ref.read(appStateProvider.notifier).handleAppResume();
  119. break;
  120. case AppLifecycleState.inactive:
  121. debugPrint("[APP STATE] inactive");
  122. ref.read(appStateProvider.notifier).handleAppInactivity();
  123. break;
  124. case AppLifecycleState.paused:
  125. debugPrint("[APP STATE] paused");
  126. ref.read(appStateProvider.notifier).handleAppPause();
  127. break;
  128. case AppLifecycleState.detached:
  129. debugPrint("[APP STATE] detached");
  130. ref.read(appStateProvider.notifier).handleAppDetached();
  131. break;
  132. case AppLifecycleState.hidden:
  133. debugPrint("[APP STATE] hidden");
  134. ref.read(appStateProvider.notifier).handleAppHidden();
  135. break;
  136. }
  137. }
  138. Future<void> initApp() async {
  139. WidgetsBinding.instance.addObserver(this);
  140. // Draw the app from edge to edge
  141. SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
  142. // Sets the navigation bar color
  143. SystemUiOverlayStyle overlayStyle = const SystemUiOverlayStyle(
  144. systemNavigationBarColor: Colors.transparent,
  145. );
  146. if (Platform.isAndroid) {
  147. // Android 8 does not support transparent app bars
  148. final info = await DeviceInfoPlugin().androidInfo;
  149. if (info.version.sdkInt <= 26) {
  150. overlayStyle =
  151. MediaQuery.of(context).platformBrightness == Brightness.light
  152. ? SystemUiOverlayStyle.light
  153. : SystemUiOverlayStyle.dark;
  154. }
  155. }
  156. SystemChrome.setSystemUIOverlayStyle(overlayStyle);
  157. await ref.read(localNotificationService).setup();
  158. }
  159. @override
  160. initState() {
  161. super.initState();
  162. initApp().then((_) => debugPrint("App Init Completed"));
  163. WidgetsBinding.instance.addPostFrameCallback((_) {
  164. // needs to be delayed so that EasyLocalization is working
  165. ref.read(backgroundServiceProvider).resumeServiceIfEnabled();
  166. });
  167. }
  168. @override
  169. void dispose() {
  170. WidgetsBinding.instance.removeObserver(this);
  171. super.dispose();
  172. }
  173. @override
  174. Widget build(BuildContext context) {
  175. var router = ref.watch(appRouterProvider);
  176. return MaterialApp(
  177. localizationsDelegates: context.localizationDelegates,
  178. supportedLocales: context.supportedLocales,
  179. locale: context.locale,
  180. debugShowCheckedModeBanner: false,
  181. home: Stack(
  182. children: [
  183. MaterialApp.router(
  184. title: 'Immich',
  185. debugShowCheckedModeBanner: false,
  186. themeMode: ref.watch(immichThemeProvider),
  187. darkTheme: immichDarkTheme,
  188. theme: immichLightTheme,
  189. routeInformationParser: router.defaultRouteParser(),
  190. routerDelegate: router.delegate(
  191. navigatorObservers: () => [TabNavigationObserver(ref: ref)],
  192. ),
  193. ),
  194. const ImmichLoadingOverlay(),
  195. ],
  196. ),
  197. );
  198. }
  199. }