main.dart 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. import 'dart:async';
  2. import 'package:background_fetch/background_fetch.dart';
  3. import 'package:flutter/foundation.dart';
  4. import 'package:flutter/material.dart';
  5. import 'package:in_app_purchase/in_app_purchase.dart';
  6. import 'package:path_provider/path_provider.dart';
  7. import 'package:photos/core/constants.dart';
  8. import 'package:photos/core/configuration.dart';
  9. import 'package:photos/services/billing_service.dart';
  10. import 'package:photos/services/collections_service.dart';
  11. import 'package:photos/services/memories_service.dart';
  12. import 'package:photos/services/sync_service.dart';
  13. import 'package:photos/ui/home_widget.dart';
  14. import 'package:photos/utils/crypto_util.dart';
  15. import 'package:sentry/sentry.dart';
  16. import 'package:super_logging/super_logging.dart';
  17. import 'package:logging/logging.dart';
  18. final _logger = Logger("main");
  19. bool _isInitialized = false;
  20. bool _isInitializing = false;
  21. void main() async {
  22. WidgetsFlutterBinding.ensureInitialized();
  23. await _runWithLogs(_main);
  24. }
  25. void _main() {
  26. final SentryClient sentry =
  27. new SentryClient(dsn: kDebugMode ? SENTRY_DEBUG_DSN : SENTRY_DSN);
  28. FlutterError.onError = (FlutterErrorDetails details) async {
  29. FlutterError.dumpErrorToConsole(details, forceReport: true);
  30. _sendErrorToSentry(sentry, details.exception, details.stack);
  31. };
  32. runZoned(
  33. () async {
  34. await _init();
  35. _sync();
  36. runApp(MyApp());
  37. BackgroundFetch.registerHeadlessTask(backgroundFetchHeadlessTask);
  38. },
  39. onError: (Object error, StackTrace stackTrace) {
  40. _sendErrorToSentry(sentry, error, stackTrace);
  41. },
  42. );
  43. }
  44. Future _init() async {
  45. _isInitializing = true;
  46. _logger.info("Initializing...");
  47. InAppPurchaseConnection.enablePendingPurchases();
  48. CryptoUtil.init();
  49. await Configuration.instance.init();
  50. await BillingService.instance.init();
  51. await CollectionsService.instance.init();
  52. await SyncService.instance.init();
  53. await MemoriesService.instance.init();
  54. _isInitialized = true;
  55. _logger.info("Initialization done");
  56. _isInitializing = false;
  57. }
  58. Future<void> _sync({bool isAppInBackground = false}) async {
  59. if (SyncService.instance.isSyncInProgress()) {
  60. return;
  61. }
  62. try {
  63. await SyncService.instance.sync(isAppInBackground: isAppInBackground);
  64. } catch (e, s) {
  65. _logger.severe("Sync error", e, s);
  66. }
  67. }
  68. /// This "Headless Task" is run when app is terminated.
  69. void backgroundFetchHeadlessTask(String taskId) async {
  70. _logger.info("[BackgroundFetch] Headless event received: $taskId");
  71. if (!_isInitialized && !_isInitializing) {
  72. await _runWithLogs(() async {
  73. await _init();
  74. await _sync(isAppInBackground: true);
  75. });
  76. } else {
  77. _logger.info("Skipping initialization");
  78. await _sync(isAppInBackground: true);
  79. }
  80. BackgroundFetch.finish(taskId);
  81. }
  82. Future _runWithLogs(Function() function) async {
  83. await SuperLogging.main(LogConfig(
  84. body: function,
  85. logDirPath: (await getTemporaryDirectory()).path + "/logs",
  86. enableInDebugMode: true,
  87. maxLogFiles: 5,
  88. ));
  89. }
  90. void _sendErrorToSentry(SentryClient sentry, Object error, StackTrace stack) {
  91. _logger.shout("Uncaught error", error, stack);
  92. try {
  93. sentry.captureException(
  94. exception: error,
  95. stackTrace: stack,
  96. );
  97. _logger.info('Error sent to sentry.io: $error');
  98. } catch (e) {
  99. _logger.info('Sending report to sentry.io failed: $e');
  100. _logger.info('Original error: $error');
  101. }
  102. }
  103. class MyApp extends StatelessWidget with WidgetsBindingObserver {
  104. final _title = 'ente';
  105. @override
  106. Widget build(BuildContext context) {
  107. WidgetsBinding.instance.addObserver(this);
  108. // Configure BackgroundFetch.
  109. BackgroundFetch.configure(
  110. BackgroundFetchConfig(
  111. minimumFetchInterval: 15,
  112. forceAlarmManager: false,
  113. stopOnTerminate: false,
  114. startOnBoot: true,
  115. enableHeadless: true,
  116. requiresBatteryNotLow: false,
  117. requiresCharging: false,
  118. requiresStorageNotLow: false,
  119. requiresDeviceIdle: false,
  120. requiredNetworkType: NetworkType.NONE,
  121. ), (String taskId) async {
  122. _logger.info("[BackgroundFetch] event received: $taskId");
  123. await _sync(isAppInBackground: true);
  124. BackgroundFetch.finish(taskId);
  125. }).then((int status) {
  126. _logger.info('[BackgroundFetch] configure success: $status');
  127. }).catchError((e) {
  128. _logger.info('[BackgroundFetch] configure ERROR: $e');
  129. });
  130. return MaterialApp(
  131. title: _title,
  132. theme: ThemeData(
  133. fontFamily: 'Ubuntu',
  134. brightness: Brightness.dark,
  135. hintColor: Colors.grey,
  136. accentColor: Color.fromRGBO(45, 194, 98, 1.0),
  137. buttonColor: Color.fromRGBO(45, 194, 98, 1.0),
  138. buttonTheme: ButtonThemeData().copyWith(
  139. buttonColor: Color.fromRGBO(45, 194, 98, 1.0),
  140. ),
  141. toggleableActiveColor: Colors.green[400],
  142. scaffoldBackgroundColor: Colors.black,
  143. backgroundColor: Colors.black,
  144. appBarTheme: AppBarTheme().copyWith(
  145. color: Color.fromRGBO(10, 20, 20, 1.0),
  146. ),
  147. cardColor: Color.fromRGBO(25, 25, 25, 1.0),
  148. dialogTheme: DialogTheme().copyWith(
  149. backgroundColor: Color.fromRGBO(20, 20, 20, 1.0),
  150. ),
  151. ),
  152. home: HomeWidget(_title),
  153. );
  154. }
  155. @override
  156. void didChangeAppLifecycleState(AppLifecycleState state) {
  157. if (state == AppLifecycleState.resumed) {
  158. _logger.info("App resumed");
  159. _sync();
  160. }
  161. }
  162. }