mirror of
				https://github.com/ImranR98/Obtainium.git
				synced 2025-10-28 04:03:44 +01:00 
			
		
		
		
	Merge pull request #434 from ImranR98/dev
Store APK names with URLs (#432)
This commit is contained in:
		| @@ -68,7 +68,7 @@ class Codeberg extends AppSource { | ||||
|     if (res.statusCode == 200) { | ||||
|       var releases = jsonDecode(res.body) as List<dynamic>; | ||||
|  | ||||
|       List<String> getReleaseAPKUrls(dynamic release) => | ||||
|       List<MapEntry<String, String>> getReleaseAPKUrls(dynamic release) => | ||||
|           (release['assets'] as List<dynamic>?) | ||||
|               ?.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<String>, | ||||
|       return APKDetails( | ||||
|           version, | ||||
|           targetRelease['apkUrls'] as List<MapEntry<String, String>>, | ||||
|           getAppNames(standardUrl), | ||||
|           releaseDate: releaseDate, | ||||
|           changeLog: changeLog.isEmpty ? null : changeLog); | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -161,7 +161,9 @@ class GitHub extends AppSource { | ||||
|         throw NoVersionError(); | ||||
|       } | ||||
|       var changeLog = targetRelease['body'].toString(); | ||||
|       return APKDetails(version, targetRelease['apkUrls'] as List<String>, | ||||
|       return APKDetails( | ||||
|           version, | ||||
|           getApkUrlsFromUrls(targetRelease['apkUrls'] as List<String>), | ||||
|           getAppNames(standardUrl), | ||||
|           releaseDate: releaseDate, | ||||
|           changeLog: changeLog.isEmpty ? null : changeLog); | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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); | ||||
|     } | ||||
|   | ||||
| @@ -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 { | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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); | ||||
|     } | ||||
|   | ||||
| @@ -50,7 +50,7 @@ class SourceForge extends AppSource { | ||||
|               .toList(); | ||||
|       return APKDetails( | ||||
|           version, | ||||
|           apkUrlList, | ||||
|           getApkUrlsFromUrls(apkUrlList), | ||||
|           AppNames( | ||||
|               name, standardUrl.substring(standardUrl.lastIndexOf('/') + 1))); | ||||
|     } else { | ||||
|   | ||||
| @@ -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); | ||||
|     } | ||||
|   | ||||
| @@ -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); | ||||
|     } | ||||
|   | ||||
| @@ -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); | ||||
|     } | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
|   | ||||
| @@ -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<String?> confirmApkUrl(App app, BuildContext? context) async { | ||||
|   Future<MapEntry<String, String>?> 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<String, String>? apkUrl = app.apkUrls[app.preferredApkIndex]; | ||||
|     // get device supported architecture | ||||
|     List<String> 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<String, String>? 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<String, String>? initVal; | ||||
|   final List<String>? archs; | ||||
|  | ||||
|   @override | ||||
| @@ -931,7 +932,7 @@ class APKPicker extends StatefulWidget { | ||||
| } | ||||
|  | ||||
| class _APKPickerState extends State<APKPicker> { | ||||
|   String? apkUrl; | ||||
|   MapEntry<String, String>? apkUrl; | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
| @@ -944,15 +945,13 @@ class _APKPickerState extends State<APKPicker> { | ||||
|         const SizedBox(height: 16), | ||||
|         ...widget.app.apkUrls.map( | ||||
|           (u) => RadioListTile<String>( | ||||
|               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; | ||||
|                 }); | ||||
|               }), | ||||
|         ), | ||||
|   | ||||
| @@ -34,7 +34,7 @@ class AppNames { | ||||
|  | ||||
| class APKDetails { | ||||
|   late String version; | ||||
|   late List<String> apkUrls; | ||||
|   late List<MapEntry<String, String>> 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<String> apkUrls = []; | ||||
|   List<MapEntry<String, String>> apkUrls = []; | ||||
|   late int preferredApkIndex; | ||||
|   late Map<String, dynamic> 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<MapEntry<String, String>> apkUrls = []; | ||||
|     if (json['apkUrls'] != null) { | ||||
|       var apkUrlJson = jsonDecode(json['apkUrls']); | ||||
|       try { | ||||
|         apkUrls = getApkUrlsFromUrls(List<String>.from(apkUrlJson)); | ||||
|       } catch (e) { | ||||
|         apkUrls = List<dynamic>.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<String>.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<String, dynamic> getDefaultValuesFromFormItems( | ||||
|       .reduce((value, element) => [...value, ...element])); | ||||
| } | ||||
|  | ||||
| getApkUrlsFromUrls(List<String> 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(); | ||||
|   | ||||
| @@ -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' | ||||
|   | ||||
		Reference in New Issue
	
	Block a user