mirror of
				https://github.com/ImranR98/Obtainium.git
				synced 2025-10-31 13:33:28 +01:00 
			
		
		
		
	- All Sources now have a "Track-Only" option that will prevent Obtainium from looking for APKs (though the App must still have a release of some kind so that a version string can be grabbed).
    - These Apps cannot be installed through Obtainium, but update notifications will still be sent.
    - The user needs to manually mark them as updated when appropriate.
    - This addresses issue #119.
    - It also partially addresses #44 by allowing some sources to be configured as "Track-Only"-only. The first such source (APKMirror) will be added later.
- Includes various UI changes to accommodate the above change.
- Also makes App loading a bit more responsive (sending Obtainium to the background then returning will now cause App re-load to pick up changes in App versioning that may have been made in the meantime, for instance through update checking).
			
			
This commit is contained in:
		| @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; | ||||
| import 'package:flutter/services.dart'; | ||||
| import 'package:obtainium/components/custom_app_bar.dart'; | ||||
| import 'package:obtainium/components/generated_form.dart'; | ||||
| import 'package:obtainium/components/generated_form_modal.dart'; | ||||
| import 'package:obtainium/custom_errors.dart'; | ||||
| import 'package:obtainium/pages/app.dart'; | ||||
| import 'package:obtainium/providers/apps_provider.dart'; | ||||
| @@ -22,8 +23,10 @@ class _AddAppPageState extends State<AddAppPage> { | ||||
|  | ||||
|   String userInput = ''; | ||||
|   AppSource? pickedSource; | ||||
|   List<String> additionalData = []; | ||||
|   bool validAdditionalData = true; | ||||
|   List<String> sourceSpecificAdditionalData = []; | ||||
|   bool sourceSpecificDataIsValid = true; | ||||
|   List<String> otherAdditionalData = []; | ||||
|   bool otherAdditionalDataIsValid = true; | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
| @@ -67,23 +70,34 @@ class _AddAppPageState extends State<AddAppPage> { | ||||
|                                     ] | ||||
|                                   ], | ||||
|                                   onValueChanges: (values, valid, isBuilding) { | ||||
|                                     setState(() { | ||||
|                                     fn() { | ||||
|                                       userInput = values[0]; | ||||
|                                       var source = valid | ||||
|                                           ? sourceProvider.getSource(userInput) | ||||
|                                           : null; | ||||
|                                       if (pickedSource != source) { | ||||
|                                         pickedSource = source; | ||||
|                                         additionalData = source != null | ||||
|                                             ? source.additionalDataDefaults | ||||
|                                         sourceSpecificAdditionalData = source != | ||||
|                                                 null | ||||
|                                             ? source | ||||
|                                                 .additionalSourceAppSpecificDefaults | ||||
|                                             : []; | ||||
|                                         validAdditionalData = source != null | ||||
|                                         sourceSpecificDataIsValid = source != | ||||
|                                                 null | ||||
|                                             ? sourceProvider | ||||
|                                                 .ifSourceAppsRequireAdditionalData( | ||||
|                                                     source) | ||||
|                                             : true; | ||||
|                                       } | ||||
|                                     }); | ||||
|                                     } | ||||
|  | ||||
|                                     if (isBuilding) { | ||||
|                                       fn(); | ||||
|                                     } else { | ||||
|                                       setState(() { | ||||
|                                         fn(); | ||||
|                                       }); | ||||
|                                     } | ||||
|                                   }, | ||||
|                                   defaultValues: const [])), | ||||
|                           const SizedBox( | ||||
| @@ -94,9 +108,14 @@ class _AddAppPageState extends State<AddAppPage> { | ||||
|                               : ElevatedButton( | ||||
|                                   onPressed: gettingAppInfo || | ||||
|                                           pickedSource == null || | ||||
|                                           (pickedSource!.additionalDataFormItems | ||||
|                                           (pickedSource! | ||||
|                                                   .additionalSourceAppSpecificFormItems | ||||
|                                                   .isNotEmpty && | ||||
|                                               !validAdditionalData) | ||||
|                                               !sourceSpecificDataIsValid) || | ||||
|                                           (pickedSource! | ||||
|                                                   .additionalAppSpecificSourceAgnosticDefaults | ||||
|                                                   .isNotEmpty && | ||||
|                                               !otherAdditionalDataIsValid) | ||||
|                                       ? null | ||||
|                                       : () async { | ||||
|                                           setState(() { | ||||
| @@ -107,47 +126,87 @@ class _AddAppPageState extends State<AddAppPage> { | ||||
|                                           var settingsProvider = | ||||
|                                               context.read<SettingsProvider>(); | ||||
|                                           () async { | ||||
|                                             HapticFeedback.selectionClick(); | ||||
|                                             App app = | ||||
|                                                 await sourceProvider.getApp( | ||||
|                                                     pickedSource!, | ||||
|                                                     userInput, | ||||
|                                                     additionalData); | ||||
|                                             await settingsProvider | ||||
|                                                 .getInstallPermission(); | ||||
|                                             // Only download the APK here if you need to for the package ID | ||||
|                                             if (sourceProvider | ||||
|                                                 .isTempId(app.id)) { | ||||
|                                               // ignore: use_build_context_synchronously | ||||
|                                               var apkUrl = await appsProvider | ||||
|                                                   .confirmApkUrl(app, context); | ||||
|                                               if (apkUrl == null) { | ||||
|                                                 throw ObtainiumError( | ||||
|                                                     'Cancelled'); | ||||
|                                             var userPickedTrackOnly = | ||||
|                                                 findGeneratedFormValueByKey( | ||||
|                                                         pickedSource! | ||||
|                                                             .additionalAppSpecificSourceAgnosticFormItems, | ||||
|                                                         otherAdditionalData, | ||||
|                                                         'trackOnlyFormItemKey') == | ||||
|                                                     'true'; | ||||
|                                             var cont = true; | ||||
|                                             if ((userPickedTrackOnly || | ||||
|                                                     pickedSource! | ||||
|                                                         .enforceTrackOnly) && | ||||
|                                                 await showDialog( | ||||
|                                                         context: context, | ||||
|                                                         builder: | ||||
|                                                             (BuildContext ctx) { | ||||
|                                                           return GeneratedFormModal( | ||||
|                                                             title: | ||||
|                                                                 'App is Track-Only', | ||||
|                                                             items: const [], | ||||
|                                                             defaultValues: const [], | ||||
|                                                             message: | ||||
|                                                                 '${pickedSource!.enforceTrackOnly ? 'Apps from this source are \'Track-Only\'.' : 'You have selected the \'Track-Only\' option.'}\n\nThe App will be tracked for updates, but Obtainium will not be able to download or install it.', | ||||
|                                                           ); | ||||
|                                                         }) == | ||||
|                                                     null) { | ||||
|                                               cont = false; | ||||
|                                             } | ||||
|                                             if (cont) { | ||||
|                                               HapticFeedback.selectionClick(); | ||||
|                                               App app = await sourceProvider.getApp( | ||||
|                                                   pickedSource!, | ||||
|                                                   userInput, | ||||
|                                                   sourceSpecificAdditionalData, | ||||
|                                                   trackOnly: pickedSource! | ||||
|                                                           .enforceTrackOnly || | ||||
|                                                       userPickedTrackOnly); | ||||
|                                               await settingsProvider | ||||
|                                                   .getInstallPermission(); | ||||
|                                               // Only download the APK here if you need to for the package ID | ||||
|                                               if (sourceProvider | ||||
|                                                       .isTempId(app.id) && | ||||
|                                                   !app.trackOnly) { | ||||
|                                                 // ignore: use_build_context_synchronously | ||||
|                                                 var apkUrl = await appsProvider | ||||
|                                                     .confirmApkUrl( | ||||
|                                                         app, context); | ||||
|                                                 if (apkUrl == null) { | ||||
|                                                   throw ObtainiumError( | ||||
|                                                       'Cancelled'); | ||||
|                                                 } | ||||
|                                                 app.preferredApkIndex = | ||||
|                                                     app.apkUrls.indexOf(apkUrl); | ||||
|                                                 var downloadedApk = | ||||
|                                                     await appsProvider | ||||
|                                                         .downloadApp(app); | ||||
|                                                 app.id = downloadedApk.appId; | ||||
|                                               } | ||||
|                                               app.preferredApkIndex = | ||||
|                                                   app.apkUrls.indexOf(apkUrl); | ||||
|                                               var downloadedApk = | ||||
|                                                   await appsProvider | ||||
|                                                       .downloadApp(app); | ||||
|                                               app.id = downloadedApk.appId; | ||||
|                                             } | ||||
|                                             if (appsProvider.apps | ||||
|                                                 .containsKey(app.id)) { | ||||
|                                               throw ObtainiumError( | ||||
|                                                   'App already added'); | ||||
|                                             } | ||||
|                                             await appsProvider.saveApps([app]); | ||||
|                                               if (appsProvider.apps | ||||
|                                                   .containsKey(app.id)) { | ||||
|                                                 throw ObtainiumError( | ||||
|                                                     'App already added'); | ||||
|                                               } | ||||
|                                               if (app.trackOnly) { | ||||
|                                                 app.installedVersion = | ||||
|                                                     app.latestVersion; | ||||
|                                               } | ||||
|                                               await appsProvider | ||||
|                                                   .saveApps([app]); | ||||
|  | ||||
|                                             return app; | ||||
|                                               return app; | ||||
|                                             } | ||||
|                                           }() | ||||
|                                               .then((app) { | ||||
|                                             Navigator.push( | ||||
|                                                 context, | ||||
|                                                 MaterialPageRoute( | ||||
|                                                     builder: (context) => | ||||
|                                                         AppPage( | ||||
|                                                             appId: app.id))); | ||||
|                                             if (app != null) { | ||||
|                                               Navigator.push( | ||||
|                                                   context, | ||||
|                                                   MaterialPageRoute( | ||||
|                                                       builder: (context) => | ||||
|                                                           AppPage( | ||||
|                                                               appId: app.id))); | ||||
|                                             } | ||||
|                                           }).catchError((e) { | ||||
|                                             showError(e, context); | ||||
|                                           }).whenComplete(() { | ||||
| @@ -160,7 +219,11 @@ class _AddAppPageState extends State<AddAppPage> { | ||||
|                         ], | ||||
|                       ), | ||||
|                       if (pickedSource != null && | ||||
|                           pickedSource!.additionalDataDefaults.isNotEmpty) | ||||
|                           (pickedSource!.additionalSourceAppSpecificDefaults | ||||
|                                   .isNotEmpty || | ||||
|                               pickedSource! | ||||
|                                   .additionalAppSpecificSourceAgnosticDefaults | ||||
|                                   .isNotEmpty)) | ||||
|                         Column( | ||||
|                           crossAxisAlignment: CrossAxisAlignment.stretch, | ||||
|                           children: [ | ||||
| @@ -176,19 +239,54 @@ class _AddAppPageState extends State<AddAppPage> { | ||||
|                               height: 16, | ||||
|                             ), | ||||
|                             if (pickedSource! | ||||
|                                 .additionalDataFormItems.isNotEmpty) | ||||
|                                 .additionalSourceAppSpecificFormItems | ||||
|                                 .isNotEmpty) | ||||
|                               GeneratedForm( | ||||
|                                   items: pickedSource!.additionalDataFormItems, | ||||
|                                   items: pickedSource! | ||||
|                                       .additionalSourceAppSpecificFormItems, | ||||
|                                   onValueChanges: (values, valid, isBuilding) { | ||||
|                                     setState(() { | ||||
|                                       additionalData = values; | ||||
|                                       validAdditionalData = valid; | ||||
|                                     }); | ||||
|                                     if (isBuilding) { | ||||
|                                       sourceSpecificAdditionalData = values; | ||||
|                                       sourceSpecificDataIsValid = valid; | ||||
|                                     } else { | ||||
|                                       setState(() { | ||||
|                                         sourceSpecificAdditionalData = values; | ||||
|                                         sourceSpecificDataIsValid = valid; | ||||
|                                       }); | ||||
|                                     } | ||||
|                                   }, | ||||
|                                   defaultValues: | ||||
|                                       pickedSource!.additionalDataDefaults), | ||||
|                                   defaultValues: pickedSource! | ||||
|                                       .additionalSourceAppSpecificDefaults), | ||||
|                             if (pickedSource! | ||||
|                                 .additionalDataFormItems.isNotEmpty) | ||||
|                                 .additionalSourceAppSpecificFormItems | ||||
|                                 .isNotEmpty) | ||||
|                               const SizedBox( | ||||
|                                 height: 8, | ||||
|                               ), | ||||
|                             if (pickedSource! | ||||
|                                 .additionalAppSpecificSourceAgnosticFormItems | ||||
|                                 .isNotEmpty) | ||||
|                               GeneratedForm( | ||||
|                                   items: pickedSource! | ||||
|                                       .additionalAppSpecificSourceAgnosticFormItems | ||||
|                                       .map((e) => [e]) | ||||
|                                       .toList(), | ||||
|                                   onValueChanges: (values, valid, isBuilding) { | ||||
|                                     if (isBuilding) { | ||||
|                                       otherAdditionalData = values; | ||||
|                                       otherAdditionalDataIsValid = valid; | ||||
|                                     } else { | ||||
|                                       setState(() { | ||||
|                                         otherAdditionalData = values; | ||||
|                                         otherAdditionalDataIsValid = valid; | ||||
|                                       }); | ||||
|                                     } | ||||
|                                   }, | ||||
|                                   defaultValues: pickedSource! | ||||
|                                       .additionalAppSpecificSourceAgnosticDefaults), | ||||
|                             if (pickedSource! | ||||
|                                 .additionalAppSpecificSourceAgnosticDefaults | ||||
|                                 .isNotEmpty) | ||||
|                               const SizedBox( | ||||
|                                 height: 8, | ||||
|                               ), | ||||
|   | ||||
		Reference in New Issue
	
	Block a user