From 63034dd3f9b25a9f0d93ea1ed39c3fd9a114e6a6 Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Sun, 18 Dec 2022 02:46:25 -0500 Subject: [PATCH 1/7] Added 'no version detection' option --- lib/pages/add_app.dart | 22 +++++++++++++++- lib/providers/apps_provider.dart | 12 ++++----- lib/providers/source_provider.dart | 40 +++++++++++++++++++----------- 3 files changed, 52 insertions(+), 22 deletions(-) diff --git a/lib/pages/add_app.dart b/lib/pages/add_app.dart index 6dd6b5f..40e1f40 100644 --- a/lib/pages/add_app.dart +++ b/lib/pages/add_app.dart @@ -71,6 +71,11 @@ class _AddAppPageState extends State { otherAdditionalData, 'trackOnlyFormItemKey') == 'true'; + var userPickedNoVersionDetection = findGeneratedFormValueByKey( + pickedSource!.additionalAppSpecificSourceAgnosticFormItems, + otherAdditionalData, + 'noVersionDetectionKey') == + 'true'; var cont = true; if ((userPickedTrackOnly || pickedSource!.enforceTrackOnly) && await showDialog( @@ -91,12 +96,27 @@ class _AddAppPageState extends State { null) { cont = false; } + if (userPickedNoVersionDetection && + await showDialog( + context: context, + builder: (BuildContext ctx) { + return GeneratedFormModal( + title: 'Disable Version Detection', // TODO + items: const [], + defaultValues: const [], + message: 'TODO', + ); + }) == + null) { + cont = false; + } if (cont) { HapticFeedback.selectionClick(); var trackOnly = pickedSource!.enforceTrackOnly || userPickedTrackOnly; App app = await sourceProvider.getApp( pickedSource!, userInput, sourceSpecificAdditionalData, - trackOnly: trackOnly); + trackOnlyOverride: trackOnly, + noVersionDetectionOverride: userPickedNoVersionDetection); if (!trackOnly) { await settingsProvider.getInstallPermission(); } diff --git a/lib/providers/apps_provider.dart b/lib/providers/apps_provider.dart index ec605e2..5a1cf8a 100644 --- a/lib/providers/apps_provider.dart +++ b/lib/providers/apps_provider.dart @@ -461,7 +461,8 @@ class AppsProvider with ChangeNotifier { app.installedVersion = installedInfo!.versionName; modded = true; } else if (installedInfo?.versionName != null && - installedInfo!.versionName != app.installedVersion) { + installedInfo!.versionName != app.installedVersion && + !app.noVersionDetection) { String? correctedInstalledVersion = reconcileRealAndInternalVersions( installedInfo.versionName!, app.installedVersion!); if (correctedInstalledVersion != null) { @@ -470,7 +471,8 @@ class AppsProvider with ChangeNotifier { } } if (app.installedVersion != null && - app.installedVersion != app.latestVersion) { + app.installedVersion != app.latestVersion && + !app.noVersionDetection) { app.installedVersion = reconcileRealAndInternalVersions( app.installedVersion!, app.latestVersion, matchMode: true) ?? @@ -624,11 +626,7 @@ class AppsProvider with ChangeNotifier { sourceProvider.getSource(currentApp.url), currentApp.url, currentApp.additionalData, - name: currentApp.name, - id: currentApp.id, - pinned: currentApp.pinned, - trackOnly: currentApp.trackOnly, - installedVersion: currentApp.installedVersion); + 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 a623477..28a203f 100644 --- a/lib/providers/source_provider.dart +++ b/lib/providers/source_provider.dart @@ -48,6 +48,7 @@ class App { late DateTime? lastUpdateCheck; bool pinned = false; bool trackOnly = false; + bool noVersionDetection = false; App( this.id, this.url, @@ -60,7 +61,8 @@ class App { this.additionalData, this.lastUpdateCheck, this.pinned, - this.trackOnly); + this.trackOnly, + {this.noVersionDetection = false}); @override String toString() { @@ -89,7 +91,8 @@ class App { ? null : DateTime.fromMicrosecondsSinceEpoch(json['lastUpdateCheck']), json['pinned'] ?? false, - json['trackOnly'] ?? false); + json['trackOnly'] ?? false, + noVersionDetection: json['noVersionDetection'] ?? false); Map toJson() => { 'id': id, @@ -103,7 +106,8 @@ class App { 'additionalData': jsonEncode(additionalData), 'lastUpdateCheck': lastUpdateCheck?.microsecondsSinceEpoch, 'pinned': pinned, - 'trackOnly': trackOnly + 'trackOnly': trackOnly, + 'noVersionDetection': noVersionDetection }; } @@ -165,9 +169,13 @@ class AppSource { GeneratedFormItem( label: tr('trackOnly'), type: FormItemType.bool, - key: 'trackOnlyFormItemKey') + key: 'trackOnlyFormItemKey'), + GeneratedFormItem( + label: 'Do not attempt version detection', // TODO + type: FormItemType.bool, + key: 'noVersionDetectionKey') ]; - final List additionalAppSpecificSourceAgnosticDefaults = ['']; + final List additionalAppSpecificSourceAgnosticDefaults = ['', '']; // Some Sources may have additional settings at the Source level (not specific to Apps) - these use SettingsProvider List additionalSourceSpecificSettingFormItems = []; @@ -275,11 +283,11 @@ class SourceProvider { } Future getApp(AppSource source, String url, List additionalData, - {String name = '', - String? id, - bool pinned = false, - bool trackOnly = false, - String? installedVersion}) async { + {App? currentApp, + bool trackOnlyOverride = false, + noVersionDetectionOverride = false}) async { + var trackOnly = trackOnlyOverride || (currentApp?.trackOnly ?? false); + String standardUrl = source.standardizeURL(preStandardizeUrl(url)); APKDetails apk = await source .getLatestAPKDetails(standardUrl, additionalData, trackOnly: trackOnly); @@ -287,8 +295,10 @@ class SourceProvider { throw NoAPKError(); } String apkVersion = apk.version.replaceAll('/', '-'); + var name = currentApp?.name.trim() ?? + apk.names.name[0].toUpperCase() + apk.names.name.substring(1); return App( - id ?? + currentApp?.id ?? source.tryInferringAppId(standardUrl, additionalData: additionalData) ?? generateTempID(apk.names, source), @@ -297,14 +307,16 @@ class SourceProvider { name.trim().isNotEmpty ? name : apk.names.name[0].toUpperCase() + apk.names.name.substring(1), - installedVersion, + currentApp?.installedVersion, apkVersion, apk.apkUrls, apk.apkUrls.length - 1, additionalData, DateTime.now(), - pinned, - trackOnly); + currentApp?.pinned ?? false, + trackOnly, + noVersionDetection: noVersionDetectionOverride || + (currentApp?.noVersionDetection ?? false)); } // Returns errors in [results, errors] instead of throwing them From 1fe9e4f91e2bc9eed3e3c559ab14bdfdbc278b93 Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Mon, 19 Dec 2022 19:34:43 -0500 Subject: [PATCH 2/7] Started switching additionaldata to map --- lib/app_sources/apkmirror.dart | 2 +- lib/app_sources/fdroid.dart | 4 +- lib/app_sources/fdroidrepo.dart | 13 ++-- lib/app_sources/github.dart | 33 ++++----- lib/app_sources/gitlab.dart | 2 +- lib/app_sources/izzyondroid.dart | 4 +- lib/app_sources/mullvad.dart | 2 +- lib/app_sources/signal.dart | 2 +- lib/app_sources/sourceforge.dart | 2 +- lib/app_sources/steammobile.dart | 15 ++--- lib/components/generated_form.dart | 64 ++++++------------ lib/components/generated_form_modal.dart | 8 ++- lib/main.dart | 2 +- lib/pages/add_app.dart | 45 ++++++------- lib/pages/app.dart | 2 +- lib/pages/apps.dart | 85 +++++++++++------------- lib/pages/import_export.dart | 12 ++-- lib/pages/settings.dart | 16 ++--- lib/providers/source_provider.dart | 30 +++++---- 19 files changed, 155 insertions(+), 188 deletions(-) diff --git a/lib/app_sources/apkmirror.dart b/lib/app_sources/apkmirror.dart index 1372d16..c0efb87 100644 --- a/lib/app_sources/apkmirror.dart +++ b/lib/app_sources/apkmirror.dart @@ -25,7 +25,7 @@ class APKMirror extends AppSource { @override Future getLatestAPKDetails( - String standardUrl, List additionalData, + String standardUrl, Map additionalData, {bool trackOnly = false}) async { Response res = await get(Uri.parse('$standardUrl/feed')); if (res.statusCode == 200) { diff --git a/lib/app_sources/fdroid.dart b/lib/app_sources/fdroid.dart index 8b7af89..fc4f4d1 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, - {List additionalData = const []}) { + {Map additionalData = const {}}) { return Uri.parse(standardUrl).pathSegments.last; } @@ -60,7 +60,7 @@ class FDroid extends AppSource { @override Future getLatestAPKDetails( - String standardUrl, List additionalData, + String standardUrl, Map additionalData, {bool trackOnly = false}) async { String? appId = tryInferringAppId(standardUrl); return getAPKUrlsFromFDroidPackagesAPIResponse( diff --git a/lib/app_sources/fdroidrepo.dart b/lib/app_sources/fdroidrepo.dart index c13a51d..7b537ef 100644 --- a/lib/app_sources/fdroidrepo.dart +++ b/lib/app_sources/fdroidrepo.dart @@ -11,11 +11,10 @@ class FDroidRepo extends AppSource { additionalSourceAppSpecificFormItems = [ [ - GeneratedFormItem( + GeneratedFormItem('appIdOrName', label: tr('appIdOrName'), hint: tr('reposHaveMultipleApps'), - required: true, - key: 'appIdOrName') + required: true) ] ]; } @@ -33,13 +32,9 @@ class FDroidRepo extends AppSource { @override Future getLatestAPKDetails( - String standardUrl, List additionalData, + String standardUrl, Map additionalData, {bool trackOnly = false}) async { - String? appIdOrName = findGeneratedFormValueByKey( - additionalSourceAppSpecificFormItems - .reduce((value, element) => [...value, ...element]), - additionalData, - 'appIdOrName'); + String? appIdOrName = additionalData['appIdOrName']; if (appIdOrName == null) { throw NoReleasesError(); } diff --git a/lib/app_sources/github.dart b/lib/app_sources/github.dart index 3fdbf4a..ed8f007 100644 --- a/lib/app_sources/github.dart +++ b/lib/app_sources/github.dart @@ -12,12 +12,9 @@ class GitHub extends AppSource { GitHub() { host = 'github.com'; - additionalSourceAppSpecificDefaults = ['true', 'true', '']; - additionalSourceSpecificSettingFormItems = [ - GeneratedFormItem( + GeneratedFormItem('github-creds', label: tr('githubPATLabel'), - id: 'github-creds', required: false, additionalValidators: [ (value) { @@ -52,17 +49,23 @@ class GitHub extends AppSource { ]) ]; + additionalSourceAppSpecificDefaults = { + 'includePrereleases': 'true', + 'fallbackToOlderReleases': 'true', + 'filterReleaseTitlesByRegEx': '' + }; + additionalSourceAppSpecificFormItems = [ [ - GeneratedFormItem( + GeneratedFormItem('includePrereleases', label: tr('includePrereleases'), type: FormItemType.bool) ], [ - GeneratedFormItem( + GeneratedFormItem('fallbackToOlderReleases', label: tr('fallbackToOlderReleases'), type: FormItemType.bool) ], [ - GeneratedFormItem( + GeneratedFormItem('filterReleaseTitlesByRegEx', label: tr('filterReleaseTitlesByRegEx'), type: FormItemType.string, required: false, @@ -99,7 +102,7 @@ class GitHub extends AppSource { SettingsProvider settingsProvider = SettingsProvider(); await settingsProvider.initializeSettings(); String? creds = settingsProvider - .getSettingString(additionalSourceSpecificSettingFormItems[0].id); + .getSettingString(additionalSourceSpecificSettingFormItems[0].key); return creds != null && creds.isNotEmpty ? '$creds@' : ''; } @@ -109,15 +112,15 @@ class GitHub extends AppSource { @override Future getLatestAPKDetails( - String standardUrl, List additionalData, + String standardUrl, Map additionalData, {bool trackOnly = false}) async { - var includePrereleases = - additionalData.isNotEmpty && additionalData[0] == 'true'; + var includePrereleases = additionalData['includePrereleases'] == 'true'; var fallbackToOlderReleases = - additionalData.length >= 2 && additionalData[1] == 'true'; - var regexFilter = additionalData.length >= 3 && additionalData[2].isNotEmpty - ? additionalData[2] - : null; + additionalData['fallbackToOlderReleases'] == 'true'; + var regexFilter = + additionalData['filterReleaseTitlesByRegEx']?.isNotEmpty == true + ? additionalData['filterReleaseTitlesByRegEx'] + : null; Response res = await get(Uri.parse( 'https://${await getCredentialPrefixIfAny()}api.$host/repos${standardUrl.substring('https://$host'.length)}/releases')); if (res.statusCode == 200) { diff --git a/lib/app_sources/gitlab.dart b/lib/app_sources/gitlab.dart index 6c2f5ab..efaff00 100644 --- a/lib/app_sources/gitlab.dart +++ b/lib/app_sources/gitlab.dart @@ -25,7 +25,7 @@ class GitLab extends AppSource { @override Future getLatestAPKDetails( - String standardUrl, List additionalData, + String standardUrl, Map additionalData, {bool trackOnly = false}) async { Response res = await get(Uri.parse('$standardUrl/-/tags?format=atom')); if (res.statusCode == 200) { diff --git a/lib/app_sources/izzyondroid.dart b/lib/app_sources/izzyondroid.dart index 13ea4bd..d2b08f1 100644 --- a/lib/app_sources/izzyondroid.dart +++ b/lib/app_sources/izzyondroid.dart @@ -23,13 +23,13 @@ class IzzyOnDroid extends AppSource { @override String? tryInferringAppId(String standardUrl, - {List additionalData = const []}) { + {Map additionalData = const {}}) { return FDroid().tryInferringAppId(standardUrl); } @override Future getLatestAPKDetails( - String standardUrl, List additionalData, + String standardUrl, Map additionalData, {bool trackOnly = false}) async { String? appId = tryInferringAppId(standardUrl); return FDroid().getAPKUrlsFromFDroidPackagesAPIResponse( diff --git a/lib/app_sources/mullvad.dart b/lib/app_sources/mullvad.dart index 353e460..f19ab6a 100644 --- a/lib/app_sources/mullvad.dart +++ b/lib/app_sources/mullvad.dart @@ -24,7 +24,7 @@ class Mullvad extends AppSource { @override Future getLatestAPKDetails( - String standardUrl, List additionalData, + String standardUrl, Map additionalData, {bool trackOnly = false}) async { Response res = await get(Uri.parse('$standardUrl/en/download/android')); if (res.statusCode == 200) { diff --git a/lib/app_sources/signal.dart b/lib/app_sources/signal.dart index 517a7df..dbf88fb 100644 --- a/lib/app_sources/signal.dart +++ b/lib/app_sources/signal.dart @@ -18,7 +18,7 @@ class Signal extends AppSource { @override Future getLatestAPKDetails( - String standardUrl, List additionalData, + String standardUrl, Map additionalData, {bool trackOnly = false}) async { Response res = await get(Uri.parse('https://updates.$host/android/latest.json')); diff --git a/lib/app_sources/sourceforge.dart b/lib/app_sources/sourceforge.dart index 6961ea8..8ad3e48 100644 --- a/lib/app_sources/sourceforge.dart +++ b/lib/app_sources/sourceforge.dart @@ -23,7 +23,7 @@ class SourceForge extends AppSource { @override Future getLatestAPKDetails( - String standardUrl, List additionalData, + String standardUrl, Map additionalData, {bool trackOnly = false}) async { Response res = await get(Uri.parse('$standardUrl/rss?path=/')); if (res.statusCode == 200) { diff --git a/lib/app_sources/steammobile.dart b/lib/app_sources/steammobile.dart index f202bd8..db9ec94 100644 --- a/lib/app_sources/steammobile.dart +++ b/lib/app_sources/steammobile.dart @@ -11,11 +11,8 @@ class SteamMobile extends AppSource { name = tr('steam'); additionalSourceAppSpecificFormItems = [ [ - GeneratedFormItem( - label: tr('app'), - key: 'app', - required: true, - opts: apks.entries.toList()) + GeneratedFormItem('app', + label: tr('app'), required: true, opts: apks.entries.toList()) ] ]; } @@ -32,15 +29,11 @@ class SteamMobile extends AppSource { @override Future getLatestAPKDetails( - String standardUrl, List additionalData, + String standardUrl, Map additionalData, {bool trackOnly = false}) async { Response res = await get(Uri.parse('https://$host/mobile')); if (res.statusCode == 200) { - var apkNamePrefix = findGeneratedFormValueByKey( - additionalSourceAppSpecificFormItems - .reduce((value, element) => [...value, ...element]), - additionalData, - 'app'); + var apkNamePrefix = additionalData['app']; if (apkNamePrefix == null) { throw NoReleasesError(); } diff --git a/lib/components/generated_form.dart b/lib/components/generated_form.dart index c0ae67f..9bf76ed 100644 --- a/lib/components/generated_form.dart +++ b/lib/components/generated_form.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; enum FormItemType { string, bool } typedef OnValueChanges = void Function( - List values, bool valid, bool isBuilding); + Map values, bool valid, bool isBuilding); class GeneratedFormItem { late String key; @@ -13,22 +13,19 @@ class GeneratedFormItem { late bool required; late int max; late List additionalValidators; - late String id; late List belowWidgets; late String? hint; late List>? opts; - GeneratedFormItem( + GeneratedFormItem(this.key, {this.label = 'Input', this.type = FormItemType.string, this.required = true, this.max = 1, this.additionalValidators = const [], - this.id = 'input', this.belowWidgets = const [], this.hint, - this.opts, - this.key = 'default'}) { + this.opts}) { if (type != FormItemType.string) { required = false; } @@ -44,7 +41,7 @@ class GeneratedForm extends StatefulWidget { final List> items; final OnValueChanges onValueChanges; - final List defaultValues; + final Map defaultValues; @override State createState() => _GeneratedFormState(); @@ -52,17 +49,18 @@ class GeneratedForm extends StatefulWidget { class _GeneratedFormState extends State { final _formKey = GlobalKey(); - late List> values; + Map values = {}; late List> formInputs; List> rows = []; // If any value changes, call this to update the parent with value and validity void someValueChanged({bool isBuilding = false}) { - List returnValues = []; + Map returnValues = {}; var valid = true; - for (int r = 0; r < values.length; r++) { - for (int i = 0; i < values[r].length; i++) { - returnValues.add(values[r][i]); + for (int r = 0; r < widget.items.length; r++) { + for (int i = 0; i < widget.items[r].length; i++) { + returnValues[widget.items[r][i].key] = + values[widget.items[r][i].key] ?? ''; if (formInputs[r][i] is TextFormField) { valid = valid && ((formInputs[r][i].key as GlobalKey) @@ -80,16 +78,13 @@ class _GeneratedFormState extends State { super.initState(); // Initialize form values as all empty + values.clear(); int j = 0; - values = widget.items - .map((row) => row.map((e) { - return j < widget.defaultValues.length - ? widget.defaultValues[j++] - : e.opts != null - ? e.opts!.first.key - : ''; - }).toList()) - .toList(); + for (var row in widget.items) { + for (var e in row) { + values[e.key] = widget.defaultValues[e.key] ?? e.opts?.first.key ?? ''; + } + } // Dynamically create form inputs formInputs = widget.items.asMap().entries.map((row) { @@ -98,11 +93,11 @@ class _GeneratedFormState extends State { final formFieldKey = GlobalKey(); return TextFormField( key: formFieldKey, - initialValue: values[row.key][e.key], + initialValue: values[e.value.key], autovalidateMode: AutovalidateMode.onUserInteraction, onChanged: (value) { setState(() { - values[row.key][e.key] = value; + values[e.value.key] = value; someValueChanged(); }); }, @@ -131,14 +126,14 @@ class _GeneratedFormState extends State { } return DropdownButtonFormField( decoration: InputDecoration(labelText: e.value.label), - value: values[row.key][e.key], + value: values[e.value.key], items: e.value.opts! .map((e) => DropdownMenuItem(value: e.key, child: Text(e.value))) .toList(), onChanged: (value) { setState(() { - values[row.key][e.key] = value ?? e.value.opts!.first.key; + values[e.value.key] = value ?? e.value.opts!.first.key; someValueChanged(); }); }); @@ -160,10 +155,10 @@ class _GeneratedFormState extends State { children: [ Text(widget.items[r][e].label), Switch( - value: values[r][e] == 'true', + value: values[widget.items[r][e].key] == 'true', onChanged: (value) { setState(() { - values[r][e] = value ? 'true' : ''; + values[widget.items[r][e].key] = value ? 'true' : ''; someValueChanged(); }); }) @@ -217,18 +212,3 @@ class _GeneratedFormState extends State { )); } } - -String? findGeneratedFormValueByKey( - List items, List values, String key) { - var foundIndex = -1; - for (var i = 0; i < items.length; i++) { - if (items[i].key == key) { - foundIndex = i; - break; - } - } - if (foundIndex >= 0 && foundIndex < values.length) { - return values[foundIndex]; - } - return null; -} diff --git a/lib/components/generated_form_modal.dart b/lib/components/generated_form_modal.dart index 5ea70ff..66afdd8 100644 --- a/lib/components/generated_form_modal.dart +++ b/lib/components/generated_form_modal.dart @@ -15,7 +15,7 @@ class GeneratedFormModal extends StatefulWidget { final String title; final String message; final List> items; - final List defaultValues; + final Map defaultValues; final bool initValid; @override @@ -23,13 +23,15 @@ class GeneratedFormModal extends StatefulWidget { } class _GeneratedFormModalState extends State { - List values = []; + Map values = {}; bool valid = false; @override void initState() { super.initState(); - values = widget.defaultValues; + widget.defaultValues.forEach((key, value) { + values[key] = value; + }); valid = widget.initValid || widget.items.isEmpty; } diff --git a/lib/main.dart b/lib/main.dart index 2f614d9..0e17272 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -200,7 +200,7 @@ class _ObtainiumState extends State { currentReleaseTag, [], 0, - ['true'], + {}, null, false, false) diff --git a/lib/pages/add_app.dart b/lib/pages/add_app.dart index 40e1f40..a3659d4 100644 --- a/lib/pages/add_app.dart +++ b/lib/pages/add_app.dart @@ -27,9 +27,9 @@ class _AddAppPageState extends State { String userInput = ''; String searchQuery = ''; AppSource? pickedSource; - List sourceSpecificAdditionalData = []; + Map sourceSpecificAdditionalData = {}; bool sourceSpecificDataIsValid = true; - List otherAdditionalData = []; + Map otherAdditionalData = {}; bool otherAdditionalDataIsValid = true; @override @@ -44,7 +44,7 @@ class _AddAppPageState extends State { if (pickedSource.runtimeType != source.runtimeType) { pickedSource = source; sourceSpecificAdditionalData = - source != null ? source.additionalSourceAppSpecificDefaults : []; + source != null ? source.additionalSourceAppSpecificDefaults : {}; sourceSpecificDataIsValid = source != null ? !sourceProvider.ifSourceAppsRequireAdditionalData(source) : true; @@ -66,16 +66,10 @@ class _AddAppPageState extends State { }); var settingsProvider = context.read(); () async { - var userPickedTrackOnly = findGeneratedFormValueByKey( - pickedSource!.additionalAppSpecificSourceAgnosticFormItems, - otherAdditionalData, - 'trackOnlyFormItemKey') == - 'true'; - var userPickedNoVersionDetection = findGeneratedFormValueByKey( - pickedSource!.additionalAppSpecificSourceAgnosticFormItems, - otherAdditionalData, - 'noVersionDetectionKey') == - 'true'; + var userPickedTrackOnly = + otherAdditionalData['trackOnlyFormItemKey'] == 'true'; + var userPickedNoVersionDetection = + otherAdditionalData['noVersionDetectionKey'] == 'true'; var cont = true; if ((userPickedTrackOnly || pickedSource!.enforceTrackOnly) && await showDialog( @@ -88,7 +82,7 @@ class _AddAppPageState extends State { : tr('app') ]), items: const [], - defaultValues: const [], + defaultValues: const {}, message: '${pickedSource!.enforceTrackOnly ? tr('appsFromSourceAreTrackOnly') : tr('youPickedTrackOnly')}\n\n${tr('trackOnlyAppDescription')}', ); @@ -100,10 +94,10 @@ class _AddAppPageState extends State { await showDialog( context: context, builder: (BuildContext ctx) { - return GeneratedFormModal( + return const GeneratedFormModal( title: 'Disable Version Detection', // TODO - items: const [], - defaultValues: const [], + items: [], + defaultValues: {}, message: 'TODO', ); }) == @@ -177,7 +171,7 @@ class _AddAppPageState extends State { child: GeneratedForm( items: [ [ - GeneratedFormItem( + GeneratedFormItem('appSourceURL', label: tr('appSourceURL'), additionalValidators: [ (value) { @@ -200,10 +194,10 @@ class _AddAppPageState extends State { ] ], onValueChanges: (values, valid, isBuilding) { - changeUserInput( - values[0], valid, isBuilding); + changeUserInput(values['appSourceURL']!, + valid, isBuilding); }, - defaultValues: const [])), + defaultValues: const {'appSourceURL': ''})), const SizedBox( width: 16, ), @@ -244,7 +238,7 @@ class _AddAppPageState extends State { child: GeneratedForm( items: [ [ - GeneratedFormItem( + GeneratedFormItem('searchSomeSources', label: tr('searchSomeSourcesLabel'), required: false), ] @@ -252,11 +246,14 @@ class _AddAppPageState extends State { onValueChanges: (values, valid, isBuilding) { if (values.isNotEmpty && valid) { setState(() { - searchQuery = values[0].trim(); + searchQuery = + values['searchSomeSources']!.trim(); }); } }, - defaultValues: const ['']), + defaultValues: const { + 'searchSomeSources': '' + }), ), const SizedBox( width: 16, diff --git a/lib/pages/app.dart b/lib/pages/app.dart index ed8415d..f5581c2 100644 --- a/lib/pages/app.dart +++ b/lib/pages/app.dart @@ -208,7 +208,7 @@ class _AppPageState extends State { onPressed: app?.downloadProgress != null ? null : () { - showDialog>( + showDialog>( context: context, builder: (BuildContext ctx) { return GeneratedFormModal( diff --git a/lib/pages/apps.dart b/lib/pages/apps.dart index 4c34dd8..ba99ca5 100644 --- a/lib/pages/apps.dart +++ b/lib/pages/apps.dart @@ -349,7 +349,7 @@ class AppsPageState extends State { return GeneratedFormModal( title: tr('removeSelectedAppsQuestion'), items: const [], - defaultValues: const [], + defaultValues: const {}, initValid: true, message: tr( 'xWillBeRemovedButRemainInstalled', @@ -377,40 +377,37 @@ class AppsPageState extends State { : () { HapticFeedback.heavyImpact(); List formInputs = []; - List defaultValues = []; + Map defaultValues = {}; if (existingUpdateIdsAllOrSelected.isNotEmpty) { - formInputs.add(GeneratedFormItem( + formInputs.add(GeneratedFormItem('updates', label: tr('updateX', args: [ plural('apps', existingUpdateIdsAllOrSelected.length) ]), - type: FormItemType.bool, - key: 'updates')); - defaultValues.add('true'); + type: FormItemType.bool)); + defaultValues['updates'] = 'true'; } if (newInstallIdsAllOrSelected.isNotEmpty) { - formInputs.add(GeneratedFormItem( + formInputs.add(GeneratedFormItem('installs', label: tr('installX', args: [ plural('apps', newInstallIdsAllOrSelected.length) ]), - type: FormItemType.bool, - key: 'installs')); - defaultValues - .add(defaultValues.isEmpty ? 'true' : ''); + type: FormItemType.bool)); + defaultValues['installs'] = + defaultValues.isEmpty ? 'true' : ''; } if (trackOnlyUpdateIdsAllOrSelected.isNotEmpty) { - formInputs.add(GeneratedFormItem( + formInputs.add(GeneratedFormItem('trackonlies', label: tr('markXTrackOnlyAsUpdated', args: [ plural('apps', trackOnlyUpdateIdsAllOrSelected.length) ]), - type: FormItemType.bool, - key: 'trackonlies')); - defaultValues - .add(defaultValues.isEmpty ? 'true' : ''); + type: FormItemType.bool)); + defaultValues['trackonlies'] = + defaultValues.isEmpty ? 'true' : ''; } - showDialog?>( + showDialog?>( context: context, builder: (BuildContext ctx) { var totalApps = existingUpdateIdsAllOrSelected @@ -430,17 +427,11 @@ class AppsPageState extends State { values = defaultValues; } bool shouldInstallUpdates = - findGeneratedFormValueByKey( - formInputs, values, 'updates') == - 'true'; + values['updates'] == 'true'; bool shouldInstallNew = - findGeneratedFormValueByKey( - formInputs, values, 'installs') == - 'true'; + values['installs'] == 'true'; bool shouldMarkTrackOnlies = - findGeneratedFormValueByKey(formInputs, - values, 'trackonlies') == - 'true'; + values['trackonlies'] == 'true'; (() async { if (shouldInstallNew || shouldInstallUpdates) { @@ -613,7 +604,7 @@ class AppsPageState extends State { title: tr( 'resetInstallStatusForSelectedAppsQuestion'), items: const [], - defaultValues: const [], + defaultValues: const {}, initValid: true, message: tr( 'installStatusOfXWillBeResetExplanation', @@ -683,36 +674,36 @@ class AppsPageState extends State { : FontWeight.bold), ), onPressed: () { - showDialog?>( + showDialog?>( context: context, builder: (BuildContext ctx) { return GeneratedFormModal( title: tr('filterApps'), items: [ [ - GeneratedFormItem( + GeneratedFormItem('appName', label: tr('appName'), required: false), - GeneratedFormItem( + GeneratedFormItem('author', label: tr('author'), required: false) ], [ - GeneratedFormItem( + GeneratedFormItem('upToDateApps', label: tr('upToDateApps'), type: FormItemType.bool) ], [ - GeneratedFormItem( + GeneratedFormItem('nonInstalledApps', label: tr('nonInstalledApps'), type: FormItemType.bool) ] ], defaultValues: filter == null - ? AppsFilter().toValuesArray() - : filter!.toValuesArray()); + ? AppsFilter().toValuesMap() + : filter!.toValuesMap()); }).then((values) { if (values != null) { setState(() { - filter = AppsFilter.fromValuesArray(values); + filter = AppsFilter.fromValuesMap(values); if (AppsFilter().isIdenticalTo(filter!)) { filter = null; } @@ -740,20 +731,20 @@ class AppsFilter { this.includeUptodate = true, this.includeNonInstalled = true}); - List toValuesArray() { - return [ - nameFilter, - authorFilter, - includeUptodate ? 'true' : '', - includeNonInstalled ? 'true' : '' - ]; + Map toValuesMap() { + return { + 'appName': nameFilter, + 'author': authorFilter, + 'upToDateApps': includeUptodate ? 'true' : '', + 'nonInstalledApps': includeNonInstalled ? 'true' : '' + }; } - AppsFilter.fromValuesArray(List values) { - nameFilter = values[0]; - authorFilter = values[1]; - includeUptodate = values[2] == 'true'; - includeNonInstalled = values[3] == 'true'; + AppsFilter.fromValuesMap(Map values) { + nameFilter = values['appName']!; + authorFilter = values['author']!; + includeUptodate = values['upToDateApps'] == 'true'; + includeNonInstalled = values['nonInstalledApps'] == 'true'; } bool isIdenticalTo(AppsFilter other) => diff --git a/lib/pages/import_export.dart b/lib/pages/import_export.dart index 889969b..d6aede6 100644 --- a/lib/pages/import_export.dart +++ b/lib/pages/import_export.dart @@ -145,7 +145,7 @@ class _ImportExportPageState extends State { title: tr('importFromURLList'), items: [ [ - GeneratedFormItem( + GeneratedFormItem('appURLList', label: tr('appURLList'), max: 7, additionalValidators: [ @@ -172,7 +172,7 @@ class _ImportExportPageState extends State { ]) ] ], - defaultValues: const [], + defaultValues: const {}, ); }).then((values) { if (values != null) { @@ -237,11 +237,12 @@ class _ImportExportPageState extends State { items: [ [ GeneratedFormItem( + 'searchQuery', label: tr( 'searchQuery')) ] ], - defaultValues: const [], + defaultValues: const {}, ); }); if (values != null && @@ -346,10 +347,11 @@ class _ImportExportPageState extends State { .requiredArgs .map( (e) => [ - GeneratedFormItem(label: e) + GeneratedFormItem(e, + label: e) ]) .toList(), - defaultValues: const [], + defaultValues: const {}, ); }); if (values != null) { diff --git a/lib/pages/settings.dart b/lib/pages/settings.dart index 8f8befb..cffb56c 100644 --- a/lib/pages/settings.dart +++ b/lib/pages/settings.dart @@ -143,16 +143,16 @@ class _SettingsPageState extends State { .toList(), onValueChanges: (values, valid, isBuilding) { if (valid) { - for (var i = 0; i < values.length; i++) { - settingsProvider.setSettingString( - e.additionalSourceSpecificSettingFormItems[i].id, - values[i]); - } + values.forEach((key, value) { + settingsProvider.setSettingString(key, value); + }); } }, - defaultValues: e.additionalSourceSpecificSettingFormItems.map((e) { - return settingsProvider.getSettingString(e.id) ?? ''; - }).toList()); + defaultValues: Map.fromEntries( + e.additionalSourceSpecificSettingFormItems.map((e) { + return MapEntry( + e.key, settingsProvider.getSettingString(e.key) ?? ''); + }))); } else { return Container(); } diff --git a/lib/providers/source_provider.dart b/lib/providers/source_provider.dart index 28a203f..f6be78e 100644 --- a/lib/providers/source_provider.dart +++ b/lib/providers/source_provider.dart @@ -44,7 +44,7 @@ class App { late String latestVersion; List apkUrls = []; late int preferredApkIndex; - late List additionalData; + late Map additionalData; late DateTime? lastUpdateCheck; bool pinned = false; bool trackOnly = false; @@ -86,7 +86,7 @@ class App { ? SourceProvider() .getSource(json['url']) .additionalSourceAppSpecificDefaults - : List.from(jsonDecode(json['additionalData'])), + : Map.from(jsonDecode(json['additionalData'])), json['lastUpdateCheck'] == null ? null : DateTime.fromMicrosecondsSinceEpoch(json['lastUpdateCheck']), @@ -155,27 +155,30 @@ class AppSource { } Future getLatestAPKDetails( - String standardUrl, List additionalData, + String standardUrl, Map additionalData, {bool trackOnly = false}) { throw NotImplementedError(); } // Different Sources may need different kinds of additional data for Apps List> additionalSourceAppSpecificFormItems = []; - List additionalSourceAppSpecificDefaults = []; + Map additionalSourceAppSpecificDefaults = {}; // Some additional data may be needed for Apps regardless of Source final List additionalAppSpecificSourceAgnosticFormItems = [ GeneratedFormItem( - label: tr('trackOnly'), - type: FormItemType.bool, - key: 'trackOnlyFormItemKey'), - GeneratedFormItem( + 'trackOnlyFormItemKey', + label: tr('trackOnly'), + type: FormItemType.bool, + ), + GeneratedFormItem('noVersionDetectionKey', label: 'Do not attempt version detection', // TODO - type: FormItemType.bool, - key: 'noVersionDetectionKey') + type: FormItemType.bool) ]; - final List additionalAppSpecificSourceAgnosticDefaults = ['', '']; + final Map additionalAppSpecificSourceAgnosticDefaults = { + 'trackOnlyFormItemKey': '', + 'noVersionDetectionKey': '' + }; // Some Sources may have additional settings at the Source level (not specific to Apps) - these use SettingsProvider List additionalSourceSpecificSettingFormItems = []; @@ -194,7 +197,7 @@ class AppSource { } String? tryInferringAppId(String standardUrl, - {List additionalData = const []}) { + {Map additionalData = const {}}) { return null; } } @@ -282,7 +285,8 @@ class SourceProvider { return true; } - Future getApp(AppSource source, String url, List additionalData, + Future getApp( + AppSource source, String url, Map additionalData, {App? currentApp, bool trackOnlyOverride = false, noVersionDetectionOverride = false}) async { From afc8e4117115c023ec7e2c3c37715634e87639fe Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Mon, 19 Dec 2022 19:48:37 -0500 Subject: [PATCH 3/7] Made defaultvallue part of formitem --- lib/app_sources/github.dart | 14 ++--- lib/components/generated_form.dart | 12 ++-- lib/components/generated_form_modal.dart | 3 +- lib/pages/add_app.dart | 72 +++++++++++------------- lib/pages/app.dart | 5 +- lib/pages/settings.dart | 7 +-- lib/providers/source_provider.dart | 20 ++++--- 7 files changed, 61 insertions(+), 72 deletions(-) diff --git a/lib/app_sources/github.dart b/lib/app_sources/github.dart index ed8f007..a77a2fa 100644 --- a/lib/app_sources/github.dart +++ b/lib/app_sources/github.dart @@ -49,20 +49,18 @@ class GitHub extends AppSource { ]) ]; - additionalSourceAppSpecificDefaults = { - 'includePrereleases': 'true', - 'fallbackToOlderReleases': 'true', - 'filterReleaseTitlesByRegEx': '' - }; - additionalSourceAppSpecificFormItems = [ [ GeneratedFormItem('includePrereleases', - label: tr('includePrereleases'), type: FormItemType.bool) + label: tr('includePrereleases'), + type: FormItemType.bool, + defaultValue: 'true') ], [ GeneratedFormItem('fallbackToOlderReleases', - label: tr('fallbackToOlderReleases'), type: FormItemType.bool) + label: tr('fallbackToOlderReleases'), + type: FormItemType.bool, + defaultValue: 'true') ], [ GeneratedFormItem('filterReleaseTitlesByRegEx', diff --git a/lib/components/generated_form.dart b/lib/components/generated_form.dart index 9bf76ed..d927371 100644 --- a/lib/components/generated_form.dart +++ b/lib/components/generated_form.dart @@ -16,6 +16,7 @@ class GeneratedFormItem { late List belowWidgets; late String? hint; late List>? opts; + late String? defaultValue; GeneratedFormItem(this.key, {this.label = 'Input', @@ -25,7 +26,8 @@ class GeneratedFormItem { this.additionalValidators = const [], this.belowWidgets = const [], this.hint, - this.opts}) { + this.opts, + this.defaultValue}) { if (type != FormItemType.string) { required = false; } @@ -34,14 +36,10 @@ class GeneratedFormItem { class GeneratedForm extends StatefulWidget { const GeneratedForm( - {super.key, - required this.items, - required this.onValueChanges, - required this.defaultValues}); + {super.key, required this.items, required this.onValueChanges}); final List> items; final OnValueChanges onValueChanges; - final Map defaultValues; @override State createState() => _GeneratedFormState(); @@ -82,7 +80,7 @@ class _GeneratedFormState extends State { int j = 0; for (var row in widget.items) { for (var e in row) { - values[e.key] = widget.defaultValues[e.key] ?? e.opts?.first.key ?? ''; + values[e.key] = e.defaultValue ?? e.opts?.first.key ?? ''; } } diff --git a/lib/components/generated_form_modal.dart b/lib/components/generated_form_modal.dart index 66afdd8..772eb59 100644 --- a/lib/components/generated_form_modal.dart +++ b/lib/components/generated_form_modal.dart @@ -59,8 +59,7 @@ class _GeneratedFormModalState extends State { this.valid = valid; }); } - }, - defaultValues: widget.defaultValues) + }) ]), actions: [ TextButton( diff --git a/lib/pages/add_app.dart b/lib/pages/add_app.dart index a3659d4..4e2809e 100644 --- a/lib/pages/add_app.dart +++ b/lib/pages/add_app.dart @@ -43,8 +43,10 @@ class _AddAppPageState extends State { var source = valid ? sourceProvider.getSource(userInput) : null; if (pickedSource.runtimeType != source.runtimeType) { pickedSource = source; - sourceSpecificAdditionalData = - source != null ? source.additionalSourceAppSpecificDefaults : {}; + sourceSpecificAdditionalData = source != null + ? getDefaultValuesFromFormItems( + source.additionalSourceAppSpecificFormItems) + : {}; sourceSpecificDataIsValid = source != null ? !sourceProvider.ifSourceAppsRequireAdditionalData(source) : true; @@ -170,34 +172,33 @@ class _AddAppPageState extends State { Expanded( child: GeneratedForm( items: [ - [ - GeneratedFormItem('appSourceURL', - label: tr('appSourceURL'), - additionalValidators: [ - (value) { - try { - sourceProvider - .getSource(value ?? '') - .standardizeURL( - preStandardizeUrl( - value ?? '')); - } catch (e) { - return e is String - ? e - : e is ObtainiumError - ? e.toString() - : tr('error'); - } - return null; - } - ]) - ] - ], + [ + GeneratedFormItem('appSourceURL', + label: tr('appSourceURL'), + additionalValidators: [ + (value) { + try { + sourceProvider + .getSource(value ?? '') + .standardizeURL( + preStandardizeUrl( + value ?? '')); + } catch (e) { + return e is String + ? e + : e is ObtainiumError + ? e.toString() + : tr('error'); + } + return null; + } + ]) + ] + ], onValueChanges: (values, valid, isBuilding) { changeUserInput(values['appSourceURL']!, valid, isBuilding); - }, - defaultValues: const {'appSourceURL': ''})), + })), const SizedBox( width: 16, ), @@ -211,7 +212,7 @@ class _AddAppPageState extends State { .isNotEmpty && !sourceSpecificDataIsValid) || (pickedSource! - .additionalAppSpecificSourceAgnosticDefaults + .additionalAppSpecificSourceAgnosticFormItems .isNotEmpty && !otherAdditionalDataIsValid) ? null @@ -250,9 +251,6 @@ class _AddAppPageState extends State { values['searchSomeSources']!.trim(); }); } - }, - defaultValues: const { - 'searchSomeSources': '' }), ), const SizedBox( @@ -309,7 +307,7 @@ class _AddAppPageState extends State { ], ), if (pickedSource != null && - (pickedSource!.additionalSourceAppSpecificDefaults + (pickedSource!.additionalSourceAppSpecificFormItems .isNotEmpty || pickedSource! .additionalAppSpecificSourceAgnosticFormItems @@ -346,11 +344,9 @@ class _AddAppPageState extends State { sourceSpecificDataIsValid = valid; }); } - }, - defaultValues: pickedSource! - .additionalSourceAppSpecificDefaults), + }), if (pickedSource! - .additionalAppSpecificSourceAgnosticDefaults + .additionalAppSpecificSourceAgnosticFormItems .isNotEmpty) const SizedBox( height: 8, @@ -370,9 +366,7 @@ class _AddAppPageState extends State { otherAdditionalDataIsValid = valid; }); } - }, - defaultValues: pickedSource! - .additionalAppSpecificSourceAgnosticDefaults), + }), ], ) else diff --git a/lib/pages/app.dart b/lib/pages/app.dart index f5581c2..3d4aae1 100644 --- a/lib/pages/app.dart +++ b/lib/pages/app.dart @@ -217,8 +217,9 @@ class _AppPageState extends State { .additionalSourceAppSpecificFormItems, defaultValues: app != null ? app.app.additionalData - : source - .additionalSourceAppSpecificDefaults); + : getDefaultValuesFromFormItems( + source + .additionalSourceAppSpecificFormItems)); }).then((values) { if (app != null && values != null) { var changedApp = app.app; diff --git a/lib/pages/settings.dart b/lib/pages/settings.dart index cffb56c..279f6f9 100644 --- a/lib/pages/settings.dart +++ b/lib/pages/settings.dart @@ -147,12 +147,7 @@ class _SettingsPageState extends State { settingsProvider.setSettingString(key, value); }); } - }, - defaultValues: Map.fromEntries( - e.additionalSourceSpecificSettingFormItems.map((e) { - return MapEntry( - e.key, settingsProvider.getSettingString(e.key) ?? ''); - }))); + }); } else { return Container(); } diff --git a/lib/providers/source_provider.dart b/lib/providers/source_provider.dart index f6be78e..8d123ea 100644 --- a/lib/providers/source_provider.dart +++ b/lib/providers/source_provider.dart @@ -83,9 +83,9 @@ class App { : List.from(jsonDecode(json['apkUrls'])), json['preferredApkIndex'] == null ? 0 : json['preferredApkIndex'] as int, json['additionalData'] == null - ? SourceProvider() + ? getDefaultValuesFromFormItems(SourceProvider() .getSource(json['url']) - .additionalSourceAppSpecificDefaults + .additionalSourceAppSpecificFormItems) : Map.from(jsonDecode(json['additionalData'])), json['lastUpdateCheck'] == null ? null @@ -141,6 +141,12 @@ List getLinksFromParsedHTML( .map((e) => '$prependToLinks${e.attributes['href']!}') .toList(); +getDefaultValuesFromFormItems(List> items) { + Map.fromEntries(items + .map((row) => row.map((el) => MapEntry(el.key, el.defaultValue ?? ''))) + .reduce((value, element) => [...value, ...element])); +} + class AppSource { String? host; late String name; @@ -162,7 +168,6 @@ class AppSource { // Different Sources may need different kinds of additional data for Apps List> additionalSourceAppSpecificFormItems = []; - Map additionalSourceAppSpecificDefaults = {}; // Some additional data may be needed for Apps regardless of Source final List additionalAppSpecificSourceAgnosticFormItems = [ @@ -175,10 +180,6 @@ class AppSource { label: 'Do not attempt version detection', // TODO type: FormItemType.bool) ]; - final Map additionalAppSpecificSourceAgnosticDefaults = { - 'trackOnlyFormItemKey': '', - 'noVersionDetectionKey': '' - }; // Some Sources may have additional settings at the Source level (not specific to Apps) - these use SettingsProvider List additionalSourceSpecificSettingFormItems = []; @@ -332,7 +333,10 @@ class SourceProvider { try { var source = getSource(url); apps.add(await getApp( - source, url, source.additionalSourceAppSpecificDefaults)); + source, + url, + getDefaultValuesFromFormItems( + source.additionalSourceAppSpecificFormItems))); } catch (e) { errors.addAll({url: e}); } From 0c2654a22618c1de0f5d302e06a8b3d1cbea4a6d Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Mon, 19 Dec 2022 19:58:12 -0500 Subject: [PATCH 4/7] More fixes to prev commit --- lib/components/generated_form_modal.dart | 5 -- lib/pages/add_app.dart | 2 - lib/pages/app.dart | 7 +-- lib/pages/apps.dart | 60 ++++++++++++++---------- lib/pages/import_export.dart | 3 -- lib/providers/source_provider.dart | 5 +- 6 files changed, 38 insertions(+), 44 deletions(-) diff --git a/lib/components/generated_form_modal.dart b/lib/components/generated_form_modal.dart index 772eb59..2a24ae1 100644 --- a/lib/components/generated_form_modal.dart +++ b/lib/components/generated_form_modal.dart @@ -8,14 +8,12 @@ class GeneratedFormModal extends StatefulWidget { {super.key, required this.title, required this.items, - required this.defaultValues, this.initValid = false, this.message = ''}); final String title; final String message; final List> items; - final Map defaultValues; final bool initValid; @override @@ -29,9 +27,6 @@ class _GeneratedFormModalState extends State { @override void initState() { super.initState(); - widget.defaultValues.forEach((key, value) { - values[key] = value; - }); valid = widget.initValid || widget.items.isEmpty; } diff --git a/lib/pages/add_app.dart b/lib/pages/add_app.dart index 4e2809e..f2a67cf 100644 --- a/lib/pages/add_app.dart +++ b/lib/pages/add_app.dart @@ -84,7 +84,6 @@ class _AddAppPageState extends State { : tr('app') ]), items: const [], - defaultValues: const {}, message: '${pickedSource!.enforceTrackOnly ? tr('appsFromSourceAreTrackOnly') : tr('youPickedTrackOnly')}\n\n${tr('trackOnlyAppDescription')}', ); @@ -99,7 +98,6 @@ class _AddAppPageState extends State { return const GeneratedFormModal( title: 'Disable Version Detection', // TODO items: [], - defaultValues: {}, message: 'TODO', ); }) == diff --git a/lib/pages/app.dart b/lib/pages/app.dart index 3d4aae1..b978e07 100644 --- a/lib/pages/app.dart +++ b/lib/pages/app.dart @@ -214,12 +214,7 @@ class _AppPageState extends State { return GeneratedFormModal( title: 'Additional Options', items: source - .additionalSourceAppSpecificFormItems, - defaultValues: app != null - ? app.app.additionalData - : getDefaultValuesFromFormItems( - source - .additionalSourceAppSpecificFormItems)); + .additionalSourceAppSpecificFormItems); }).then((values) { if (app != null && values != null) { var changedApp = app.app; diff --git a/lib/pages/apps.dart b/lib/pages/apps.dart index ba99ca5..e9db472 100644 --- a/lib/pages/apps.dart +++ b/lib/pages/apps.dart @@ -349,7 +349,6 @@ class AppsPageState extends State { return GeneratedFormModal( title: tr('removeSelectedAppsQuestion'), items: const [], - defaultValues: const {}, initValid: true, message: tr( 'xWillBeRemovedButRemainInstalled', @@ -376,36 +375,40 @@ class AppsPageState extends State { ? null : () { HapticFeedback.heavyImpact(); - List formInputs = []; - Map defaultValues = {}; + List formItems = []; if (existingUpdateIdsAllOrSelected.isNotEmpty) { - formInputs.add(GeneratedFormItem('updates', + formItems.add(GeneratedFormItem('updates', label: tr('updateX', args: [ plural('apps', existingUpdateIdsAllOrSelected.length) ]), - type: FormItemType.bool)); - defaultValues['updates'] = 'true'; + type: FormItemType.bool, + defaultValue: 'true')); } if (newInstallIdsAllOrSelected.isNotEmpty) { - formInputs.add(GeneratedFormItem('installs', + formItems.add(GeneratedFormItem('installs', label: tr('installX', args: [ plural('apps', newInstallIdsAllOrSelected.length) ]), - type: FormItemType.bool)); - defaultValues['installs'] = - defaultValues.isEmpty ? 'true' : ''; + type: FormItemType.bool, + defaultValue: + existingUpdateIdsAllOrSelected.isNotEmpty + ? 'true' + : '')); } if (trackOnlyUpdateIdsAllOrSelected.isNotEmpty) { - formInputs.add(GeneratedFormItem('trackonlies', + formItems.add(GeneratedFormItem('trackonlies', label: tr('markXTrackOnlyAsUpdated', args: [ plural('apps', trackOnlyUpdateIdsAllOrSelected.length) ]), - type: FormItemType.bool)); - defaultValues['trackonlies'] = - defaultValues.isEmpty ? 'true' : ''; + type: FormItemType.bool, + defaultValue: existingUpdateIdsAllOrSelected + .isNotEmpty || + newInstallIdsAllOrSelected.isNotEmpty + ? 'true' + : '')); } showDialog?>( context: context, @@ -417,14 +420,14 @@ class AppsPageState extends State { return GeneratedFormModal( title: tr('changeX', args: [plural('apps', totalApps)]), - items: formInputs.map((e) => [e]).toList(), - defaultValues: defaultValues, + items: formItems.map((e) => [e]).toList(), initValid: true, ); }).then((values) { if (values != null) { if (values.isEmpty) { - values = defaultValues; + values = getDefaultValuesFromFormItems( + [formItems]); } bool shouldInstallUpdates = values['updates'] == 'true'; @@ -604,7 +607,6 @@ class AppsPageState extends State { title: tr( 'resetInstallStatusForSelectedAppsQuestion'), items: const [], - defaultValues: const {}, initValid: true, message: tr( 'installStatusOfXWillBeResetExplanation', @@ -677,29 +679,35 @@ class AppsPageState extends State { showDialog?>( context: context, builder: (BuildContext ctx) { + var vals = filter == null + ? AppsFilter().toValuesMap() + : filter!.toValuesMap(); return GeneratedFormModal( title: tr('filterApps'), items: [ [ GeneratedFormItem('appName', - label: tr('appName'), required: false), + label: tr('appName'), + required: false, + defaultValue: vals['appName']), GeneratedFormItem('author', - label: tr('author'), required: false) + label: tr('author'), + required: false, + defaultValue: vals['author']) ], [ GeneratedFormItem('upToDateApps', label: tr('upToDateApps'), - type: FormItemType.bool) + type: FormItemType.bool, + defaultValue: vals['upToDateApps']) ], [ GeneratedFormItem('nonInstalledApps', label: tr('nonInstalledApps'), - type: FormItemType.bool) + type: FormItemType.bool, + defaultValue: vals['nonInstalledApps']) ] - ], - defaultValues: filter == null - ? AppsFilter().toValuesMap() - : filter!.toValuesMap()); + ]); }).then((values) { if (values != null) { setState(() { diff --git a/lib/pages/import_export.dart b/lib/pages/import_export.dart index d6aede6..db69b7f 100644 --- a/lib/pages/import_export.dart +++ b/lib/pages/import_export.dart @@ -172,7 +172,6 @@ class _ImportExportPageState extends State { ]) ] ], - defaultValues: const {}, ); }).then((values) { if (values != null) { @@ -242,7 +241,6 @@ class _ImportExportPageState extends State { 'searchQuery')) ] ], - defaultValues: const {}, ); }); if (values != null && @@ -351,7 +349,6 @@ class _ImportExportPageState extends State { label: e) ]) .toList(), - defaultValues: const {}, ); }); if (values != null) { diff --git a/lib/providers/source_provider.dart b/lib/providers/source_provider.dart index 8d123ea..0b3692e 100644 --- a/lib/providers/source_provider.dart +++ b/lib/providers/source_provider.dart @@ -141,8 +141,9 @@ List getLinksFromParsedHTML( .map((e) => '$prependToLinks${e.attributes['href']!}') .toList(); -getDefaultValuesFromFormItems(List> items) { - Map.fromEntries(items +Map getDefaultValuesFromFormItems( + List> items) { + return Map.fromEntries(items .map((row) => row.map((el) => MapEntry(el.key, el.defaultValue ?? ''))) .reduce((value, element) => [...value, ...element])); } From 9a129d41dfe9ca10005c1c9f2c4a4cdd8552d97a Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Mon, 19 Dec 2022 20:14:54 -0500 Subject: [PATCH 5/7] Added migration code for additionalData (NOTHING TESTED) --- lib/providers/source_provider.dart | 69 +++++++++++++++++++----------- 1 file changed, 45 insertions(+), 24 deletions(-) diff --git a/lib/providers/source_provider.dart b/lib/providers/source_provider.dart index 0b3692e..61df886 100644 --- a/lib/providers/source_provider.dart +++ b/lib/providers/source_provider.dart @@ -69,30 +69,51 @@ class App { return 'ID: $id URL: $url INSTALLED: $installedVersion LATEST: $latestVersion APK: $apkUrls PREFERREDAPK: $preferredApkIndex ADDITIONALDATA: ${additionalData.toString()} LASTCHECK: ${lastUpdateCheck.toString()} PINNED $pinned'; } - factory App.fromJson(Map json) => App( - json['id'] as String, - json['url'] as String, - json['author'] as String, - json['name'] as String, - json['installedVersion'] == null - ? null - : json['installedVersion'] as String, - json['latestVersion'] as String, - json['apkUrls'] == null - ? [] - : List.from(jsonDecode(json['apkUrls'])), - json['preferredApkIndex'] == null ? 0 : json['preferredApkIndex'] as int, - json['additionalData'] == null - ? getDefaultValuesFromFormItems(SourceProvider() - .getSource(json['url']) - .additionalSourceAppSpecificFormItems) - : Map.from(jsonDecode(json['additionalData'])), - json['lastUpdateCheck'] == null - ? null - : DateTime.fromMicrosecondsSinceEpoch(json['lastUpdateCheck']), - json['pinned'] ?? false, - json['trackOnly'] ?? false, - noVersionDetection: json['noVersionDetection'] ?? false); + factory App.fromJson(Map json) { + var formItems = SourceProvider() + .getSource(json['url']) + .additionalSourceAppSpecificFormItems + .reduce((value, element) => [...value, ...element]); + Map additionalData = + getDefaultValuesFromFormItems([formItems]); + 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; + } + }); + } + } + return App( + json['id'] as String, + json['url'] as String, + json['author'] as String, + json['name'] as String, + json['installedVersion'] == null + ? null + : json['installedVersion'] as String, + json['latestVersion'] as String, + json['apkUrls'] == null + ? [] + : List.from(jsonDecode(json['apkUrls'])), + json['preferredApkIndex'] == null + ? 0 + : json['preferredApkIndex'] as int, + additionalData, + json['lastUpdateCheck'] == null + ? null + : DateTime.fromMicrosecondsSinceEpoch(json['lastUpdateCheck']), + json['pinned'] ?? false, + json['trackOnly'] ?? false, + noVersionDetection: json['noVersionDetection'] ?? false); + } Map toJson() => { 'id': id, From 6a21045e5b2e831048a09a10c731daf8b6982cbb Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Tue, 20 Dec 2022 18:00:22 -0500 Subject: [PATCH 6/7] 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}); } From b151eb27e1084afb29333a1cd4c4cb90dea9737a Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Tue, 20 Dec 2022 18:19:44 -0500 Subject: [PATCH 7/7] Translations + bugfix --- assets/translations/de.json | 11 +++++++++++ assets/translations/en.json | 11 +++++++++++ assets/translations/hu.json | 13 ++++++++++++- assets/translations/it.json | 13 ++++++++++++- assets/translations/ja.json | 13 ++++++++++++- assets/translations/zh.json | 11 +++++++++++ lib/pages/add_app.dart | 8 ++++---- lib/pages/app.dart | 18 ++++++++++-------- lib/providers/notifications_provider.dart | 6 +++--- lib/providers/source_provider.dart | 5 ++--- 10 files changed, 88 insertions(+), 21 deletions(-) diff --git a/assets/translations/de.json b/assets/translations/de.json index e20a601..7e0f16e 100644 --- a/assets/translations/de.json +++ b/assets/translations/de.json @@ -188,6 +188,17 @@ "steam": "Steam", "steamMobile": "Steam Mobile", "steamChat": "Steam Chat", + "install": "Install", + "markInstalled": "Mark Installed", + "update": "Update", + "markUpdated": "Mark Updated", + "additionalOptions": "Additional Options", + "disableVersionDetection": "Disable Version Detection", + "noVersionDetectionExplanation": "This option should only be used for Apps where version detection does not work correctly.", + "downloadingX": "Downloading {}", + "downloadNotifDescription": "Notifies the user of the progress in downloading an App", + "noAPKFound": "No APK found", + "noVersionDetection": "No version detection", "tooManyRequestsTryAgainInMinutes": { "one": "Zu viele Anfragen (Rate begrenzt) - versuchen Sie es in {} Minute erneut", "other": "Zu viele Anfragen (Rate begrenzt) - versuchen Sie es in {} Minuten erneut" diff --git a/assets/translations/en.json b/assets/translations/en.json index 8aa0a06..215fd2b 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -188,6 +188,17 @@ "steam": "Steam", "steamMobile": "Steam Mobile", "steamChat": "Steam Chat", + "install": "Install", + "markInstalled": "Mark Installed", + "update": "Update", + "markUpdated": "Mark Updated", + "additionalOptions": "Additional Options", + "disableVersionDetection": "Disable Version Detection", + "noVersionDetectionExplanation": "This option should only be used for Apps where version detection does not work correctly.", + "downloadingX": "Downloading {}", + "downloadNotifDescription": "Notifies the user of the progress in downloading an App", + "noAPKFound": "No APK found", + "noVersionDetection": "No version detection", "tooManyRequestsTryAgainInMinutes": { "one": "Too many requests (rate limited) - try again in {} minute", "other": "Too many requests (rate limited) - try again in {} minutes" diff --git a/assets/translations/hu.json b/assets/translations/hu.json index 0866789..985646c 100644 --- a/assets/translations/hu.json +++ b/assets/translations/hu.json @@ -188,6 +188,17 @@ "steam": "Steam", "steamMobile": "Steam Mobile", "steamChat": "Steam Chat", + "install": "Install", + "markInstalled": "Mark Installed", + "update": "Update", + "markUpdated": "Mark Updated", + "additionalOptions": "Additional Options", + "disableVersionDetection": "Disable Version Detection", + "noVersionDetectionExplanation": "This option should only be used for Apps where version detection does not work correctly.", + "downloadingX": "Downloading {}", + "downloadNotifDescription": "Notifies the user of the progress in downloading an App", + "noAPKFound": "No APK found", + "noVersionDetection": "No version detection", "tooManyRequestsTryAgainInMinutes": { "one": "Túl sok kérés (korlátozott arány) – próbálja újra {} perc múlva", "other": "Túl sok kérés (korlátozott arány) – próbálja újra {} perc múlva" @@ -232,4 +243,4 @@ "one": "A(z) {} és 1 további alkalmazás frissítve.", "other": "{} és további {} alkalmazás frissítve." } -} +} \ No newline at end of file diff --git a/assets/translations/it.json b/assets/translations/it.json index 46d2824..79086e8 100644 --- a/assets/translations/it.json +++ b/assets/translations/it.json @@ -188,6 +188,17 @@ "steam": "Steam", "steamMobile": "Steam Mobile", "steamChat": "Steam Chat", + "install": "Install", + "markInstalled": "Mark Installed", + "update": "Update", + "markUpdated": "Mark Updated", + "additionalOptions": "Additional Options", + "disableVersionDetection": "Disable Version Detection", + "noVersionDetectionExplanation": "This option should only be used for Apps where version detection does not work correctly.", + "downloadingX": "Downloading {}", + "downloadNotifDescription": "Notifies the user of the progress in downloading an App", + "noAPKFound": "No APK found", + "noVersionDetection": "No version detection", "tooManyRequestsTryAgainInMinutes": { "one": "Troppe richieste (traffico limitato) - riprova tra {} minuto", "other": "Troppe richieste (traffico limitato) - riprova tra {} minuti" @@ -232,4 +243,4 @@ "one": "{} e un'altra App sono state aggiornate.", "other": "{} e altre {} App sono state aggiornate." } -} +} \ No newline at end of file diff --git a/assets/translations/ja.json b/assets/translations/ja.json index 11e87da..278a962 100644 --- a/assets/translations/ja.json +++ b/assets/translations/ja.json @@ -188,6 +188,17 @@ "steam": "Steam", "steamMobile": "Steam Mobile", "steamChat": "Steam Chat", + "install": "Install", + "markInstalled": "Mark Installed", + "update": "Update", + "markUpdated": "Mark Updated", + "additionalOptions": "Additional Options", + "disableVersionDetection": "Disable Version Detection", + "noVersionDetectionExplanation": "This option should only be used for Apps where version detection does not work correctly.", + "downloadingX": "Downloading {}", + "downloadNotifDescription": "Notifies the user of the progress in downloading an App", + "noAPKFound": "No APK found", + "noVersionDetection": "No version detection", "tooManyRequestsTryAgainInMinutes": { "one": "リクエストが多すぎます(レート制限)- {}分後に再試行してください", "other": "リクエストが多すぎます(レート制限)- {}分後に再試行してください" @@ -232,4 +243,4 @@ "one": "{}とさらに{}個のアプリがアップデートされました。", "other": "{}とさらに{}個のアプリがアップデートされました。" } -} +} \ No newline at end of file diff --git a/assets/translations/zh.json b/assets/translations/zh.json index 50e8040..e88a99f 100644 --- a/assets/translations/zh.json +++ b/assets/translations/zh.json @@ -188,6 +188,17 @@ "steam": "Steam", "steamMobile": "Steam Mobile", "steamChat": "Steam Chat", + "install": "Install", + "markInstalled": "Mark Installed", + "update": "Update", + "markUpdated": "Mark Updated", + "additionalOptions": "Additional Options", + "disableVersionDetection": "Disable Version Detection", + "noVersionDetectionExplanation": "This option should only be used for Apps where version detection does not work correctly.", + "downloadingX": "Downloading {}", + "downloadNotifDescription": "Notifies the user of the progress in downloading an App", + "noAPKFound": "No APK found", + "noVersionDetection": "No version detection", "tooManyRequestsTryAgainInMinutes": { "one": "请求过多 (API 限制) - 在 {} 分钟后重试", "other": "请求过多 (API 限制) - 在 {} 分钟后重试" diff --git a/lib/pages/add_app.dart b/lib/pages/add_app.dart index 71c92f9..4408a3c 100644 --- a/lib/pages/add_app.dart +++ b/lib/pages/add_app.dart @@ -92,10 +92,10 @@ class _AddAppPageState extends State { await showDialog( context: context, builder: (BuildContext ctx) { - return const GeneratedFormModal( - title: 'Disable Version Detection', // TODO - items: [], - message: 'TODO', + return GeneratedFormModal( + title: tr('disableVersionDetection'), + items: const [], + message: tr('noVersionDetectionExplanation'), ); }) == null) { diff --git a/lib/pages/app.dart b/lib/pages/app.dart index ba131fc..0326a3a 100644 --- a/lib/pages/app.dart +++ b/lib/pages/app.dart @@ -73,7 +73,9 @@ class _AppPageState extends State { height: 25, ), Text( - app?.installedInfo?.name ?? app?.app.name ?? 'App', + app?.installedInfo?.name ?? + app?.app.name ?? + tr('app'), textAlign: TextAlign.center, style: Theme.of(context).textTheme.displayLarge, ), @@ -200,7 +202,7 @@ class _AppPageState extends State { ); }); }, - tooltip: 'Mark as Updated', + tooltip: tr('markUpdated'), icon: const Icon(Icons.done)), if (source != null && source @@ -228,7 +230,7 @@ class _AppPageState extends State { return row; }).toList(); return GeneratedFormModal( - title: 'Additional Options', + title: tr('additionalOptions'), items: items); }).then((values) { if (app != null && values != null) { @@ -249,7 +251,7 @@ class _AppPageState extends State { } }); }, - tooltip: 'Additional Options', + tooltip: tr('additionalOptions'), icon: const Icon(Icons.settings)), const SizedBox(width: 16.0), Expanded( @@ -286,11 +288,11 @@ class _AppPageState extends State { : null, child: Text(app?.app.installedVersion == null ? !trackOnly - ? 'Install' - : 'Mark Installed' + ? tr('install') + : tr('markInstalled') : !trackOnly - ? 'Update' - : 'Mark Updated'))), + ? tr('update') + : tr('markUpdated')))), const SizedBox(width: 16.0), ElevatedButton( onPressed: app?.downloadProgress != null diff --git a/lib/providers/notifications_provider.dart b/lib/providers/notifications_provider.dart index 9e62c5a..b68a669 100644 --- a/lib/providers/notifications_provider.dart +++ b/lib/providers/notifications_provider.dart @@ -80,11 +80,11 @@ class DownloadNotification extends ObtainiumNotification { DownloadNotification(String appName, int progPercent) : super( appName.hashCode, - 'Downloading $appName', + tr('downloadingX', args: [appName]), '', 'APP_DOWNLOADING', - 'Downloading App', - 'Notifies the user of the progress in downloading an App', + tr('downloadingX', args: [tr('app')]), + tr('downloadNotifDescription'), Importance.low, onlyAlertOnce: true, progPercent: progPercent); diff --git a/lib/providers/source_provider.dart b/lib/providers/source_provider.dart index 3a51e50..5966456 100644 --- a/lib/providers/source_provider.dart +++ b/lib/providers/source_provider.dart @@ -142,7 +142,7 @@ preStandardizeUrl(String url) { return url; } -const String noAPKFound = 'No APK found'; +String noAPKFound = tr('noAPKFound'); List getLinksFromParsedHTML( Document dom, RegExp hrefPattern, String prependToLinks) => @@ -196,8 +196,7 @@ class AppSource { ], [ GeneratedFormItem('noVersionDetection', - label: 'Do not attempt version detection', // TODO - type: FormItemType.bool) + label: tr('noVersionDetection'), type: FormItemType.bool) ] ];