main.dart 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  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:hive_flutter/hive_flutter.dart';
  9. import 'package:hooks_riverpod/hooks_riverpod.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/modules/backup/models/hive_backup_albums.model.dart';
  15. import 'package:immich_mobile/modules/backup/models/hive_duplicated_assets.model.dart';
  16. import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
  17. import 'package:immich_mobile/modules/backup/providers/ios_background_settings.provider.dart';
  18. import 'package:immich_mobile/modules/login/models/hive_saved_login_info.model.dart';
  19. import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
  20. import 'package:immich_mobile/modules/onboarding/providers/gallery_permission.provider.dart';
  21. import 'package:immich_mobile/modules/settings/providers/notification_permission.provider.dart';
  22. import 'package:immich_mobile/routing/router.dart';
  23. import 'package:immich_mobile/routing/tab_navigation_observer.dart';
  24. import 'package:immich_mobile/shared/models/album.dart';
  25. import 'package:immich_mobile/shared/models/asset.dart';
  26. import 'package:immich_mobile/shared/models/exif_info.dart';
  27. import 'package:immich_mobile/shared/models/immich_logger_message.model.dart';
  28. import 'package:immich_mobile/shared/models/logger_message.model.dart';
  29. import 'package:immich_mobile/shared/models/store.dart';
  30. import 'package:immich_mobile/shared/models/user.dart';
  31. import 'package:immich_mobile/shared/providers/app_state.provider.dart';
  32. import 'package:immich_mobile/shared/providers/asset.provider.dart';
  33. import 'package:immich_mobile/shared/providers/db.provider.dart';
  34. import 'package:immich_mobile/shared/providers/release_info.provider.dart';
  35. import 'package:immich_mobile/shared/providers/server_info.provider.dart';
  36. import 'package:immich_mobile/shared/providers/websocket.provider.dart';
  37. import 'package:immich_mobile/shared/services/immich_logger.service.dart';
  38. import 'package:immich_mobile/shared/views/immich_loading_overlay.dart';
  39. import 'package:immich_mobile/shared/views/version_announcement_overlay.dart';
  40. import 'package:immich_mobile/utils/immich_app_theme.dart';
  41. import 'package:immich_mobile/utils/migration.dart';
  42. import 'package:isar/isar.dart';
  43. import 'package:logging/logging.dart';
  44. import 'package:path_provider/path_provider.dart';
  45. import 'package:permission_handler/permission_handler.dart';
  46. void main() async {
  47. WidgetsFlutterBinding.ensureInitialized();
  48. final db = await loadDb();
  49. await initApp();
  50. await migrateHiveToStoreIfNecessary();
  51. await migrateJsonCacheIfNecessary();
  52. await migrateDatabaseIfNeeded(db);
  53. runApp(getMainWidget(db));
  54. }
  55. Future<void> initApp() async {
  56. await Hive.initFlutter();
  57. Hive.registerAdapter(HiveSavedLoginInfoAdapter());
  58. Hive.registerAdapter(HiveBackupAlbumsAdapter());
  59. Hive.registerAdapter(HiveDuplicatedAssetsAdapter());
  60. Hive.registerAdapter(ImmichLoggerMessageAdapter());
  61. await EasyLocalization.ensureInitialized();
  62. if (kReleaseMode && Platform.isAndroid) {
  63. try {
  64. await FlutterDisplayMode.setHighRefreshRate();
  65. debugPrint("Enabled high refresh mode");
  66. } catch (e) {
  67. debugPrint("Error setting high refresh rate: $e");
  68. }
  69. }
  70. // Initialize Immich Logger Service
  71. ImmichLogger();
  72. var log = Logger("ImmichErrorLogger");
  73. FlutterError.onError = (details) {
  74. FlutterError.presentError(details);
  75. log.severe(details.toString(), details, details.stack);
  76. };
  77. PlatformDispatcher.instance.onError = (error, stack) {
  78. log.severe(error.toString(), error, stack);
  79. return true;
  80. };
  81. }
  82. Future<Isar> loadDb() async {
  83. final dir = await getApplicationDocumentsDirectory();
  84. Isar db = await Isar.open(
  85. [
  86. StoreValueSchema,
  87. ExifInfoSchema,
  88. AssetSchema,
  89. AlbumSchema,
  90. UserSchema,
  91. BackupAlbumSchema,
  92. DuplicatedAssetSchema,
  93. LoggerMessageSchema,
  94. ],
  95. directory: dir.path,
  96. maxSizeMiB: 256,
  97. );
  98. Store.init(db);
  99. return db;
  100. }
  101. Widget getMainWidget(Isar db) {
  102. return EasyLocalization(
  103. supportedLocales: locales,
  104. path: translationsPath,
  105. useFallbackTranslations: true,
  106. fallbackLocale: locales.first,
  107. child: ProviderScope(
  108. overrides: [dbProvider.overrideWithValue(db)],
  109. child: const ImmichApp(),
  110. ),
  111. );
  112. }
  113. class ImmichApp extends ConsumerStatefulWidget {
  114. const ImmichApp({super.key});
  115. @override
  116. ImmichAppState createState() => ImmichAppState();
  117. }
  118. class ImmichAppState extends ConsumerState<ImmichApp>
  119. with WidgetsBindingObserver {
  120. @override
  121. void didChangeAppLifecycleState(AppLifecycleState state) {
  122. switch (state) {
  123. case AppLifecycleState.resumed:
  124. debugPrint("[APP STATE] resumed");
  125. ref.watch(appStateProvider.notifier).state = AppStateEnum.resumed;
  126. var isAuthenticated = ref.watch(authenticationProvider).isAuthenticated;
  127. final permission = ref.watch(galleryPermissionNotifier);
  128. // Needs to be logged in and have gallery permissions
  129. if (isAuthenticated && (permission.isGranted || permission.isLimited)) {
  130. ref.read(backupProvider.notifier).resumeBackup();
  131. ref.read(backgroundServiceProvider).resumeServiceIfEnabled();
  132. ref.watch(assetProvider.notifier).getAllAsset();
  133. ref.watch(serverInfoProvider.notifier).getServerVersion();
  134. }
  135. ref.watch(websocketProvider.notifier).connect();
  136. ref.watch(releaseInfoProvider.notifier).checkGithubReleaseInfo();
  137. ref
  138. .watch(notificationPermissionProvider.notifier)
  139. .getNotificationPermission();
  140. ref
  141. .watch(galleryPermissionNotifier.notifier)
  142. .getGalleryPermissionStatus();
  143. ref.read(iOSBackgroundSettingsProvider.notifier).refresh();
  144. break;
  145. case AppLifecycleState.inactive:
  146. debugPrint("[APP STATE] inactive");
  147. ref.watch(appStateProvider.notifier).state = AppStateEnum.inactive;
  148. ImmichLogger().flush();
  149. ref.watch(websocketProvider.notifier).disconnect();
  150. ref.watch(backupProvider.notifier).cancelBackup();
  151. break;
  152. case AppLifecycleState.paused:
  153. debugPrint("[APP STATE] paused");
  154. ref.watch(appStateProvider.notifier).state = AppStateEnum.paused;
  155. break;
  156. case AppLifecycleState.detached:
  157. debugPrint("[APP STATE] detached");
  158. ref.watch(appStateProvider.notifier).state = AppStateEnum.detached;
  159. break;
  160. }
  161. }
  162. Future<void> initApp() async {
  163. WidgetsBinding.instance.addObserver(this);
  164. // Draw the app from edge to edge
  165. SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
  166. // Sets the navigation bar color
  167. SystemUiOverlayStyle overlayStyle = const SystemUiOverlayStyle(
  168. systemNavigationBarColor: Colors.transparent,
  169. );
  170. if (Platform.isAndroid) {
  171. // Android 8 does not support transparent app bars
  172. final info = await DeviceInfoPlugin().androidInfo;
  173. if (info.version.sdkInt <= 26) {
  174. overlayStyle =
  175. MediaQuery.of(context).platformBrightness == Brightness.light
  176. ? SystemUiOverlayStyle.light
  177. : SystemUiOverlayStyle.dark;
  178. }
  179. }
  180. SystemChrome.setSystemUIOverlayStyle(overlayStyle);
  181. }
  182. @override
  183. initState() {
  184. super.initState();
  185. initApp().then((_) => debugPrint("App Init Completed"));
  186. WidgetsBinding.instance.addPostFrameCallback((_) {
  187. // needs to be delayed so that EasyLocalization is working
  188. ref.read(backgroundServiceProvider).resumeServiceIfEnabled();
  189. });
  190. }
  191. @override
  192. void dispose() {
  193. WidgetsBinding.instance.removeObserver(this);
  194. super.dispose();
  195. }
  196. @override
  197. Widget build(BuildContext context) {
  198. var router = ref.watch(appRouterProvider);
  199. ref.watch(releaseInfoProvider.notifier).checkGithubReleaseInfo();
  200. return MaterialApp(
  201. localizationsDelegates: context.localizationDelegates,
  202. supportedLocales: context.supportedLocales,
  203. locale: context.locale,
  204. debugShowCheckedModeBanner: false,
  205. home: Stack(
  206. children: [
  207. MaterialApp.router(
  208. title: 'Immich',
  209. debugShowCheckedModeBanner: false,
  210. themeMode: ref.watch(immichThemeProvider),
  211. darkTheme: immichDarkTheme,
  212. theme: immichLightTheme,
  213. routeInformationParser: router.defaultRouteParser(),
  214. routerDelegate: router.delegate(
  215. navigatorObservers: () => [TabNavigationObserver(ref: ref)],
  216. ),
  217. ),
  218. const ImmichLoadingOverlay(),
  219. const VersionAnnouncementOverlay(),
  220. ],
  221. ),
  222. );
  223. }
  224. }