diff --git a/lib/app_sources/codeberg.dart b/lib/app_sources/codeberg.dart index 1d181bc..32ecad0 100644 --- a/lib/app_sources/codeberg.dart +++ b/lib/app_sources/codeberg.dart @@ -68,7 +68,7 @@ class Codeberg extends AppSource { if (res.statusCode == 200) { var releases = jsonDecode(res.body) as List; - List getReleaseAPKUrls(dynamic release) => + List> getReleaseAPKUrls(dynamic release) => (release['assets'] as List?) ?.map((e) { return e['name'] != null && e['browser_download_url'] != null @@ -77,7 +77,6 @@ class Codeberg extends AppSource { : const MapEntry('', ''); }) .where((element) => element.key.toLowerCase().endsWith('.apk')) - .map((e) => e.value) .toList() ?? []; @@ -119,7 +118,9 @@ class Codeberg extends AppSource { throw NoVersionError(); } var changeLog = targetRelease['body'].toString(); - return APKDetails(version, targetRelease['apkUrls'] as List, + return APKDetails( + version, + targetRelease['apkUrls'] as List>, getAppNames(standardUrl), releaseDate: releaseDate, changeLog: changeLog.isEmpty ? null : changeLog); diff --git a/lib/app_sources/fdroid.dart b/lib/app_sources/fdroid.dart index f0279fe..3d87419 100644 --- a/lib/app_sources/fdroid.dart +++ b/lib/app_sources/fdroid.dart @@ -50,7 +50,7 @@ class FDroid extends AppSource { .where((element) => element['versionName'] == latestVersion) .map((e) => '${apkUrlPrefix}_${e['versionCode']}.apk') .toList(); - return APKDetails(latestVersion, apkUrls, + return APKDetails(latestVersion, getApkUrlsFromUrls(apkUrls), AppNames(name, Uri.parse(standardUrl).pathSegments.last)); } else { throw getObtainiumHttpError(res); diff --git a/lib/app_sources/fdroidrepo.dart b/lib/app_sources/fdroidrepo.dart index 11e8e01..8523257 100644 --- a/lib/app_sources/fdroidrepo.dart +++ b/lib/app_sources/fdroidrepo.dart @@ -80,7 +80,8 @@ class FDroidRepo extends AppSource { element.querySelector('apkname') != null) .map((e) => '$standardUrl/${e.querySelector('apkname')!.innerHtml}') .toList(); - return APKDetails(latestVersion, apkUrls, AppNames(authorName, appName), + return APKDetails(latestVersion, getApkUrlsFromUrls(apkUrls), + AppNames(authorName, appName), releaseDate: releaseDate); } else { throw getObtainiumHttpError(res); diff --git a/lib/app_sources/github.dart b/lib/app_sources/github.dart index 39fbf2f..a7d8a0e 100644 --- a/lib/app_sources/github.dart +++ b/lib/app_sources/github.dart @@ -161,7 +161,9 @@ class GitHub extends AppSource { throw NoVersionError(); } var changeLog = targetRelease['body'].toString(); - return APKDetails(version, targetRelease['apkUrls'] as List, + return APKDetails( + version, + getApkUrlsFromUrls(targetRelease['apkUrls'] as List), getAppNames(standardUrl), releaseDate: releaseDate, changeLog: changeLog.isEmpty ? null : changeLog); diff --git a/lib/app_sources/gitlab.dart b/lib/app_sources/gitlab.dart index 428568e..7d813d7 100644 --- a/lib/app_sources/gitlab.dart +++ b/lib/app_sources/gitlab.dart @@ -60,7 +60,8 @@ class GitLab extends AppSource { if (version == null) { throw NoVersionError(); } - return APKDetails(version, apkUrls, GitHub().getAppNames(standardUrl), + return APKDetails(version, getApkUrlsFromUrls(apkUrls), + GitHub().getAppNames(standardUrl), releaseDate: releaseDate); } else { throw getObtainiumHttpError(res); diff --git a/lib/app_sources/html.dart b/lib/app_sources/html.dart index 5d44b98..bf4203e 100644 --- a/lib/app_sources/html.dart +++ b/lib/app_sources/html.dart @@ -42,7 +42,8 @@ class HTML extends AppSource { ? '${uri.origin}/$e' : '${uri.origin}/${uri.path}/$e') .toList(); - return APKDetails(version, apkUrls, AppNames(uri.host, tr('app'))); + return APKDetails( + version, getApkUrlsFromUrls(apkUrls), AppNames(uri.host, tr('app'))); } else { throw getObtainiumHttpError(res); } diff --git a/lib/app_sources/mullvad.dart b/lib/app_sources/mullvad.dart index 763befd..3f43def 100644 --- a/lib/app_sources/mullvad.dart +++ b/lib/app_sources/mullvad.dart @@ -58,7 +58,7 @@ class Mullvad extends AppSource { } return APKDetails( versions[0], - ['https://mullvad.net/download/app/apk/latest'], + getApkUrlsFromUrls(['https://mullvad.net/download/app/apk/latest']), AppNames(name, 'Mullvad-VPN'), changeLog: changeLog); } else { diff --git a/lib/app_sources/neutroncode.dart b/lib/app_sources/neutroncode.dart index b5e499a..881cc31 100644 --- a/lib/app_sources/neutroncode.dart +++ b/lib/app_sources/neutroncode.dart @@ -98,7 +98,7 @@ class NeutronCode extends AppSource { ? (customDateParse(dateStringOriginal)) : null; var changeLogElements = http.querySelectorAll('.pd-fdesc p'); - return APKDetails(version, [apkUrl], + return APKDetails(version, getApkUrlsFromUrls([apkUrl]), AppNames(runtimeType.toString(), name ?? standardUrl.split('/').last), releaseDate: dateString != null ? DateTime.parse(dateString) : null, changeLog: changeLogElements.isNotEmpty diff --git a/lib/app_sources/signal.dart b/lib/app_sources/signal.dart index 38af04a..4258d0c 100644 --- a/lib/app_sources/signal.dart +++ b/lib/app_sources/signal.dart @@ -28,7 +28,8 @@ class Signal extends AppSource { if (version == null) { throw NoVersionError(); } - return APKDetails(version, apkUrls, AppNames(name, 'Signal')); + return APKDetails( + version, getApkUrlsFromUrls(apkUrls), AppNames(name, 'Signal')); } else { throw getObtainiumHttpError(res); } diff --git a/lib/app_sources/sourceforge.dart b/lib/app_sources/sourceforge.dart index ed484a6..11aec8e 100644 --- a/lib/app_sources/sourceforge.dart +++ b/lib/app_sources/sourceforge.dart @@ -50,7 +50,7 @@ class SourceForge extends AppSource { .toList(); return APKDetails( version, - apkUrlList, + getApkUrlsFromUrls(apkUrlList), AppNames( name, standardUrl.substring(standardUrl.lastIndexOf('/') + 1))); } else { diff --git a/lib/app_sources/steammobile.dart b/lib/app_sources/steammobile.dart index db6def3..5478a8f 100644 --- a/lib/app_sources/steammobile.dart +++ b/lib/app_sources/steammobile.dart @@ -53,7 +53,8 @@ class SteamMobile extends AppSource { var version = links[0].substring( versionMatch.start + apkNamePrefix.length + 2, versionMatch.end - 4); var apkUrls = [links[0]]; - return APKDetails(version, apkUrls, AppNames(name, apks[apkNamePrefix]!)); + return APKDetails(version, getApkUrlsFromUrls(apkUrls), + AppNames(name, apks[apkNamePrefix]!)); } else { throw getObtainiumHttpError(res); } diff --git a/lib/app_sources/telegramapp.dart b/lib/app_sources/telegramapp.dart index c152ed9..dd59e50 100644 --- a/lib/app_sources/telegramapp.dart +++ b/lib/app_sources/telegramapp.dart @@ -32,7 +32,8 @@ class TelegramApp extends AppSource { throw NoVersionError(); } String? apkUrl = 'https://telegram.org/dl/android/apk'; - return APKDetails(version, [apkUrl], AppNames('Telegram', 'Telegram')); + return APKDetails(version, getApkUrlsFromUrls([apkUrl]), + AppNames('Telegram', 'Telegram')); } else { throw getObtainiumHttpError(res); } diff --git a/lib/app_sources/vlc.dart b/lib/app_sources/vlc.dart index e4678d7..56adf8d 100644 --- a/lib/app_sources/vlc.dart +++ b/lib/app_sources/vlc.dart @@ -54,7 +54,8 @@ class VLC extends AppSource { throw getObtainiumHttpError(res2); } - return APKDetails(version, apkUrls, AppNames('VideoLAN', 'VLC')); + return APKDetails( + version, getApkUrlsFromUrls(apkUrls), AppNames('VideoLAN', 'VLC')); } else { throw getObtainiumHttpError(res); } diff --git a/lib/app_sources/whatsapp.dart b/lib/app_sources/whatsapp.dart index 6d08549..9512158 100644 --- a/lib/app_sources/whatsapp.dart +++ b/lib/app_sources/whatsapp.dart @@ -64,9 +64,9 @@ class WhatsApp extends AppSource { vLines[0].substring(versionMatch.start, versionMatch.end); return APKDetails( version, - [ + getApkUrlsFromUrls([ 'https://www.whatsapp.com/android?v=$version&=thisIsaPlaceholder&a=realURLPrefetchedAtDownloadTime' - ], + ]), AppNames('Meta', 'WhatsApp')); } else { throw getObtainiumHttpError(res); diff --git a/lib/main.dart b/lib/main.dart index b30c939..db412df 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -21,7 +21,7 @@ import 'package:easy_localization/src/easy_localization_controller.dart'; // ignore: implementation_imports import 'package:easy_localization/src/localization.dart'; -const String currentVersion = '0.11.24'; +const String currentVersion = '0.11.25'; const String currentReleaseTag = 'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES diff --git a/lib/providers/apps_provider.dart b/lib/providers/apps_provider.dart index 4815129..31f5b22 100644 --- a/lib/providers/apps_provider.dart +++ b/lib/providers/apps_provider.dart @@ -169,7 +169,7 @@ class AppsProvider with ChangeNotifier { '${app.id}-${app.latestVersion}-${app.preferredApkIndex}.apk'; String downloadUrl = await SourceProvider() .getSource(app.url) - .apkUrlPrefetchModifier(app.apkUrls[app.preferredApkIndex]); + .apkUrlPrefetchModifier(app.apkUrls[app.preferredApkIndex].value); var notif = DownloadNotification(app.name, 100); notificationsProvider?.cancel(notif.id); int? prevProg; @@ -296,9 +296,10 @@ class AppsProvider with ChangeNotifier { await intent.launch(); } - Future confirmApkUrl(App app, BuildContext? context) async { + Future?> confirmApkUrl( + App app, BuildContext? context) async { // If the App has more than one APK, the user should pick one (if context provided) - String? apkUrl = app.apkUrls[app.preferredApkIndex]; + MapEntry? apkUrl = app.apkUrls[app.preferredApkIndex]; // get device supported architecture List archs = (await DeviceInfoPlugin().androidInfo).supportedAbis; @@ -321,14 +322,14 @@ class AppsProvider with ChangeNotifier { // If the picked APK comes from an origin different from the source, get user confirmation (if context provided) if (apkUrl != null && - getHost(apkUrl) != getHost(app.url) && + getHost(apkUrl.value) != getHost(app.url) && context != null) { // ignore: use_build_context_synchronously if (await showDialog( context: context, builder: (BuildContext ctx) { return APKOriginWarningDialog( - sourceUrl: app.url, apkUrl: apkUrl!); + sourceUrl: app.url, apkUrl: apkUrl!.value); }) != true) { apkUrl = null; @@ -353,7 +354,7 @@ class AppsProvider with ChangeNotifier { if (apps[id] == null) { throw ObtainiumError(tr('appNotFound')); } - String? apkUrl; + MapEntry? apkUrl; var trackOnly = apps[id]!.app.additionalSettings['trackOnly'] == true; if (!trackOnly) { apkUrl = await confirmApkUrl(apps[id]!.app, context); @@ -923,7 +924,7 @@ class APKPicker extends StatefulWidget { const APKPicker({super.key, required this.app, this.initVal, this.archs}); final App app; - final String? initVal; + final MapEntry? initVal; final List? archs; @override @@ -931,7 +932,7 @@ class APKPicker extends StatefulWidget { } class _APKPickerState extends State { - String? apkUrl; + MapEntry? apkUrl; @override Widget build(BuildContext context) { @@ -944,15 +945,13 @@ class _APKPickerState extends State { const SizedBox(height: 16), ...widget.app.apkUrls.map( (u) => RadioListTile( - title: Text(Uri.parse(u) - .pathSegments - .where((element) => element.isNotEmpty) - .last), - value: u, - groupValue: apkUrl, + title: Text(u.key), + value: u.value, + groupValue: apkUrl!.value, onChanged: (String? val) { setState(() { - apkUrl = val; + apkUrl = + widget.app.apkUrls.where((e) => e.value == val).first; }); }), ), diff --git a/lib/providers/source_provider.dart b/lib/providers/source_provider.dart index be09d43..6e3430c 100644 --- a/lib/providers/source_provider.dart +++ b/lib/providers/source_provider.dart @@ -34,7 +34,7 @@ class AppNames { class APKDetails { late String version; - late List apkUrls; + late List> apkUrls; late AppNames names; late DateTime? releaseDate; late String? changeLog; @@ -50,7 +50,7 @@ class App { late String name; String? installedVersion; late String latestVersion; - List apkUrls = []; + List> apkUrls = []; late int preferredApkIndex; late Map additionalSettings; late DateTime? lastUpdateCheck; @@ -134,6 +134,18 @@ class App { if (preferredApkIndex < 0) { preferredApkIndex = 0; } + // apkUrls can either be old list or new named list apkUrls + List> apkUrls = []; + if (json['apkUrls'] != null) { + var apkUrlJson = jsonDecode(json['apkUrls']); + try { + apkUrls = getApkUrlsFromUrls(List.from(apkUrlJson)); + } catch (e) { + apkUrls = List.from(apkUrlJson) + .map((e) => MapEntry(e[0] as String, e[1] as String)) + .toList(); + } + } return App( json['id'] as String, json['url'] as String, @@ -143,9 +155,7 @@ class App { ? null : json['installedVersion'] as String, json['latestVersion'] as String, - json['apkUrls'] == null - ? [] - : List.from(jsonDecode(json['apkUrls'])), + apkUrls, preferredApkIndex, additionalSettings, json['lastUpdateCheck'] == null @@ -173,7 +183,7 @@ class App { 'name': name, 'installedVersion': installedVersion, 'latestVersion': latestVersion, - 'apkUrls': jsonEncode(apkUrls), + 'apkUrls': jsonEncode(apkUrls.map((e) => [e.key, e.value]).toList()), 'preferredApkIndex': preferredApkIndex, 'additionalSettings': jsonEncode(additionalSettings), 'lastUpdateCheck': lastUpdateCheck?.microsecondsSinceEpoch, @@ -225,6 +235,11 @@ Map getDefaultValuesFromFormItems( .reduce((value, element) => [...value, ...element])); } +getApkUrlsFromUrls(List urls) => urls + .map((e) => + MapEntry(e.split('/').where((el) => el.trim().isNotEmpty).last, e)) + .toList(); + class AppSource { String? host; late String name; @@ -422,7 +437,7 @@ class SourceProvider { if (additionalSettings['apkFilterRegEx'] != null) { var reg = RegExp(additionalSettings['apkFilterRegEx']); apk.apkUrls = - apk.apkUrls.where((element) => reg.hasMatch(element)).toList(); + apk.apkUrls.where((element) => reg.hasMatch(element.key)).toList(); } if (apk.apkUrls.isEmpty && !trackOnly) { throw NoAPKError(); diff --git a/pubspec.yaml b/pubspec.yaml index e0b862b..f080bd2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,7 +17,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 0.11.24+146 # When changing this, update the tag in main() accordingly +version: 0.11.25+147 # When changing this, update the tag in main() accordingly environment: sdk: '>=2.18.2 <3.0.0'