app.dart 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. import 'dart:io';
  2. import 'package:adaptive_theme/adaptive_theme.dart';
  3. import 'package:background_fetch/background_fetch.dart';
  4. import 'package:flutter/foundation.dart';
  5. import 'package:flutter/material.dart';
  6. import 'package:flutter_easyloading/flutter_easyloading.dart';
  7. import 'package:flutter_gen/gen_l10n/app_localizations.dart';
  8. import 'package:flutter_localizations/flutter_localizations.dart';
  9. import 'package:logging/logging.dart';
  10. import 'package:photos/core/network.dart';
  11. import 'package:photos/ente_theme_data.dart';
  12. import 'package:photos/l10n/l10n.dart';
  13. import 'package:photos/services/app_lifecycle_service.dart';
  14. import 'package:photos/services/sync_service.dart';
  15. import 'package:photos/ui/home_widget.dart';
  16. final lightThemeData = ThemeData(
  17. fontFamily: 'Inter',
  18. brightness: Brightness.light,
  19. hintColor: Colors.grey,
  20. primaryColor: Colors.deepOrangeAccent,
  21. primaryColorLight: Colors.black54,
  22. iconTheme: IconThemeData(color: Colors.black),
  23. primaryIconTheme: IconThemeData(color: Colors.red, opacity: 1.0, size: 50.0),
  24. colorScheme: ColorScheme.light(
  25. primary: Colors.black,
  26. secondary: Color.fromARGB(255, 163, 163, 163),
  27. ),
  28. accentColor: Color.fromRGBO(0, 0, 0, 0.6),
  29. buttonColor: Color.fromRGBO(45, 194, 98, 1.0),
  30. outlinedButtonTheme: buildOutlinedButtonThemeData(
  31. bgDisabled: Colors.grey.shade500,
  32. bgEnabled: Colors.black,
  33. fgDisabled: Colors.white,
  34. fgEnabled: Colors.white,
  35. ),
  36. elevatedButtonTheme: buildElevatedButtonThemeData(
  37. onPrimary: Colors.white,
  38. primary: Colors.black,
  39. ),
  40. toggleableActiveColor: Colors.green[400],
  41. scaffoldBackgroundColor: Colors.white,
  42. backgroundColor: Colors.white,
  43. appBarTheme: AppBarTheme().copyWith(
  44. backgroundColor: Colors.white,
  45. foregroundColor: Colors.black,
  46. iconTheme: IconThemeData(color: Colors.black),
  47. elevation: 0,
  48. ),
  49. //https://api.flutter.dev/flutter/material/TextTheme-class.html
  50. textTheme: _buildTextTheme(Colors.black),
  51. primaryTextTheme: TextTheme().copyWith(
  52. bodyText2: TextStyle(color: Colors.yellow),
  53. bodyText1: TextStyle(color: Colors.orange),
  54. ),
  55. cardColor: Color.fromRGBO(250, 250, 250, 1.0),
  56. dialogTheme: DialogTheme().copyWith(
  57. backgroundColor: Color.fromRGBO(250, 250, 250, 1.0), //
  58. titleTextStyle: TextStyle(
  59. color: Colors.black,
  60. fontSize: 24,
  61. fontWeight: FontWeight.w600,
  62. ),
  63. contentTextStyle: TextStyle(
  64. fontFamily: 'Inter-Medium',
  65. color: Colors.black,
  66. fontSize: 16,
  67. fontWeight: FontWeight.w500,
  68. ),
  69. shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
  70. ),
  71. inputDecorationTheme: InputDecorationTheme().copyWith(
  72. focusedBorder: UnderlineInputBorder(
  73. borderSide: BorderSide(
  74. color: Color.fromRGBO(45, 194, 98, 1.0),
  75. ),
  76. ),
  77. ),
  78. checkboxTheme: CheckboxThemeData(
  79. side: BorderSide(
  80. color: Colors.black,
  81. width: 2,
  82. ),
  83. fillColor: MaterialStateProperty.resolveWith((states) {
  84. return states.contains(MaterialState.selected)
  85. ? Colors.black
  86. : Colors.white;
  87. }),
  88. checkColor: MaterialStateProperty.resolveWith((states) {
  89. return states.contains(MaterialState.selected)
  90. ? Colors.white
  91. : Colors.black;
  92. }),
  93. ),
  94. );
  95. final darkThemeData = ThemeData(
  96. fontFamily: 'Inter',
  97. brightness: Brightness.dark,
  98. primaryColorLight: Colors.white70,
  99. iconTheme: IconThemeData(color: Colors.white),
  100. primaryIconTheme: IconThemeData(color: Colors.red, opacity: 1.0, size: 50.0),
  101. hintColor: Colors.grey,
  102. colorScheme: ColorScheme.dark(primary: Colors.white),
  103. accentColor: Color.fromRGBO(45, 194, 98, 0.2),
  104. buttonColor: Color.fromRGBO(45, 194, 98, 1.0),
  105. buttonTheme: ButtonThemeData().copyWith(
  106. buttonColor: Color.fromRGBO(45, 194, 98, 1.0),
  107. ),
  108. textTheme: _buildTextTheme(Colors.white),
  109. toggleableActiveColor: Colors.green[400],
  110. outlinedButtonTheme: buildOutlinedButtonThemeData(
  111. bgDisabled: Colors.grey.shade500,
  112. bgEnabled: Colors.white,
  113. fgDisabled: Colors.white,
  114. fgEnabled: Colors.black,
  115. ),
  116. elevatedButtonTheme: buildElevatedButtonThemeData(
  117. onPrimary: Colors.black,
  118. primary: Colors.white,
  119. ),
  120. scaffoldBackgroundColor: Colors.black,
  121. backgroundColor: Colors.black,
  122. appBarTheme: AppBarTheme().copyWith(
  123. color: Colors.black,
  124. elevation: 0,
  125. ),
  126. cardColor: Color.fromRGBO(10, 15, 15, 1.0),
  127. dialogTheme: DialogTheme().copyWith(
  128. backgroundColor: Color.fromRGBO(15, 15, 15, 1.0),
  129. titleTextStyle: TextStyle(
  130. color: Colors.white,
  131. fontSize: 24,
  132. fontWeight: FontWeight.w600,
  133. ),
  134. contentTextStyle: TextStyle(
  135. fontFamily: 'Inter-Medium',
  136. color: Colors.white,
  137. fontSize: 16,
  138. fontWeight: FontWeight.w500,
  139. ),
  140. shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
  141. ),
  142. inputDecorationTheme: InputDecorationTheme().copyWith(
  143. focusedBorder: UnderlineInputBorder(
  144. borderSide: BorderSide(
  145. color: Color.fromRGBO(45, 194, 98, 1.0),
  146. ),
  147. ),
  148. ),
  149. checkboxTheme: CheckboxThemeData(
  150. side: BorderSide(
  151. color: Colors.grey,
  152. width: 2,
  153. ),
  154. fillColor: MaterialStateProperty.resolveWith((states) {
  155. if (states.contains(MaterialState.selected)) {
  156. return Colors.grey;
  157. } else {
  158. return Colors.black;
  159. }
  160. }),
  161. checkColor: MaterialStateProperty.resolveWith((states) {
  162. if (states.contains(MaterialState.selected)) {
  163. return Colors.black;
  164. } else {
  165. return Colors.grey;
  166. }
  167. }),
  168. ),
  169. );
  170. TextTheme _buildTextTheme(Color textColor) {
  171. return TextTheme().copyWith(
  172. headline4: TextStyle(
  173. color: textColor,
  174. fontSize: 32,
  175. fontWeight: FontWeight.w600,
  176. fontFamily: 'Inter',
  177. ),
  178. headline5: TextStyle(
  179. color: textColor,
  180. fontSize: 24,
  181. fontWeight: FontWeight.w600,
  182. fontFamily: 'Inter',
  183. ),
  184. headline6: TextStyle(
  185. color: textColor,
  186. fontSize: 18,
  187. fontFamily: 'Inter',
  188. fontWeight: FontWeight.w600,
  189. ),
  190. subtitle1: TextStyle(
  191. color: textColor,
  192. fontFamily: 'Inter',
  193. fontSize: 16,
  194. fontWeight: FontWeight.w500,
  195. ),
  196. subtitle2: TextStyle(
  197. color: textColor,
  198. fontFamily: 'Inter',
  199. fontSize: 14,
  200. fontWeight: FontWeight.w500,
  201. ),
  202. bodyText1: TextStyle(
  203. fontFamily: 'Inter',
  204. color: textColor,
  205. fontSize: 16,
  206. fontWeight: FontWeight.w500,
  207. ),
  208. bodyText2: TextStyle(
  209. fontFamily: 'Inter',
  210. color: textColor,
  211. fontSize: 14,
  212. fontWeight: FontWeight.w500,
  213. ),
  214. caption: TextStyle(
  215. color: textColor.withOpacity(0.6),
  216. fontSize: 14,
  217. fontWeight: FontWeight.w500,
  218. ),
  219. overline: TextStyle(
  220. color: textColor.withOpacity(0.8),
  221. fontSize: 12,
  222. ),
  223. );
  224. }
  225. class EnteApp extends StatefulWidget {
  226. static const _homeWidget = HomeWidget();
  227. final Future<void> Function(String) runBackgroundTask;
  228. final Future<void> Function(String) killBackgroundTask;
  229. EnteApp(
  230. this.runBackgroundTask,
  231. this.killBackgroundTask, {
  232. Key key,
  233. }) : super(key: key);
  234. @override
  235. _EnteAppState createState() => _EnteAppState();
  236. }
  237. class _EnteAppState extends State<EnteApp> with WidgetsBindingObserver {
  238. final _logger = Logger("EnteAppState");
  239. @override
  240. void initState() {
  241. _logger.info('init App');
  242. super.initState();
  243. WidgetsBinding.instance.addObserver(this);
  244. _configureBackgroundFetch();
  245. }
  246. @override
  247. Widget build(BuildContext context) {
  248. if (kDebugMode && Platform.isAndroid) {
  249. return AdaptiveTheme(
  250. light: lightThemeData,
  251. dark: darkThemeData,
  252. initial: AdaptiveThemeMode.system,
  253. builder: (lightTheme, dartTheme) => MaterialApp(
  254. title: "ente",
  255. themeMode: ThemeMode.system,
  256. theme: lightTheme,
  257. darkTheme: dartTheme,
  258. home: EnteApp._homeWidget,
  259. debugShowCheckedModeBanner: false,
  260. navigatorKey: Network.instance.getAlice().getNavigatorKey(),
  261. builder: EasyLoading.init(),
  262. supportedLocales: L10n.all,
  263. localizationsDelegates: const [
  264. AppLocalizations.delegate,
  265. GlobalMaterialLocalizations.delegate,
  266. GlobalCupertinoLocalizations.delegate,
  267. GlobalWidgetsLocalizations.delegate,
  268. ],
  269. ),
  270. );
  271. } else {
  272. return MaterialApp(
  273. title: "ente",
  274. themeMode: ThemeMode.system,
  275. theme: lightThemeData,
  276. darkTheme: darkThemeData,
  277. home: EnteApp._homeWidget,
  278. debugShowCheckedModeBanner: false,
  279. navigatorKey: Network.instance.getAlice().getNavigatorKey(),
  280. builder: EasyLoading.init(),
  281. supportedLocales: L10n.all,
  282. localizationsDelegates: const [
  283. AppLocalizations.delegate,
  284. GlobalMaterialLocalizations.delegate,
  285. GlobalCupertinoLocalizations.delegate,
  286. GlobalWidgetsLocalizations.delegate,
  287. ],
  288. );
  289. }
  290. }
  291. @override
  292. void dispose() {
  293. WidgetsBinding.instance.removeObserver(this);
  294. super.dispose();
  295. }
  296. @override
  297. void didChangeAppLifecycleState(AppLifecycleState state) {
  298. final String stateChangeReason = 'app -> $state';
  299. if (state == AppLifecycleState.resumed) {
  300. AppLifecycleService.instance
  301. .onAppInForeground(stateChangeReason + ': sync now');
  302. SyncService.instance.sync();
  303. } else {
  304. AppLifecycleService.instance.onAppInBackground(stateChangeReason);
  305. }
  306. }
  307. void _configureBackgroundFetch() {
  308. BackgroundFetch.configure(
  309. BackgroundFetchConfig(
  310. minimumFetchInterval: 15,
  311. forceAlarmManager: false,
  312. stopOnTerminate: false,
  313. startOnBoot: true,
  314. enableHeadless: true,
  315. requiresBatteryNotLow: false,
  316. requiresCharging: false,
  317. requiresStorageNotLow: false,
  318. requiresDeviceIdle: false,
  319. requiredNetworkType: NetworkType.NONE,
  320. ), (String taskId) async {
  321. await widget.runBackgroundTask(taskId);
  322. }, (taskId) {
  323. _logger.info("BG task timeout taskID: $taskId");
  324. widget.killBackgroundTask(taskId);
  325. }).then((int status) {
  326. _logger.info('[BackgroundFetch] configure success: $status');
  327. }).catchError((e) {
  328. _logger.info('[BackgroundFetch] configure ERROR: $e');
  329. });
  330. }
  331. }