main.dart 8.4 KB

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