backup_section_widget.dart 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. import 'dart:io';
  2. import 'package:flutter/material.dart';
  3. import 'package:logging/logging.dart';
  4. import 'package:photos/core/configuration.dart';
  5. import 'package:photos/models/backup_status.dart';
  6. import 'package:photos/services/deduplication_service.dart';
  7. import 'package:photos/services/sync_service.dart';
  8. import 'package:photos/ui/backup_folder_selection_page.dart';
  9. import 'package:photos/ui/deduplicate_page.dart';
  10. import 'package:photos/ui/free_space_page.dart';
  11. import 'package:photos/ui/settings/settings_section_title.dart';
  12. import 'package:photos/ui/settings/settings_text_item.dart';
  13. import 'package:photos/utils/data_util.dart';
  14. import 'package:photos/utils/dialog_util.dart';
  15. import 'package:photos/utils/navigation_util.dart';
  16. import 'package:photos/utils/toast_util.dart';
  17. import 'package:url_launcher/url_launcher.dart';
  18. class BackupSectionWidget extends StatefulWidget {
  19. BackupSectionWidget({Key key}) : super(key: key);
  20. @override
  21. BackupSectionWidgetState createState() => BackupSectionWidgetState();
  22. }
  23. class BackupSectionWidgetState extends State<BackupSectionWidget> {
  24. @override
  25. Widget build(BuildContext context) {
  26. return Column(
  27. children: [
  28. SettingsSectionTitle("backup"),
  29. Padding(
  30. padding: EdgeInsets.all(4),
  31. ),
  32. GestureDetector(
  33. behavior: HitTestBehavior.translucent,
  34. onTap: () async {
  35. routeToPage(
  36. context,
  37. BackupFolderSelectionPage(
  38. buttonText: "backup",
  39. ),
  40. );
  41. },
  42. child: SettingsTextItem(
  43. text: "backed up folders", icon: Icons.navigate_next),
  44. ),
  45. Platform.isIOS
  46. ? Padding(padding: EdgeInsets.all(2))
  47. : Padding(padding: EdgeInsets.all(0)),
  48. Divider(height: 4),
  49. Platform.isIOS
  50. ? Padding(padding: EdgeInsets.all(2))
  51. : Padding(padding: EdgeInsets.all(4)),
  52. SizedBox(
  53. height: 36,
  54. child: Row(
  55. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  56. children: [
  57. Text("backup over mobile data"),
  58. Switch(
  59. value: Configuration.instance.shouldBackupOverMobileData(),
  60. onChanged: (value) async {
  61. Configuration.instance.setBackupOverMobileData(value);
  62. setState(() {});
  63. },
  64. ),
  65. ],
  66. ),
  67. ),
  68. Platform.isIOS
  69. ? Padding(padding: EdgeInsets.all(2))
  70. : Padding(padding: EdgeInsets.all(4)),
  71. Divider(height: 4),
  72. Platform.isIOS
  73. ? Padding(padding: EdgeInsets.all(2))
  74. : Padding(padding: EdgeInsets.all(4)),
  75. SizedBox(
  76. height: 36,
  77. child: Row(
  78. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  79. children: [
  80. Text("backup videos"),
  81. Switch(
  82. value: Configuration.instance.shouldBackupVideos(),
  83. onChanged: (value) async {
  84. Configuration.instance.setShouldBackupVideos(value);
  85. setState(() {});
  86. },
  87. ),
  88. ],
  89. ),
  90. ),
  91. Platform.isIOS
  92. ? Padding(padding: EdgeInsets.all(4))
  93. : Padding(padding: EdgeInsets.all(2)),
  94. Divider(height: 4),
  95. Platform.isIOS
  96. ? Padding(padding: EdgeInsets.all(2))
  97. : Padding(padding: EdgeInsets.all(2)),
  98. GestureDetector(
  99. behavior: HitTestBehavior.translucent,
  100. onTap: () async {
  101. final dialog = createProgressDialog(context, "calculating...");
  102. await dialog.show();
  103. final status = await SyncService.instance.getBackupStatus();
  104. await dialog.hide();
  105. if (status.localIDs.isEmpty) {
  106. showErrorDialog(context, "✨ all clear",
  107. "you've no files on this device that can be deleted");
  108. } else {
  109. bool result = await routeToPage(context, FreeSpacePage(status));
  110. if (result == true) {
  111. _showSpaceFreedDialog(status);
  112. }
  113. }
  114. },
  115. child: SettingsTextItem(
  116. text: "free up space",
  117. icon: Icons.navigate_next,
  118. ),
  119. ),
  120. Platform.isIOS
  121. ? Padding(padding: EdgeInsets.all(4))
  122. : Padding(padding: EdgeInsets.all(2)),
  123. Divider(height: 4),
  124. Platform.isIOS
  125. ? Padding(padding: EdgeInsets.all(2))
  126. : Padding(padding: EdgeInsets.all(2)),
  127. GestureDetector(
  128. behavior: HitTestBehavior.translucent,
  129. onTap: () async {
  130. final dialog = createProgressDialog(context, "calculating...");
  131. await dialog.show();
  132. final duplicates =
  133. await DeduplicationService.instance.getDuplicateFiles();
  134. await dialog.hide();
  135. if (duplicates.isEmpty) {
  136. showErrorDialog(context, "✨ no duplicates",
  137. "you've no duplicate files that can be cleared");
  138. } else {
  139. bool result =
  140. await routeToPage(context, DeduplicatePage(duplicates));
  141. if (result == true) {
  142. _showDuplicateFilesDeletedDialog(0); // todo
  143. }
  144. }
  145. },
  146. child: SettingsTextItem(
  147. text: "deduplicate files",
  148. icon: Icons.navigate_next,
  149. ),
  150. ),
  151. ],
  152. );
  153. }
  154. void _showSpaceFreedDialog(BackupStatus status) {
  155. AlertDialog alert = AlertDialog(
  156. title: Text("success"),
  157. content: Text(
  158. "you have successfully freed up " + formatBytes(status.size) + "!"),
  159. actions: [
  160. TextButton(
  161. child: Text(
  162. "rate us",
  163. style: TextStyle(
  164. color: Theme.of(context).buttonColor,
  165. ),
  166. ),
  167. onPressed: () {
  168. Navigator.of(context, rootNavigator: true).pop('dialog');
  169. if (Platform.isAndroid) {
  170. launch(
  171. "https://play.google.com/store/apps/details?id=io.ente.photos");
  172. } else {
  173. launch("https://apps.apple.com/in/app/ente-photos/id1542026904");
  174. }
  175. },
  176. ),
  177. TextButton(
  178. child: Text(
  179. "ok",
  180. style: TextStyle(
  181. color: Colors.white,
  182. ),
  183. ),
  184. onPressed: () {
  185. if (Platform.isIOS) {
  186. showToast(
  187. "also empty \"Recently Deleted\" from \"Settings\" -> \"Storage\" to claim the freed space");
  188. }
  189. Navigator.of(context, rootNavigator: true).pop('dialog');
  190. },
  191. ),
  192. ],
  193. );
  194. showConfettiDialog(
  195. context: context,
  196. builder: (BuildContext context) {
  197. return alert;
  198. },
  199. barrierColor: Colors.black87,
  200. confettiAlignment: Alignment.topCenter,
  201. useRootNavigator: true,
  202. );
  203. }
  204. void _showDuplicateFilesDeletedDialog(int spaceFreed) {
  205. AlertDialog alert = AlertDialog(
  206. title: Text("success"),
  207. content: Text(
  208. "you have successfully freed up " + formatBytes(spaceFreed) + "!"),
  209. actions: [
  210. TextButton(
  211. child: Text(
  212. "rate us",
  213. style: TextStyle(
  214. color: Theme.of(context).buttonColor,
  215. ),
  216. ),
  217. onPressed: () {
  218. Navigator.of(context, rootNavigator: true).pop('dialog');
  219. if (Platform.isAndroid) {
  220. launch(
  221. "https://play.google.com/store/apps/details?id=io.ente.photos");
  222. } else {
  223. launch("https://apps.apple.com/in/app/ente-photos/id1542026904");
  224. }
  225. },
  226. ),
  227. TextButton(
  228. child: Text(
  229. "ok",
  230. style: TextStyle(
  231. color: Colors.white,
  232. ),
  233. ),
  234. onPressed: () {
  235. showToast(
  236. "also empty your \"Trash\" from to claim the freed space");
  237. Navigator.of(context, rootNavigator: true).pop('dialog');
  238. },
  239. ),
  240. ],
  241. );
  242. showConfettiDialog(
  243. context: context,
  244. builder: (BuildContext context) {
  245. return alert;
  246. },
  247. barrierColor: Colors.black87,
  248. confettiAlignment: Alignment.topCenter,
  249. useRootNavigator: true,
  250. );
  251. }
  252. }