From 6a21045e5b2e831048a09a10c731daf8b6982cbb Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Tue, 20 Dec 2022 18:00:22 -0500 Subject: [PATCH] Progress --- lib/app_sources/apkmirror.dart | 5 +- lib/app_sources/fdroid.dart | 7 +- lib/app_sources/fdroidrepo.dart | 9 +- lib/app_sources/github.dart | 17 ++-- lib/app_sources/gitlab.dart | 5 +- lib/app_sources/izzyondroid.dart | 7 +- lib/app_sources/mullvad.dart | 5 +- lib/app_sources/signal.dart | 5 +- lib/app_sources/sourceforge.dart | 5 +- lib/app_sources/steammobile.dart | 9 +- lib/main.dart | 1 - lib/pages/add_app.dart | 76 +++++------------ lib/pages/app.dart | 45 +++++++--- lib/pages/apps.dart | 12 +-- lib/providers/apps_provider.dart | 18 ++-- lib/providers/source_provider.dart | 128 +++++++++++++++-------------- 16 files changed, 181 insertions(+), 173 deletions(-) diff --git a/lib/app_sources/apkmirror.dart b/lib/app_sources/apkmirror.dart index c0efb87..b562b89 100644 --- a/lib/app_sources/apkmirror.dart +++ b/lib/app_sources/apkmirror.dart @@ -25,8 +25,9 @@ class APKMirror extends AppSource { @override Future getLatestAPKDetails( - String standardUrl, Map additionalData, - {bool trackOnly = false}) async { + String standardUrl, + Map additionalSettings, + ) async { Response res = await get(Uri.parse('$standardUrl/feed')); if (res.statusCode == 200) { String? titleString = parse(res.body) diff --git a/lib/app_sources/fdroid.dart b/lib/app_sources/fdroid.dart index fc4f4d1..f5ef19f 100644 --- a/lib/app_sources/fdroid.dart +++ b/lib/app_sources/fdroid.dart @@ -32,7 +32,7 @@ class FDroid extends AppSource { @override String? tryInferringAppId(String standardUrl, - {Map additionalData = const {}}) { + {Map additionalSettings = const {}}) { return Uri.parse(standardUrl).pathSegments.last; } @@ -60,8 +60,9 @@ class FDroid extends AppSource { @override Future getLatestAPKDetails( - String standardUrl, Map additionalData, - {bool trackOnly = false}) async { + String standardUrl, + Map additionalSettings, + ) async { String? appId = tryInferringAppId(standardUrl); return getAPKUrlsFromFDroidPackagesAPIResponse( await get(Uri.parse('https://f-droid.org/api/v1/packages/$appId')), diff --git a/lib/app_sources/fdroidrepo.dart b/lib/app_sources/fdroidrepo.dart index 7b537ef..dd4c85f 100644 --- a/lib/app_sources/fdroidrepo.dart +++ b/lib/app_sources/fdroidrepo.dart @@ -9,7 +9,7 @@ class FDroidRepo extends AppSource { FDroidRepo() { name = tr('fdroidThirdPartyRepo'); - additionalSourceAppSpecificFormItems = [ + additionalSourceAppSpecificSettingFormItems = [ [ GeneratedFormItem('appIdOrName', label: tr('appIdOrName'), @@ -32,9 +32,10 @@ class FDroidRepo extends AppSource { @override Future getLatestAPKDetails( - String standardUrl, Map additionalData, - {bool trackOnly = false}) async { - String? appIdOrName = additionalData['appIdOrName']; + String standardUrl, + Map additionalSettings, + ) async { + String? appIdOrName = additionalSettings['appIdOrName']; if (appIdOrName == null) { throw NoReleasesError(); } diff --git a/lib/app_sources/github.dart b/lib/app_sources/github.dart index a77a2fa..3cffd37 100644 --- a/lib/app_sources/github.dart +++ b/lib/app_sources/github.dart @@ -49,7 +49,7 @@ class GitHub extends AppSource { ]) ]; - additionalSourceAppSpecificFormItems = [ + additionalSourceAppSpecificSettingFormItems = [ [ GeneratedFormItem('includePrereleases', label: tr('includePrereleases'), @@ -110,14 +110,15 @@ class GitHub extends AppSource { @override Future getLatestAPKDetails( - String standardUrl, Map additionalData, - {bool trackOnly = false}) async { - var includePrereleases = additionalData['includePrereleases'] == 'true'; + String standardUrl, + Map additionalSettings, + ) async { + var includePrereleases = additionalSettings['includePrereleases'] == 'true'; var fallbackToOlderReleases = - additionalData['fallbackToOlderReleases'] == 'true'; + additionalSettings['fallbackToOlderReleases'] == 'true'; var regexFilter = - additionalData['filterReleaseTitlesByRegEx']?.isNotEmpty == true - ? additionalData['filterReleaseTitlesByRegEx'] + additionalSettings['filterReleaseTitlesByRegEx']?.isNotEmpty == true + ? additionalSettings['filterReleaseTitlesByRegEx'] : null; Response res = await get(Uri.parse( 'https://${await getCredentialPrefixIfAny()}api.$host/repos${standardUrl.substring('https://$host'.length)}/releases')); @@ -149,7 +150,7 @@ class GitHub extends AppSource { continue; } var apkUrls = getReleaseAPKUrls(releases[i]); - if (apkUrls.isEmpty && !trackOnly) { + if (apkUrls.isEmpty && additionalSettings['trackOnly'] != 'true') { continue; } targetRelease = releases[i]; diff --git a/lib/app_sources/gitlab.dart b/lib/app_sources/gitlab.dart index efaff00..225c610 100644 --- a/lib/app_sources/gitlab.dart +++ b/lib/app_sources/gitlab.dart @@ -25,8 +25,9 @@ class GitLab extends AppSource { @override Future getLatestAPKDetails( - String standardUrl, Map additionalData, - {bool trackOnly = false}) async { + String standardUrl, + Map additionalSettings, + ) async { Response res = await get(Uri.parse('$standardUrl/-/tags?format=atom')); if (res.statusCode == 200) { var standardUri = Uri.parse(standardUrl); diff --git a/lib/app_sources/izzyondroid.dart b/lib/app_sources/izzyondroid.dart index d2b08f1..eb174f8 100644 --- a/lib/app_sources/izzyondroid.dart +++ b/lib/app_sources/izzyondroid.dart @@ -23,14 +23,15 @@ class IzzyOnDroid extends AppSource { @override String? tryInferringAppId(String standardUrl, - {Map additionalData = const {}}) { + {Map additionalSettings = const {}}) { return FDroid().tryInferringAppId(standardUrl); } @override Future getLatestAPKDetails( - String standardUrl, Map additionalData, - {bool trackOnly = false}) async { + String standardUrl, + Map additionalSettings, + ) async { String? appId = tryInferringAppId(standardUrl); return FDroid().getAPKUrlsFromFDroidPackagesAPIResponse( await get( diff --git a/lib/app_sources/mullvad.dart b/lib/app_sources/mullvad.dart index f19ab6a..283cf37 100644 --- a/lib/app_sources/mullvad.dart +++ b/lib/app_sources/mullvad.dart @@ -24,8 +24,9 @@ class Mullvad extends AppSource { @override Future getLatestAPKDetails( - String standardUrl, Map additionalData, - {bool trackOnly = false}) async { + String standardUrl, + Map additionalSettings, + ) async { Response res = await get(Uri.parse('$standardUrl/en/download/android')); if (res.statusCode == 200) { var version = parse(res.body) diff --git a/lib/app_sources/signal.dart b/lib/app_sources/signal.dart index dbf88fb..90004a0 100644 --- a/lib/app_sources/signal.dart +++ b/lib/app_sources/signal.dart @@ -18,8 +18,9 @@ class Signal extends AppSource { @override Future getLatestAPKDetails( - String standardUrl, Map additionalData, - {bool trackOnly = false}) async { + String standardUrl, + Map additionalSettings, + ) async { Response res = await get(Uri.parse('https://updates.$host/android/latest.json')); if (res.statusCode == 200) { diff --git a/lib/app_sources/sourceforge.dart b/lib/app_sources/sourceforge.dart index 8ad3e48..d847dbd 100644 --- a/lib/app_sources/sourceforge.dart +++ b/lib/app_sources/sourceforge.dart @@ -23,8 +23,9 @@ class SourceForge extends AppSource { @override Future getLatestAPKDetails( - String standardUrl, Map additionalData, - {bool trackOnly = false}) async { + String standardUrl, + Map additionalSettings, + ) async { Response res = await get(Uri.parse('$standardUrl/rss?path=/')); if (res.statusCode == 200) { var parsedHtml = parse(res.body); diff --git a/lib/app_sources/steammobile.dart b/lib/app_sources/steammobile.dart index db9ec94..8fe8ece 100644 --- a/lib/app_sources/steammobile.dart +++ b/lib/app_sources/steammobile.dart @@ -9,7 +9,7 @@ class SteamMobile extends AppSource { SteamMobile() { host = 'store.steampowered.com'; name = tr('steam'); - additionalSourceAppSpecificFormItems = [ + additionalSourceAppSpecificSettingFormItems = [ [ GeneratedFormItem('app', label: tr('app'), required: true, opts: apks.entries.toList()) @@ -29,11 +29,12 @@ class SteamMobile extends AppSource { @override Future getLatestAPKDetails( - String standardUrl, Map additionalData, - {bool trackOnly = false}) async { + String standardUrl, + Map additionalSettings, + ) async { Response res = await get(Uri.parse('https://$host/mobile')); if (res.statusCode == 200) { - var apkNamePrefix = additionalData['app']; + var apkNamePrefix = additionalSettings['app']; if (apkNamePrefix == null) { throw NoReleasesError(); } diff --git a/lib/main.dart b/lib/main.dart index 0e17272..3be599c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -202,7 +202,6 @@ class _ObtainiumState extends State { 0, {}, null, - false, false) ]); } diff --git a/lib/pages/add_app.dart b/lib/pages/add_app.dart index f2a67cf..71c92f9 100644 --- a/lib/pages/add_app.dart +++ b/lib/pages/add_app.dart @@ -27,10 +27,8 @@ class _AddAppPageState extends State { String userInput = ''; String searchQuery = ''; AppSource? pickedSource; - Map sourceSpecificAdditionalData = {}; - bool sourceSpecificDataIsValid = true; - Map otherAdditionalData = {}; - bool otherAdditionalDataIsValid = true; + Map additionalSettings = {}; + bool additionalSettingsValid = true; @override Widget build(BuildContext context) { @@ -43,12 +41,12 @@ class _AddAppPageState extends State { var source = valid ? sourceProvider.getSource(userInput) : null; if (pickedSource.runtimeType != source.runtimeType) { pickedSource = source; - sourceSpecificAdditionalData = source != null + additionalSettings = source != null ? getDefaultValuesFromFormItems( - source.additionalSourceAppSpecificFormItems) + source.combinedAppSpecificSettingFormItems) : {}; - sourceSpecificDataIsValid = source != null - ? !sourceProvider.ifSourceAppsRequireAdditionalData(source) + additionalSettingsValid = source != null + ? !sourceProvider.ifRequiredAppSpecificSettingsExist(source) : true; } } @@ -68,10 +66,9 @@ class _AddAppPageState extends State { }); var settingsProvider = context.read(); () async { - var userPickedTrackOnly = - otherAdditionalData['trackOnlyFormItemKey'] == 'true'; + var userPickedTrackOnly = additionalSettings['trackOnly'] == 'true'; var userPickedNoVersionDetection = - otherAdditionalData['noVersionDetectionKey'] == 'true'; + additionalSettings['noVersionDetection'] == 'true'; var cont = true; if ((userPickedTrackOnly || pickedSource!.enforceTrackOnly) && await showDialog( @@ -108,14 +105,15 @@ class _AddAppPageState extends State { HapticFeedback.selectionClick(); var trackOnly = pickedSource!.enforceTrackOnly || userPickedTrackOnly; App app = await sourceProvider.getApp( - pickedSource!, userInput, sourceSpecificAdditionalData, + pickedSource!, userInput, additionalSettings, trackOnlyOverride: trackOnly, noVersionDetectionOverride: userPickedNoVersionDetection); if (!trackOnly) { await settingsProvider.getInstallPermission(); } // Only download the APK here if you need to for the package ID - if (sourceProvider.isTempId(app.id) && !app.trackOnly) { + if (sourceProvider.isTempId(app.id) && + app.additionalSettings['trackOnly'] != 'true') { // ignore: use_build_context_synchronously var apkUrl = await appsProvider.confirmApkUrl(app, context); if (apkUrl == null) { @@ -130,7 +128,7 @@ class _AddAppPageState extends State { if (appsProvider.apps.containsKey(app.id)) { throw ObtainiumError(tr('appAlreadyAdded')); } - if (app.trackOnly) { + if (app.additionalSettings['trackOnly'] == 'true') { app.installedVersion = app.latestVersion; } await appsProvider.saveApps([app]); @@ -206,13 +204,9 @@ class _AddAppPageState extends State { onPressed: gettingAppInfo || pickedSource == null || (pickedSource! - .additionalSourceAppSpecificFormItems + .combinedAppSpecificSettingFormItems .isNotEmpty && - !sourceSpecificDataIsValid) || - (pickedSource! - .additionalAppSpecificSourceAgnosticFormItems - .isNotEmpty && - !otherAdditionalDataIsValid) + !additionalSettingsValid) ? null : addApp, child: Text(tr('add'))) @@ -305,15 +299,8 @@ class _AddAppPageState extends State { ], ), if (pickedSource != null && - (pickedSource!.additionalSourceAppSpecificFormItems - .isNotEmpty || - pickedSource! - .additionalAppSpecificSourceAgnosticFormItems - .where((e) => pickedSource!.enforceTrackOnly - ? e.key != 'trackOnlyFormItemKey' - : true) - .map((e) => [e]) - .isNotEmpty)) + (pickedSource! + .combinedAppSpecificSettingFormItems.isNotEmpty)) Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ @@ -329,39 +316,14 @@ class _AddAppPageState extends State { const SizedBox( height: 16, ), - if (pickedSource! - .additionalSourceAppSpecificFormItems - .isNotEmpty) - GeneratedForm( - items: pickedSource! - .additionalSourceAppSpecificFormItems, - onValueChanges: (values, valid, isBuilding) { - if (!isBuilding) { - setState(() { - sourceSpecificAdditionalData = values; - sourceSpecificDataIsValid = valid; - }); - } - }), - if (pickedSource! - .additionalAppSpecificSourceAgnosticFormItems - .isNotEmpty) - const SizedBox( - height: 8, - ), GeneratedForm( items: pickedSource! - .additionalAppSpecificSourceAgnosticFormItems - .where((e) => pickedSource!.enforceTrackOnly - ? e.key != 'trackOnlyFormItemKey' - : true) - .map((e) => [e]) - .toList(), + .combinedAppSpecificSettingFormItems, onValueChanges: (values, valid, isBuilding) { if (!isBuilding) { setState(() { - otherAdditionalData = values; - otherAdditionalDataIsValid = valid; + additionalSettings = values; + additionalSettingsValid = valid; }); } }), diff --git a/lib/pages/app.dart b/lib/pages/app.dart index b978e07..ba131fc 100644 --- a/lib/pages/app.dart +++ b/lib/pages/app.dart @@ -40,6 +40,7 @@ class _AppPageState extends State { prevApp = app; getUpdate(app.app.id); } + var trackOnly = app?.app.additionalSettings['trackOnly'] == 'true'; return Scaffold( appBar: settingsProvider.showAppWebpage ? AppBar() : null, backgroundColor: Theme.of(context).colorScheme.surface, @@ -111,7 +112,7 @@ class _AppPageState extends State { Text( '${tr('installedVersionX', args: [ app?.app.installedVersion ?? tr('none') - ])}${app?.app.trackOnly == true ? ' ${tr('estimateInBrackets')}\n\n${tr('xIsTrackOnly', args: [ + ])}${trackOnly ? ' ${tr('estimateInBrackets')}\n\n${tr('xIsTrackOnly', args: [ tr('app') ])}' : ''}', textAlign: TextAlign.center, @@ -151,7 +152,7 @@ class _AppPageState extends State { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ if (app?.app.installedVersion != null && - app?.app.trackOnly == false && + !trackOnly && app?.app.installedVersion != app?.app.latestVersion) IconButton( onPressed: app?.downloadProgress != null @@ -202,8 +203,8 @@ class _AppPageState extends State { tooltip: 'Mark as Updated', icon: const Icon(Icons.done)), if (source != null && - source.additionalSourceAppSpecificFormItems - .isNotEmpty) + source + .combinedAppSpecificSettingFormItems.isNotEmpty) IconButton( onPressed: app?.downloadProgress != null ? null @@ -211,14 +212,36 @@ class _AppPageState extends State { showDialog>( context: context, builder: (BuildContext ctx) { + var items = source + .combinedAppSpecificSettingFormItems + .map((row) { + row.map((e) { + if (app?.app.additionalSettings[ + e.key] != + null) { + e.defaultValue = app?.app + .additionalSettings[ + e.key]; + } + return e; + }).toList(); + return row; + }).toList(); return GeneratedFormModal( title: 'Additional Options', - items: source - .additionalSourceAppSpecificFormItems); + items: items); }).then((values) { if (app != null && values != null) { var changedApp = app.app; - changedApp.additionalData = values; + changedApp.additionalSettings = + values; + if (source.enforceTrackOnly) { + changedApp.additionalSettings[ + 'trackOnly'] = 'true'; + showError( + tr('appsFromSourceAreTrackOnly'), + context); + } appsProvider.saveApps( [changedApp]).then((value) { getUpdate(changedApp.id); @@ -238,7 +261,9 @@ class _AppPageState extends State { ? () { HapticFeedback.heavyImpact(); () async { - if (app?.app.trackOnly != true) { + if (app?.app.additionalSettings[ + 'trackOnly'] != + 'true') { await settingsProvider .getInstallPermission(); } @@ -260,10 +285,10 @@ class _AppPageState extends State { } : null, child: Text(app?.app.installedVersion == null - ? app?.app.trackOnly == false + ? !trackOnly ? 'Install' : 'Mark Installed' - : app?.app.trackOnly == false + : !trackOnly ? 'Update' : 'Mark Updated'))), const SizedBox(width: 16.0), diff --git a/lib/pages/apps.dart b/lib/pages/apps.dart index e9db472..71f3cb9 100644 --- a/lib/pages/apps.dart +++ b/lib/pages/apps.dart @@ -139,14 +139,16 @@ class AppsPageState extends State { List trackOnlyUpdateIdsAllOrSelected = []; existingUpdateIdsAllOrSelected = existingUpdateIdsAllOrSelected.where((id) { - if (appsProvider.apps[id]!.app.trackOnly) { + if (appsProvider.apps[id]!.app.additionalSettings['trackOnly'] == + 'true') { trackOnlyUpdateIdsAllOrSelected.add(id); return false; } return true; }).toList(); newInstallIdsAllOrSelected = newInstallIdsAllOrSelected.where((id) { - if (appsProvider.apps[id]!.app.trackOnly) { + if (appsProvider.apps[id]!.app.additionalSettings['trackOnly'] == + 'true') { trackOnlyUpdateIdsAllOrSelected.add(id); return false; } @@ -271,7 +273,7 @@ class AppsPageState extends State { SizedBox( width: 100, child: Text( - '${sortedApps[index].app.installedVersion ?? tr('notInstalled')}${sortedApps[index].app.trackOnly == true ? ' ${tr('estimateInBrackets')}' : ''}', + '${sortedApps[index].app.installedVersion ?? tr('notInstalled')}${sortedApps[index].app.additionalSettings['trackOnly'] == 'true' ? ' ${tr('estimateInBrackets')}' : ''}', overflow: TextOverflow.fade, textAlign: TextAlign.end, )), @@ -289,7 +291,7 @@ class AppsPageState extends State { child: appsProvider.areDownloadsRunning() ? Text(tr('pleaseWait')) : Text( - '${tr('updateAvailable')}${sortedApps[index].app.trackOnly ? ' ${tr('estimateInBracketsShort')}' : ''}', + '${tr('updateAvailable')}${sortedApps[index].app.additionalSettings['trackOnly'] == 'true' ? ' ${tr('estimateInBracketsShort')}' : ''}', style: TextStyle( fontStyle: FontStyle.italic, decoration: changesUrl == null @@ -343,7 +345,7 @@ class AppsPageState extends State { : IconButton( visualDensity: VisualDensity.compact, onPressed: () { - showDialog?>( + showDialog?>( context: context, builder: (BuildContext ctx) { return GeneratedFormModal( diff --git a/lib/providers/apps_provider.dart b/lib/providers/apps_provider.dart index 5a1cf8a..570a3dd 100644 --- a/lib/providers/apps_provider.dart +++ b/lib/providers/apps_provider.dart @@ -313,7 +313,8 @@ class AppsProvider with ChangeNotifier { throw ObtainiumError(tr('appNotFound')); } String? apkUrl; - if (!apps[id]!.app.trackOnly) { + var trackOnly = apps[id]!.app.additionalSettings['trackOnly'] == 'true'; + if (!trackOnly) { apkUrl = await confirmApkUrl(apps[id]!.app, context); } if (apkUrl != null) { @@ -326,7 +327,7 @@ class AppsProvider with ChangeNotifier { appsToInstall.add(id); } } - if (apps[id]!.app.trackOnly) { + if (trackOnly) { trackOnlyAppsToUpdate.add(id); } } @@ -451,9 +452,10 @@ class AppsProvider with ChangeNotifier { // Don't save changes, just return the object if changes were made (else null) App? getCorrectedInstallStatusAppIfPossible(App app, AppInfo? installedInfo) { var modded = false; - if (installedInfo == null && - app.installedVersion != null && - !app.trackOnly) { + var trackOnly = app.additionalSettings['trackOnly'] == 'true'; + var noVersionDetection = + app.additionalSettings['noVersionDetection'] == 'true'; + if (installedInfo == null && app.installedVersion != null && !trackOnly) { app.installedVersion = null; modded = true; } else if (installedInfo?.versionName != null && @@ -462,7 +464,7 @@ class AppsProvider with ChangeNotifier { modded = true; } else if (installedInfo?.versionName != null && installedInfo!.versionName != app.installedVersion && - !app.noVersionDetection) { + !noVersionDetection) { String? correctedInstalledVersion = reconcileRealAndInternalVersions( installedInfo.versionName!, app.installedVersion!); if (correctedInstalledVersion != null) { @@ -472,7 +474,7 @@ class AppsProvider with ChangeNotifier { } if (app.installedVersion != null && app.installedVersion != app.latestVersion && - !app.noVersionDetection) { + !noVersionDetection) { app.installedVersion = reconcileRealAndInternalVersions( app.installedVersion!, app.latestVersion, matchMode: true) ?? @@ -625,7 +627,7 @@ class AppsProvider with ChangeNotifier { App newApp = await sourceProvider.getApp( sourceProvider.getSource(currentApp.url), currentApp.url, - currentApp.additionalData, + currentApp.additionalSettings, currentApp: currentApp); if (currentApp.preferredApkIndex < newApp.apkUrls.length) { newApp.preferredApkIndex = currentApp.preferredApkIndex; diff --git a/lib/providers/source_provider.dart b/lib/providers/source_provider.dart index 61df886..3a51e50 100644 --- a/lib/providers/source_provider.dart +++ b/lib/providers/source_provider.dart @@ -44,11 +44,9 @@ class App { late String latestVersion; List apkUrls = []; late int preferredApkIndex; - late Map additionalData; + late Map additionalSettings; late DateTime? lastUpdateCheck; bool pinned = false; - bool trackOnly = false; - bool noVersionDetection = false; App( this.id, this.url, @@ -58,38 +56,37 @@ class App { this.latestVersion, this.apkUrls, this.preferredApkIndex, - this.additionalData, + this.additionalSettings, this.lastUpdateCheck, - this.pinned, - this.trackOnly, - {this.noVersionDetection = false}); + this.pinned); @override String toString() { - return 'ID: $id URL: $url INSTALLED: $installedVersion LATEST: $latestVersion APK: $apkUrls PREFERREDAPK: $preferredApkIndex ADDITIONALDATA: ${additionalData.toString()} LASTCHECK: ${lastUpdateCheck.toString()} PINNED $pinned'; + return 'ID: $id URL: $url INSTALLED: $installedVersion LATEST: $latestVersion APK: $apkUrls PREFERREDAPK: $preferredApkIndex ADDITIONALSETTINGS: ${additionalSettings.toString()} LASTCHECK: ${lastUpdateCheck.toString()} PINNED $pinned'; } factory App.fromJson(Map json) { - var formItems = SourceProvider() - .getSource(json['url']) - .additionalSourceAppSpecificFormItems + var source = SourceProvider().getSource(json['url']); + var formItems = source.combinedAppSpecificSettingFormItems .reduce((value, element) => [...value, ...element]); - Map additionalData = + Map additionalSettings = getDefaultValuesFromFormItems([formItems]); + if (json['additionalSettings'] != null) { + additionalSettings.addEntries( + Map.from(jsonDecode(json['additionalSettings'])) + .entries); + } + // If needed, migrate old-style additionalData to new-style additionalSettings if (json['additionalData'] != null) { - try { - additionalData = - Map.from(jsonDecode(json['additionalData'])); - } catch (e) { - // Migrate old-style additionalData List to new-style Map - List temp = - List.from(jsonDecode(json['additionalData'])); - temp.asMap().forEach((i, value) { - if (i < formItems.length) { - additionalData[formItems[i].key] = value; - } - }); - } + List temp = List.from(jsonDecode(json['additionalData'])); + temp.asMap().forEach((i, value) { + if (i < formItems.length) { + additionalSettings[formItems[i].key] = value; + } + }); + additionalSettings['trackOnly'] = (json['trackOnly'] ?? false).toString(); + additionalSettings['noVersionDetection'] = + (json['noVersionDetection'] ?? false).toString(); } return App( json['id'] as String, @@ -106,13 +103,11 @@ class App { json['preferredApkIndex'] == null ? 0 : json['preferredApkIndex'] as int, - additionalData, + additionalSettings, json['lastUpdateCheck'] == null ? null : DateTime.fromMicrosecondsSinceEpoch(json['lastUpdateCheck']), - json['pinned'] ?? false, - json['trackOnly'] ?? false, - noVersionDetection: json['noVersionDetection'] ?? false); + json['pinned'] ?? false); } Map toJson() => { @@ -124,11 +119,9 @@ class App { 'latestVersion': latestVersion, 'apkUrls': jsonEncode(apkUrls), 'preferredApkIndex': preferredApkIndex, - 'additionalData': jsonEncode(additionalData), + 'additionalSettings': jsonEncode(additionalSettings), 'lastUpdateCheck': lastUpdateCheck?.microsecondsSinceEpoch, - 'pinned': pinned, - 'trackOnly': trackOnly, - 'noVersionDetection': noVersionDetection + 'pinned': pinned }; } @@ -183,26 +176,39 @@ class AppSource { } Future getLatestAPKDetails( - String standardUrl, Map additionalData, - {bool trackOnly = false}) { + String standardUrl, Map additionalSettings) { throw NotImplementedError(); } // Different Sources may need different kinds of additional data for Apps - List> additionalSourceAppSpecificFormItems = []; + List> additionalSourceAppSpecificSettingFormItems = + []; // Some additional data may be needed for Apps regardless of Source - final List additionalAppSpecificSourceAgnosticFormItems = [ - GeneratedFormItem( - 'trackOnlyFormItemKey', - label: tr('trackOnly'), - type: FormItemType.bool, - ), - GeneratedFormItem('noVersionDetectionKey', - label: 'Do not attempt version detection', // TODO - type: FormItemType.bool) + final List> + additionalAppSpecificSourceAgnosticSettingFormItems = [ + [ + GeneratedFormItem( + 'trackOnly', + label: tr('trackOnly'), + type: FormItemType.bool, + ) + ], + [ + GeneratedFormItem('noVersionDetection', + label: 'Do not attempt version detection', // TODO + type: FormItemType.bool) + ] ]; + // Previous 2 variables combined into one at runtime for convenient usage + List> get combinedAppSpecificSettingFormItems { + return [ + ...additionalSourceAppSpecificSettingFormItems, + ...additionalAppSpecificSourceAgnosticSettingFormItems + ]; + } + // Some Sources may have additional settings at the Source level (not specific to Apps) - these use SettingsProvider List additionalSourceSpecificSettingFormItems = []; @@ -220,7 +226,7 @@ class AppSource { } String? tryInferringAppId(String standardUrl, - {Map additionalData = const {}}) { + {Map additionalSettings = const {}}) { return null; } } @@ -280,8 +286,8 @@ class SourceProvider { return source; } - bool ifSourceAppsRequireAdditionalData(AppSource source) { - for (var row in source.additionalSourceAppSpecificFormItems) { + bool ifRequiredAppSpecificSettingsExist(AppSource source) { + for (var row in source.combinedAppSpecificSettingFormItems) { for (var element in row) { if (element.required && element.opts == null) { return true; @@ -309,15 +315,20 @@ class SourceProvider { } Future getApp( - AppSource source, String url, Map additionalData, + AppSource source, String url, Map additionalSettings, {App? currentApp, bool trackOnlyOverride = false, noVersionDetectionOverride = false}) async { - var trackOnly = trackOnlyOverride || (currentApp?.trackOnly ?? false); - + if (trackOnlyOverride) { + additionalSettings['trackOnly'] = 'true'; + } + if (noVersionDetectionOverride) { + additionalSettings['noVersionDetection'] = 'true'; + } + var trackOnly = currentApp?.additionalSettings['trackOnly'] == 'true'; String standardUrl = source.standardizeURL(preStandardizeUrl(url)); - APKDetails apk = await source - .getLatestAPKDetails(standardUrl, additionalData, trackOnly: trackOnly); + APKDetails apk = + await source.getLatestAPKDetails(standardUrl, additionalSettings); if (apk.apkUrls.isEmpty && !trackOnly) { throw NoAPKError(); } @@ -327,7 +338,7 @@ class SourceProvider { return App( currentApp?.id ?? source.tryInferringAppId(standardUrl, - additionalData: additionalData) ?? + additionalSettings: additionalSettings) ?? generateTempID(apk.names, source), standardUrl, apk.names.author[0].toUpperCase() + apk.names.author.substring(1), @@ -338,12 +349,9 @@ class SourceProvider { apkVersion, apk.apkUrls, apk.apkUrls.length - 1, - additionalData, + additionalSettings, DateTime.now(), - currentApp?.pinned ?? false, - trackOnly, - noVersionDetection: noVersionDetectionOverride || - (currentApp?.noVersionDetection ?? false)); + currentApp?.pinned ?? false); } // Returns errors in [results, errors] instead of throwing them @@ -358,7 +366,7 @@ class SourceProvider { source, url, getDefaultValuesFromFormItems( - source.additionalSourceAppSpecificFormItems))); + source.combinedAppSpecificSettingFormItems))); } catch (e) { errors.addAll({url: e}); }