mirror of
				https://github.com/ImranR98/Obtainium.git
				synced 2025-10-30 21:13:28 +01:00 
			
		
		
		
	Enable auto-export on update checks
This commit is contained in:
		| @@ -254,6 +254,8 @@ | |||||||
|     "versionExtractionRegEx": "Version Extraction RegEx", |     "versionExtractionRegEx": "Version Extraction RegEx", | ||||||
|     "matchGroupToUse": "Match Group to Use", |     "matchGroupToUse": "Match Group to Use", | ||||||
|     "highlightTouchTargets": "Highlight less obvious touch targets", |     "highlightTouchTargets": "Highlight less obvious touch targets", | ||||||
|  |     "pickExportDir": "Pick Export Directory", | ||||||
|  |     "autoExportOnUpdateCheckKeepNum": "Auto-export on update check (keep last N auto-exports)", | ||||||
|     "removeAppQuestion": { |     "removeAppQuestion": { | ||||||
|         "one": "Remover App?", |         "one": "Remover App?", | ||||||
|         "other": "Remover Apps?" |         "other": "Remover Apps?" | ||||||
|   | |||||||
| @@ -251,6 +251,8 @@ | |||||||
|    "versionExtractionRegEx": "Version Extraction RegEx", |    "versionExtractionRegEx": "Version Extraction RegEx", | ||||||
|    "matchGroupToUse": "Match Group to Use", |    "matchGroupToUse": "Match Group to Use", | ||||||
|    "highlightTouchTargets": "Highlight less obvious touch targets", |    "highlightTouchTargets": "Highlight less obvious touch targets", | ||||||
|  |    "pickExportDir": "Pick Export Directory", | ||||||
|  |    "autoExportOnUpdateCheckKeepNum": "Auto-export on update check (keep last N auto-exports)", | ||||||
|      "removeAppQuestion": { |      "removeAppQuestion": { | ||||||
|       "one": "Želite li ukloniti aplikaciju?", |       "one": "Želite li ukloniti aplikaciju?", | ||||||
|       "other": "Želite li ukloniti aplikacije?" |       "other": "Želite li ukloniti aplikacije?" | ||||||
|   | |||||||
| @@ -251,6 +251,8 @@ | |||||||
|     "versionExtractionRegEx": "Version Extraction RegEx", |     "versionExtractionRegEx": "Version Extraction RegEx", | ||||||
|     "matchGroupToUse": "Match Group to Use", |     "matchGroupToUse": "Match Group to Use", | ||||||
|     "highlightTouchTargets": "Highlight less obvious touch targets", |     "highlightTouchTargets": "Highlight less obvious touch targets", | ||||||
|  |     "pickExportDir": "Pick Export Directory", | ||||||
|  |     "autoExportOnUpdateCheckKeepNum": "Auto-export on update check (keep last N auto-exports)", | ||||||
|     "removeAppQuestion": { |     "removeAppQuestion": { | ||||||
|         "one": "App entfernen?", |         "one": "App entfernen?", | ||||||
|         "other": "Apps entfernen?" |         "other": "Apps entfernen?" | ||||||
|   | |||||||
| @@ -254,6 +254,8 @@ | |||||||
|     "versionExtractionRegEx": "Version Extraction RegEx", |     "versionExtractionRegEx": "Version Extraction RegEx", | ||||||
|     "matchGroupToUse": "Match Group to Use", |     "matchGroupToUse": "Match Group to Use", | ||||||
|     "highlightTouchTargets": "Highlight less obvious touch targets", |     "highlightTouchTargets": "Highlight less obvious touch targets", | ||||||
|  |     "pickExportDir": "Pick Export Directory", | ||||||
|  |     "autoExportOnUpdateCheckKeepNum": "Auto-export on update check (keep last N auto-exports)", | ||||||
|     "removeAppQuestion": { |     "removeAppQuestion": { | ||||||
|         "one": "Remove App?", |         "one": "Remove App?", | ||||||
|         "other": "Remove Apps?" |         "other": "Remove Apps?" | ||||||
|   | |||||||
| @@ -251,6 +251,8 @@ | |||||||
|     "versionExtractionRegEx": "Version Extraction RegEx", |     "versionExtractionRegEx": "Version Extraction RegEx", | ||||||
|     "matchGroupToUse": "Match Group to Use", |     "matchGroupToUse": "Match Group to Use", | ||||||
|     "highlightTouchTargets": "Highlight less obvious touch targets", |     "highlightTouchTargets": "Highlight less obvious touch targets", | ||||||
|  |     "pickExportDir": "Pick Export Directory", | ||||||
|  |     "autoExportOnUpdateCheckKeepNum": "Auto-export on update check (keep last N auto-exports)", | ||||||
|     "removeAppQuestion": { |     "removeAppQuestion": { | ||||||
|         "one": "¿Eliminar Aplicación?", |         "one": "¿Eliminar Aplicación?", | ||||||
|         "other": "¿Eliminar Aplicaciones?" |         "other": "¿Eliminar Aplicaciones?" | ||||||
|   | |||||||
| @@ -251,6 +251,8 @@ | |||||||
|     "versionExtractionRegEx": "Version Extraction RegEx", |     "versionExtractionRegEx": "Version Extraction RegEx", | ||||||
|     "matchGroupToUse": "Match Group to Use", |     "matchGroupToUse": "Match Group to Use", | ||||||
|     "highlightTouchTargets": "Highlight less obvious touch targets", |     "highlightTouchTargets": "Highlight less obvious touch targets", | ||||||
|  |     "pickExportDir": "Pick Export Directory", | ||||||
|  |     "autoExportOnUpdateCheckKeepNum": "Auto-export on update check (keep last N auto-exports)", | ||||||
|     "removeAppQuestion": { |     "removeAppQuestion": { | ||||||
|         "one": "برنامه حذف شود؟", |         "one": "برنامه حذف شود؟", | ||||||
|         "other": "برنامه ها حذف شوند؟" |         "other": "برنامه ها حذف شوند؟" | ||||||
|   | |||||||
| @@ -251,6 +251,8 @@ | |||||||
|     "versionExtractionRegEx": "Version Extraction RegEx", |     "versionExtractionRegEx": "Version Extraction RegEx", | ||||||
|     "matchGroupToUse": "Match Group to Use", |     "matchGroupToUse": "Match Group to Use", | ||||||
|     "highlightTouchTargets": "Highlight less obvious touch targets", |     "highlightTouchTargets": "Highlight less obvious touch targets", | ||||||
|  |     "pickExportDir": "Pick Export Directory", | ||||||
|  |     "autoExportOnUpdateCheckKeepNum": "Auto-export on update check (keep last N auto-exports)", | ||||||
|     "removeAppQuestion": { |     "removeAppQuestion": { | ||||||
|         "one": "Supprimer l'application ?", |         "one": "Supprimer l'application ?", | ||||||
|         "other": "Supprimer les applications ?" |         "other": "Supprimer les applications ?" | ||||||
|   | |||||||
| @@ -250,6 +250,8 @@ | |||||||
|     "versionExtractionRegEx": "Version Extraction RegEx", |     "versionExtractionRegEx": "Version Extraction RegEx", | ||||||
|     "matchGroupToUse": "Match Group to Use", |     "matchGroupToUse": "Match Group to Use", | ||||||
|     "highlightTouchTargets": "Highlight less obvious touch targets", |     "highlightTouchTargets": "Highlight less obvious touch targets", | ||||||
|  |     "pickExportDir": "Pick Export Directory", | ||||||
|  |     "autoExportOnUpdateCheckKeepNum": "Auto-export on update check (keep last N auto-exports)", | ||||||
|     "removeAppQuestion": { |     "removeAppQuestion": { | ||||||
|         "one": "Eltávolítja az alkalmazást?", |         "one": "Eltávolítja az alkalmazást?", | ||||||
|         "other": "Eltávolítja az alkalmazást?" |         "other": "Eltávolítja az alkalmazást?" | ||||||
|   | |||||||
| @@ -251,6 +251,8 @@ | |||||||
|     "versionExtractionRegEx": "Version Extraction RegEx", |     "versionExtractionRegEx": "Version Extraction RegEx", | ||||||
|     "matchGroupToUse": "Match Group to Use", |     "matchGroupToUse": "Match Group to Use", | ||||||
|     "highlightTouchTargets": "Highlight less obvious touch targets", |     "highlightTouchTargets": "Highlight less obvious touch targets", | ||||||
|  |     "pickExportDir": "Pick Export Directory", | ||||||
|  |     "autoExportOnUpdateCheckKeepNum": "Auto-export on update check (keep last N auto-exports)", | ||||||
|     "removeAppQuestion": { |     "removeAppQuestion": { | ||||||
|         "one": "Rimuovere l'app?", |         "one": "Rimuovere l'app?", | ||||||
|         "other": "Rimuovere le app?" |         "other": "Rimuovere le app?" | ||||||
|   | |||||||
| @@ -252,6 +252,8 @@ | |||||||
|     "versionExtractionRegEx": "Version Extraction RegEx", |     "versionExtractionRegEx": "Version Extraction RegEx", | ||||||
|     "matchGroupToUse": "Match Group to Use", |     "matchGroupToUse": "Match Group to Use", | ||||||
|     "highlightTouchTargets": "Highlight less obvious touch targets", |     "highlightTouchTargets": "Highlight less obvious touch targets", | ||||||
|  |     "pickExportDir": "Pick Export Directory", | ||||||
|  |     "autoExportOnUpdateCheckKeepNum": "Auto-export on update check (keep last N auto-exports)", | ||||||
|     "removeAppQuestion": { |     "removeAppQuestion": { | ||||||
|         "one": "アプリを削除しますか?", |         "one": "アプリを削除しますか?", | ||||||
|         "other": "アプリを削除しますか?" |         "other": "アプリを削除しますか?" | ||||||
|   | |||||||
| @@ -257,6 +257,8 @@ | |||||||
|     "versionExtractionRegEx": "Version Extraction RegEx", |     "versionExtractionRegEx": "Version Extraction RegEx", | ||||||
|     "matchGroupToUse": "Match Group to Use", |     "matchGroupToUse": "Match Group to Use", | ||||||
|     "highlightTouchTargets": "Highlight less obvious touch targets", |     "highlightTouchTargets": "Highlight less obvious touch targets", | ||||||
|  |     "pickExportDir": "Pick Export Directory", | ||||||
|  |     "autoExportOnUpdateCheckKeepNum": "Auto-export on update check (keep last N auto-exports)", | ||||||
|     "removeAppQuestion": { |     "removeAppQuestion": { | ||||||
|         "one": "Usunąć aplikację?", |         "one": "Usunąć aplikację?", | ||||||
|         "few": "Usunąć aplikacje?", |         "few": "Usunąć aplikacje?", | ||||||
|   | |||||||
| @@ -251,6 +251,8 @@ | |||||||
|     "versionExtractionRegEx": "Version Extraction RegEx", |     "versionExtractionRegEx": "Version Extraction RegEx", | ||||||
|     "matchGroupToUse": "Match Group to Use", |     "matchGroupToUse": "Match Group to Use", | ||||||
|     "highlightTouchTargets": "Highlight less obvious touch targets", |     "highlightTouchTargets": "Highlight less obvious touch targets", | ||||||
|  |     "pickExportDir": "Pick Export Directory", | ||||||
|  |     "autoExportOnUpdateCheckKeepNum": "Auto-export on update check (keep last N auto-exports)", | ||||||
|     "removeAppQuestion": { |     "removeAppQuestion": { | ||||||
|         "one": "Удалить приложение?", |         "one": "Удалить приложение?", | ||||||
|         "other": "Удалить приложения?" |         "other": "Удалить приложения?" | ||||||
|   | |||||||
| @@ -252,6 +252,8 @@ | |||||||
|     "versionExtractionRegEx": "Version Extraction RegEx", |     "versionExtractionRegEx": "Version Extraction RegEx", | ||||||
|     "matchGroupToUse": "Match Group to Use", |     "matchGroupToUse": "Match Group to Use", | ||||||
|     "highlightTouchTargets": "Highlight less obvious touch targets", |     "highlightTouchTargets": "Highlight less obvious touch targets", | ||||||
|  |     "pickExportDir": "Pick Export Directory", | ||||||
|  |     "autoExportOnUpdateCheckKeepNum": "Auto-export on update check (keep last N auto-exports)", | ||||||
|     "removeAppQuestion": { |     "removeAppQuestion": { | ||||||
|         "one": "是否删除应用?", |         "one": "是否删除应用?", | ||||||
|         "other": "是否删除应用?" |         "other": "是否删除应用?" | ||||||
|   | |||||||
| @@ -124,10 +124,7 @@ class HTML extends AppSource { | |||||||
|             additionalValidators: [ |             additionalValidators: [ | ||||||
|               (value) { |               (value) { | ||||||
|                 value ??= '1'; |                 value ??= '1'; | ||||||
|                 if (int.tryParse(value) == null) { |                 return intValidator(value); | ||||||
|                   return tr('invalidInput'); |  | ||||||
|                 } |  | ||||||
|                 return null; |  | ||||||
|               } |               } | ||||||
|             ]) |             ]) | ||||||
|       ] |       ] | ||||||
|   | |||||||
| @@ -74,6 +74,11 @@ class AppsPageState extends State<AppsPage> { | |||||||
|         setState(() { |         setState(() { | ||||||
|           refreshingSince = null; |           refreshingSince = null; | ||||||
|         }); |         }); | ||||||
|  |         if (settingsProvider.autoExportOnUpdateCheckKeepNum > 0) { | ||||||
|  |           appsProvider.exportApps(isAuto: true).then((value) { | ||||||
|  |             appsProvider.trimAutoExports(); | ||||||
|  |           }); | ||||||
|  |         } | ||||||
|       }); |       }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -104,8 +104,12 @@ class _ImportExportPageState extends State<ImportExportPage> { | |||||||
|  |  | ||||||
|     runObtainiumExport() { |     runObtainiumExport() { | ||||||
|       HapticFeedback.selectionClick(); |       HapticFeedback.selectionClick(); | ||||||
|       appsProvider.exportApps().then((String path) { |       appsProvider | ||||||
|         showError(tr('exportedTo', args: [path]), context); |           .exportApps(pickOnly: settingsProvider.exportDir == null) | ||||||
|  |           .then((String? result) { | ||||||
|  |         if (result != null) { | ||||||
|  |           showError(tr('exportedTo', args: [result]), context); | ||||||
|  |         } | ||||||
|       }).catchError((e) { |       }).catchError((e) { | ||||||
|         showError(e, context); |         showError(e, context); | ||||||
|       }); |       }); | ||||||
| @@ -310,7 +314,10 @@ class _ImportExportPageState extends State<ImportExportPage> { | |||||||
|                                           importInProgress |                                           importInProgress | ||||||
|                                       ? null |                                       ? null | ||||||
|                                       : runObtainiumExport, |                                       : runObtainiumExport, | ||||||
|                                   child: Text(tr('obtainiumExport')))), |                                   child: Text(tr( | ||||||
|  |                                       settingsProvider.exportDir != null | ||||||
|  |                                           ? 'obtainiumExport' | ||||||
|  |                                           : 'pickExportDirKeepLastN')))), | ||||||
|                           const SizedBox( |                           const SizedBox( | ||||||
|                             width: 16, |                             width: 16, | ||||||
|                           ), |                           ), | ||||||
| @@ -323,6 +330,48 @@ class _ImportExportPageState extends State<ImportExportPage> { | |||||||
|                                   child: Text(tr('obtainiumImport')))) |                                   child: Text(tr('obtainiumImport')))) | ||||||
|                         ], |                         ], | ||||||
|                       ), |                       ), | ||||||
|  |                       if (settingsProvider.exportDir != null) | ||||||
|  |                         Column( | ||||||
|  |                           children: [ | ||||||
|  |                             const SizedBox(height: 16), | ||||||
|  |                             GeneratedForm( | ||||||
|  |                                 items: [ | ||||||
|  |                                   [ | ||||||
|  |                                     GeneratedFormTextField( | ||||||
|  |                                         'autoExportOnUpdateCheckKeepNum', | ||||||
|  |                                         label: tr( | ||||||
|  |                                             'autoExportOnUpdateCheckKeepNum'), | ||||||
|  |                                         required: false, | ||||||
|  |                                         defaultValue: settingsProvider | ||||||
|  |                                             .autoExportOnUpdateCheckKeepNum | ||||||
|  |                                             .toString(), | ||||||
|  |                                         textInputType: const TextInputType | ||||||
|  |                                             .numberWithOptions(), | ||||||
|  |                                         additionalValidators: [ | ||||||
|  |                                           (value) { | ||||||
|  |                                             value ??= settingsProvider | ||||||
|  |                                                 .autoExportOnUpdateCheckKeepNum | ||||||
|  |                                                 .toString(); | ||||||
|  |                                             return intValidator(value, | ||||||
|  |                                                 positive: true); | ||||||
|  |                                           } | ||||||
|  |                                         ]) | ||||||
|  |                                   ] | ||||||
|  |                                 ], | ||||||
|  |                                 onValueChanges: (value, valid, isBuilding) { | ||||||
|  |                                   if (valid && !isBuilding) { | ||||||
|  |                                     if (value[ | ||||||
|  |                                             'autoExportOnUpdateCheckKeepNum'] != | ||||||
|  |                                         null) { | ||||||
|  |                                       settingsProvider | ||||||
|  |                                               .autoExportOnUpdateCheckKeepNum = | ||||||
|  |                                           int.parse(value[ | ||||||
|  |                                               'autoExportOnUpdateCheckKeepNum']); | ||||||
|  |                                     } | ||||||
|  |                                   } | ||||||
|  |                                 }), | ||||||
|  |                           ], | ||||||
|  |                         ), | ||||||
|                       if (importInProgress) |                       if (importInProgress) | ||||||
|                         const Column( |                         const Column( | ||||||
|                           children: [ |                           children: [ | ||||||
| @@ -399,7 +448,7 @@ class _ImportExportPageState extends State<ImportExportPage> { | |||||||
|                               fontStyle: FontStyle.italic, fontSize: 12)), |                               fontStyle: FontStyle.italic, fontSize: 12)), | ||||||
|                       const SizedBox( |                       const SizedBox( | ||||||
|                         height: 8, |                         height: 8, | ||||||
|                       ) |                       ), | ||||||
|                     ], |                     ], | ||||||
|                   ))) |                   ))) | ||||||
|         ])); |         ])); | ||||||
|   | |||||||
| @@ -16,6 +16,7 @@ import 'package:device_info_plus/device_info_plus.dart'; | |||||||
| import 'package:easy_localization/easy_localization.dart'; | import 'package:easy_localization/easy_localization.dart'; | ||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:flutter/services.dart'; | import 'package:flutter/services.dart'; | ||||||
|  | import 'package:obtainium/app_sources/html.dart'; | ||||||
| import 'package:obtainium/components/generated_form.dart'; | import 'package:obtainium/components/generated_form.dart'; | ||||||
| import 'package:obtainium/components/generated_form_modal.dart'; | import 'package:obtainium/components/generated_form_modal.dart'; | ||||||
| import 'package:obtainium/custom_errors.dart'; | import 'package:obtainium/custom_errors.dart'; | ||||||
| @@ -31,6 +32,7 @@ import 'package:obtainium/providers/source_provider.dart'; | |||||||
| import 'package:http/http.dart'; | import 'package:http/http.dart'; | ||||||
| import 'package:android_intent_plus/android_intent.dart'; | import 'package:android_intent_plus/android_intent.dart'; | ||||||
| import 'package:flutter_archive/flutter_archive.dart'; | import 'package:flutter_archive/flutter_archive.dart'; | ||||||
|  | import 'package:shared_storage/shared_storage.dart' as saf; | ||||||
|  |  | ||||||
| final pm = AndroidPackageManager(); | final pm = AndroidPackageManager(); | ||||||
|  |  | ||||||
| @@ -167,7 +169,8 @@ class AppsProvider with ChangeNotifier { | |||||||
|       if (cacheDirs?.isNotEmpty ?? false) { |       if (cacheDirs?.isNotEmpty ?? false) { | ||||||
|         APKDir = cacheDirs!.first; |         APKDir = cacheDirs!.first; | ||||||
|       } else { |       } else { | ||||||
|         APKDir = Directory('${await settingsProvider.getAppDir()}/apks'); |         APKDir = | ||||||
|  |             Directory('${(await getExternalStorageDirectory())!.path}/apks'); | ||||||
|         if (!APKDir.existsSync()) { |         if (!APKDir.existsSync()) { | ||||||
|           APKDir.createSync(); |           APKDir.createSync(); | ||||||
|         } |         } | ||||||
| @@ -676,7 +679,7 @@ class AppsProvider with ChangeNotifier { | |||||||
|  |  | ||||||
|   Future<Directory> getAppsDir() async { |   Future<Directory> getAppsDir() async { | ||||||
|     Directory appsDir = |     Directory appsDir = | ||||||
|         Directory('${await settingsProvider.getAppDir()}/app_data'); |         Directory('${(await getExternalStorageDirectory())!.path}/app_data'); | ||||||
|     if (!appsDir.existsSync()) { |     if (!appsDir.existsSync()) { | ||||||
|       appsDir.createSync(); |       appsDir.createSync(); | ||||||
|     } |     } | ||||||
| @@ -1091,32 +1094,58 @@ class AppsProvider with ChangeNotifier { | |||||||
|     return updateAppIds; |     return updateAppIds; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   Future<String> exportApps() async { |   Future<String?> exportApps({bool pickOnly = false, isAuto = false}) async { | ||||||
|     if ((await DeviceInfoPlugin().androidInfo).version.sdkInt <= 29) { |     if (isAuto) { | ||||||
|       if (await Permission.storage.isDenied) { |       logs.add('Started auto-export.'); | ||||||
|         await Permission.storage.request(); |  | ||||||
|     } |     } | ||||||
|       if (await Permission.storage.isDenied) { |     var exportDir = settingsProvider.exportDir; | ||||||
|         throw ObtainiumError(tr('storagePermissionDenied')); |     if (exportDir == null || pickOnly) { | ||||||
|  |       await settingsProvider.pickExportDirKeepLastN(); | ||||||
|  |       exportDir = settingsProvider.exportDir; | ||||||
|  |     } | ||||||
|  |     if (exportDir == null) { | ||||||
|  |       throw ObtainiumError(tr('unexpectedError')); | ||||||
|  |     } | ||||||
|  |     String? returnPath; | ||||||
|  |     if (!pickOnly) { | ||||||
|  |       var result = await saf.createFile(exportDir, | ||||||
|  |           displayName: | ||||||
|  |               '${tr('obtainiumExportHyphenatedLowercase')}-${DateTime.now().toIso8601String().replaceAll(':', '-')}${isAuto ? '-auto' : ''}.json', | ||||||
|  |           mimeType: 'application/json', | ||||||
|  |           content: jsonEncode(apps.values.map((e) => e.app.toJson()).toList())); | ||||||
|  |       if (result == null) { | ||||||
|  |         throw ObtainiumError(tr('unexpectedError')); | ||||||
|  |       } | ||||||
|  |       returnPath = | ||||||
|  |           exportDir.pathSegments.join('/').replaceFirst('tree/primary:', ''); | ||||||
|  |     } | ||||||
|  |     return returnPath; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   Future<void> trimAutoExports() async { | ||||||
|  |     var exportDir = settingsProvider.exportDir; | ||||||
|  |     if (exportDir != null) { | ||||||
|  |       var files = await saf | ||||||
|  |           .listFiles(exportDir, columns: [saf.DocumentFileColumn.id]).toList(); | ||||||
|  |       var maxCount = settingsProvider.autoExportOnUpdateCheckKeepNum; | ||||||
|  |       if (files.length > maxCount) { | ||||||
|  |         files.sort((a, b) { | ||||||
|  |           if (a.name == null) { | ||||||
|  |             return -1; | ||||||
|  |           } else if (b.name == null) { | ||||||
|  |             return 1; | ||||||
|  |           } else { | ||||||
|  |             return compareAlphaNumeric(a.name!, b.name!); | ||||||
|  |           } | ||||||
|  |         }); | ||||||
|  |         files = files.reversed.toList(); | ||||||
|  |         logs.add( | ||||||
|  |             'Deleting auto-exports older than ${files[maxCount - 1].uri.pathSegments.last}.'); | ||||||
|  |         files.sublist(maxCount).forEach((f) { | ||||||
|  |           saf.delete(f.uri); | ||||||
|  |         }); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|     Directory? exportDir = Directory('/storage/emulated/0/Download'); |  | ||||||
|     String path = 'Downloads'; // TODO: See if hardcoding this can be avoided |  | ||||||
|     var downloadsAccessible = false; |  | ||||||
|     try { |  | ||||||
|       downloadsAccessible = exportDir.existsSync(); |  | ||||||
|     } catch (e) { |  | ||||||
|       logs.add('Error accessing Downloads (will use fallback): $e'); |  | ||||||
|     } |  | ||||||
|     if (!downloadsAccessible) { |  | ||||||
|       exportDir = Directory(await settingsProvider.getAppDir()); |  | ||||||
|       path = exportDir.path; |  | ||||||
|     } |  | ||||||
|     File export = File( |  | ||||||
|         '${exportDir.path}/${tr('obtainiumExportHyphenatedLowercase')}-${DateTime.now().millisecondsSinceEpoch}.json'); |  | ||||||
|     export.writeAsStringSync( |  | ||||||
|         jsonEncode(apps.values.map((e) => e.app.toJson()).toList())); |  | ||||||
|     return path; |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   Future<int> importApps(String appsJSON) async { |   Future<int> importApps(String appsJSON) async { | ||||||
| @@ -1402,6 +1431,10 @@ Future<void> bgUpdateCheck(int taskId, Map<String, dynamic>? params) async { | |||||||
|       if (toNotify.isNotEmpty) { |       if (toNotify.isNotEmpty) { | ||||||
|         notificationsProvider.notify(UpdateNotification(toNotify)); |         notificationsProvider.notify(UpdateNotification(toNotify)); | ||||||
|       } |       } | ||||||
|  |       if (appsProvider.settingsProvider.autoExportOnUpdateCheckKeepNum > 0) { | ||||||
|  |         await appsProvider.exportApps(isAuto: true); | ||||||
|  |         await appsProvider.trimAutoExports(); | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|     // If you're done checking and found some silently installable updates, schedule another task which will run in install mode |     // If you're done checking and found some silently installable updates, schedule another task which will run in install mode | ||||||
|     if (didCompleteChecking && toInstall.isNotEmpty) { |     if (didCompleteChecking && toInstall.isNotEmpty) { | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| // Exposes functions used to save/load app settings | // Exposes functions used to save/load app settings | ||||||
|  |  | ||||||
| import 'dart:convert'; | import 'dart:convert'; | ||||||
| import 'dart:io'; |  | ||||||
|  |  | ||||||
| import 'package:easy_localization/easy_localization.dart'; | import 'package:easy_localization/easy_localization.dart'; | ||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| @@ -363,52 +362,41 @@ class SettingsProvider with ChangeNotifier { | |||||||
|     notifyListeners(); |     notifyListeners(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   Future<String> getAppDir() async { |   Uri? get exportDir { | ||||||
|     return prefs?.getString('appDir') ?? defaultAppDir!; |     var uriString = prefs?.getString('exportDir'); | ||||||
|  |     if (uriString != null) { | ||||||
|  |       return Uri.parse(uriString); | ||||||
|  |     } else { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   pickAppDir({bool useDefault = false}) async { |   Future<void> pickExportDirKeepLastN({bool remove = false}) async { | ||||||
|     var existingSAFPerms = (await saf.persistedUriPermissions()) ?? []; |     var existingSAFPerms = (await saf.persistedUriPermissions()) ?? []; | ||||||
|     var currentAppDir = await getAppDir(); |     var currentOneWayDataSyncDir = exportDir; | ||||||
|     if (currentAppDir != defaultAppDir) { |     Uri? newOneWayDataSyncDir; | ||||||
|       currentAppDir = currentAppDir.replaceFirst( |     if (!remove) { | ||||||
|           '/storage/emulated/0/', '/tree/primary%3A'); |       newOneWayDataSyncDir = (await saf.openDocumentTree()); | ||||||
|     } |  | ||||||
|     String? newAppDir; |  | ||||||
|     if (!useDefault) { |  | ||||||
|       var target = (await saf.openDocumentTree()); |  | ||||||
|       if (target != null) { |  | ||||||
|         newAppDir = target.path |  | ||||||
|             .replaceFirst('/tree/primary%3A', '/storage/emulated/0/'); |  | ||||||
|     } |     } | ||||||
|  |     if (currentOneWayDataSyncDir?.path != newOneWayDataSyncDir?.path) { | ||||||
|  |       if (newOneWayDataSyncDir == null) { | ||||||
|  |         prefs?.remove('exportDir'); | ||||||
|       } else { |       } else { | ||||||
|       newAppDir = defaultAppDir; |         prefs?.setString('exportDir', newOneWayDataSyncDir.toString()); | ||||||
|       } |       } | ||||||
|     newAppDir ??= defaultAppDir; |  | ||||||
|     if (currentAppDir != newAppDir) { |  | ||||||
|       moveDirectoryContents(Directory(currentAppDir), Directory(newAppDir!)); |  | ||||||
|       prefs?.setString('appDir', newAppDir); |  | ||||||
|       notifyListeners(); |       notifyListeners(); | ||||||
|     } |     } | ||||||
|     for (var e in existingSAFPerms) { |     for (var e in existingSAFPerms) { | ||||||
|       await saf.releasePersistableUriPermission(e.uri); |       await saf.releasePersistableUriPermission(e.uri); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   int get autoExportOnUpdateCheckKeepNum { | ||||||
|  |     return prefs?.getInt('autoExportOnUpdateCheckKeepNum') ?? 0; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| void moveDirectoryContents(Directory sourceDir, Directory destinationDir) { |   set autoExportOnUpdateCheckKeepNum(int val) { | ||||||
|   if (!destinationDir.existsSync()) { |     prefs?.setInt('autoExportOnUpdateCheckKeepNum', val); | ||||||
|     destinationDir.createSync(recursive: true); |     notifyListeners(); | ||||||
|   } |  | ||||||
|   List<FileSystemEntity> contents = sourceDir.listSync(); |  | ||||||
|   for (FileSystemEntity entity in contents) { |  | ||||||
|     String newPath = '${destinationDir.path}/${entity.uri.pathSegments.last}'; |  | ||||||
|     if (entity is File) { |  | ||||||
|       entity.renameSync(newPath); |  | ||||||
|     } else if (entity is Directory) { |  | ||||||
|       Directory newDestinationDir = Directory(newPath); |  | ||||||
|       moveDirectoryContents(entity, newDestinationDir); |  | ||||||
|       entity.deleteSync(recursive: true); |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -521,6 +521,20 @@ regExValidator(String? value) { | |||||||
|   return null; |   return null; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | intValidator(String? value, {bool positive = false}) { | ||||||
|  |   if (value == null) { | ||||||
|  |     return tr('invalidInput'); | ||||||
|  |   } | ||||||
|  |   var num = int.tryParse(value); | ||||||
|  |   if (num == null) { | ||||||
|  |     return tr('invalidInput'); | ||||||
|  |   } | ||||||
|  |   if (positive && num <= 0) { | ||||||
|  |     return tr('invalidInput'); | ||||||
|  |   } | ||||||
|  |   return null; | ||||||
|  | } | ||||||
|  |  | ||||||
| class SourceProvider { | class SourceProvider { | ||||||
|   // Add more source classes here so they are available via the service |   // Add more source classes here so they are available via the service | ||||||
|   List<AppSource> get sources => [ |   List<AppSource> get sources => [ | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user