mirror of
				https://github.com/ImranR98/Obtainium.git
				synced 2025-10-31 13:33:28 +01:00 
			
		
		
		
	Compare commits
	
		
			11 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 0e2a0b65ec | ||
|  | 5b79f399d1 | ||
|  | 2961d5ac17 | ||
|  | 4af4160aaa | ||
|  | 327f73cc9e | ||
|  | e82170fec6 | ||
|  | 8922b1c048 | ||
|  | e9550c6ff0 | ||
|  | 890c3682c4 | ||
|  | a2c38968e1 | ||
|  | a9c3ee4c54 | 
| @@ -7,7 +7,7 @@ Get Android app updates straight from the source. | ||||
| Obtainium allows you to install and update apps directly from their releases pages, and receive notifications when new releases are made available. | ||||
|  | ||||
| More info: | ||||
| - [Obtainium Wiki](https://github.com/ImranR98/Obtainium/wiki) | ||||
| - [Obtainium Wiki](https://wiki.obtainium.imranr.dev/) ([repository](https://github.com/ImranR98/Obtainium-Wiki)) | ||||
| - [AppVerifier](https://github.com/soupslurpr/AppVerifier) - App verification tool (recommended, integrates with Obtainium) | ||||
| - [apps.obtainium.imranr.dev](https://apps.obtainium.imranr.dev/) - Crowdsourced app configurations ([repository](https://github.com/ImranR98/apps.obtainium.imranr.dev)) | ||||
| - [Side Of Burritos - You should use this instead of F-Droid | How to use app RSS feed](https://youtu.be/FFz57zNR_M0) - Original motivation for this app | ||||
|   | ||||
| @@ -114,7 +114,7 @@ | ||||
|     "light": "Hell", | ||||
|     "followSystem": "System folgen", | ||||
|     "followSystemThemeExplanation": "Das Folgen des Systemthemes ist unter Android < 10 nur mit Hilfe von Drittanbieterapps möglich", | ||||
|     "useBlackTheme": "Pure Black Dark Theme verwenden", | ||||
|     "useBlackTheme": "Rein schwarzen Hintergrund verwenden", | ||||
|     "appSortBy": "App sortieren nach", | ||||
|     "authorName": "Autor/Name", | ||||
|     "nameAuthor": "Name/Autor", | ||||
|   | ||||
| @@ -213,7 +213,7 @@ | ||||
|     "releaseDateAsVersion": "Használja a kiadás dátumát verzió-karakterláncként", | ||||
|     "releaseTitleAsVersion": "Használja a kiadás címét verzió-karakterláncként", | ||||
|     "releaseDateAsVersionExplanation": "Ezt a beállítást csak olyan alkalmazásoknál szabad használni, ahol a verzió-érzékelés nem működik megfelelően, de elérhető a kiadás dátuma.", | ||||
|     "changes": "Változásnapló", | ||||
|     "changes": "Változáslista", | ||||
|     "releaseDate": "Kiadás dátuma", | ||||
|     "importFromURLsInFile": "Importálás fájlban található webcímből (pl. OPML)", | ||||
|     "versionDetectionExplanation": "A verzió-karakterlánc egyeztetése az rendszer által érzékelt verzióval", | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| <p>Obtainium allows you to install and update Apps directly from their releases pages, and receive notifications when new releases are made available.</p> | ||||
| <p>Read the <a href="https://github.com/ImranR98/Obtainium/wiki">Wiki</a></p> | ||||
| <p>Read the <a href="https://wiki.obtainium.imranr.dev/">Wiki</a></p> | ||||
| <p> | ||||
| 	<b>Currently supported App sources:</b> | ||||
| </p> | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| <p>Obtainium позволяет вам устанавливать и обновлять приложения прямо с их объявлений о выпусках и получать уведомления о новых выпусках.</p> | ||||
| <p>Для деталей читайте <a href="https://github.com/ImranR98/Obtainium/wiki">Вики</a></p> | ||||
| <p>Для деталей читайте <a href="https://wiki.obtainium.imranr.dev/">Вики</a></p> | ||||
| <p> | ||||
| 	<b>Поддерживаемые источники приложений:</b> | ||||
| </p> | ||||
|   | ||||
| @@ -5,6 +5,8 @@ import 'package:html/parser.dart'; | ||||
| import 'package:http/http.dart'; | ||||
| import 'package:obtainium/components/generated_form.dart'; | ||||
| import 'package:obtainium/custom_errors.dart'; | ||||
| import 'package:obtainium/providers/apps_provider.dart'; | ||||
| import 'package:obtainium/providers/settings_provider.dart'; | ||||
| import 'package:obtainium/providers/source_provider.dart'; | ||||
|  | ||||
| class APKMirror extends AppSource { | ||||
| @@ -31,6 +33,16 @@ class APKMirror extends AppSource { | ||||
|     ]; | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Future<Map<String, String>?> getRequestHeaders( | ||||
|       Map<String, dynamic> additionalSettings, | ||||
|       {bool forAPKDownload = false}) async { | ||||
|     return { | ||||
|       "User-Agent": | ||||
|           "Obtainium/${(await getInstalledInfo(obtainiumId))?.versionName ?? '1.0.0'}" | ||||
|     }; | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   String sourceSpecificStandardizeURL(String url, {bool forSelection = false}) { | ||||
|     RegExp standardUrlRegEx = RegExp( | ||||
|   | ||||
| @@ -105,11 +105,7 @@ class APKPure extends AppSource { | ||||
|                         .map((e) => e.text.trim()) | ||||
|                         .map((t) => t == 'APKs' ? 'APK' : t) ?? | ||||
|                     []; | ||||
|                 String type = types.isEmpty | ||||
|                     ? 'APK' | ||||
|                     : types.length == 1 | ||||
|                         ? types.first | ||||
|                         : types.last; | ||||
|                 String type = types.isEmpty ? 'APK' : types.first; | ||||
|                 String? dateString = apkInfo | ||||
|                     ?.querySelector('div.info-bottom span.time') | ||||
|                     ?.text | ||||
|   | ||||
| @@ -886,7 +886,7 @@ class _SettingsPageState extends State<SettingsPage> { | ||||
|                     ), | ||||
|                     IconButton( | ||||
|                       onPressed: () { | ||||
|                         launchUrlString('${settingsProvider.sourceUrl}/wiki', | ||||
|                         launchUrlString('https://wiki.obtainium.imranr.dev/', | ||||
|                             mode: LaunchMode.externalApplication); | ||||
|                       }, | ||||
|                       icon: const Icon(Icons.help_outline_rounded), | ||||
|   | ||||
| @@ -516,11 +516,29 @@ class AppsProvider with ChangeNotifier { | ||||
|             .listSync() | ||||
|             .where((e) => e.path.toLowerCase().endsWith('.apk')) | ||||
|             .toList(); | ||||
|  | ||||
|         FileSystemEntity? temp; | ||||
|         apks.removeWhere((element) { | ||||
|           bool res = element.uri.pathSegments.last.startsWith(app.id); | ||||
|           if (res) { | ||||
|             temp = element; | ||||
|           } | ||||
|           return res; | ||||
|         }); | ||||
|         if (temp != null) { | ||||
|           apks = [ | ||||
|             temp!, | ||||
|             ...apks, | ||||
|           ]; | ||||
|         } | ||||
|  | ||||
|         for (var i = 0; i < apks.length; i++) { | ||||
|           try { | ||||
|             newInfo = await pm.getPackageArchiveInfo( | ||||
|                 archiveFilePath: apks.first.path); | ||||
|             break; | ||||
|             newInfo = | ||||
|                 await pm.getPackageArchiveInfo(archiveFilePath: apks[i].path); | ||||
|             if (newInfo != null) { | ||||
|               break; | ||||
|             } | ||||
|           } catch (e) { | ||||
|             if (i == apks.length - 1) { | ||||
|               rethrow; | ||||
| @@ -644,28 +662,47 @@ class AppsProvider with ChangeNotifier { | ||||
|     var somethingInstalled = false; | ||||
|     try { | ||||
|       MultiAppMultiError errors = MultiAppMultiError(); | ||||
|       List<File> APKFiles = []; | ||||
|       for (var file in dir.extracted | ||||
|           .listSync(recursive: true, followLinks: false) | ||||
|           .whereType<File>()) { | ||||
|         if (file.path.toLowerCase().endsWith('.apk')) { | ||||
|           try { | ||||
|             somethingInstalled = somethingInstalled || | ||||
|                 await installApk( | ||||
|                     DownloadedApk(dir.appId, file), firstTimeWithContext, | ||||
|                     needsBGWorkaround: needsBGWorkaround, | ||||
|                     shizukuPretendToBeGooglePlay: shizukuPretendToBeGooglePlay); | ||||
|           } catch (e) { | ||||
|             logs.add( | ||||
|                 'Could not install APK from XAPK \'${file.path}\': ${e.toString()}'); | ||||
|             errors.add(dir.appId, e, appName: apps[dir.appId]?.name); | ||||
|           } | ||||
|           APKFiles.add(file); | ||||
|         } else if (file.path.toLowerCase().endsWith('.obb')) { | ||||
|           await moveObbFile(file, dir.appId); | ||||
|         } | ||||
|       } | ||||
|       if (somethingInstalled) { | ||||
|  | ||||
|       File? temp; | ||||
|       APKFiles.removeWhere((element) { | ||||
|         bool res = element.uri.pathSegments.last.startsWith(dir.appId); | ||||
|         if (res) { | ||||
|           temp = element; | ||||
|         } | ||||
|         return res; | ||||
|       }); | ||||
|       if (temp != null) { | ||||
|         APKFiles = [ | ||||
|           temp!, | ||||
|           ...APKFiles, | ||||
|         ]; | ||||
|       } | ||||
|  | ||||
|       try { | ||||
|         await installApk( | ||||
|             DownloadedApk(dir.appId, APKFiles[0]), firstTimeWithContext, | ||||
|             needsBGWorkaround: needsBGWorkaround, | ||||
|             shizukuPretendToBeGooglePlay: shizukuPretendToBeGooglePlay, | ||||
|             additionalAPKs: APKFiles.sublist(1) | ||||
|                 .map((a) => DownloadedApk(dir.appId, a)) | ||||
|                 .toList()); | ||||
|         somethingInstalled = true; | ||||
|         dir.file.delete(recursive: true); | ||||
|       } else if (errors.idsByErrorString.isNotEmpty) { | ||||
|       } catch (e) { | ||||
|         logs.add('Could not install APKs from XAPK: ${e.toString()}'); | ||||
|         errors.add(dir.appId, e, appName: apps[dir.appId]?.name); | ||||
|       } | ||||
|       if (errors.idsByErrorString.isNotEmpty) { | ||||
|         throw errors; | ||||
|       } | ||||
|     } finally { | ||||
| @@ -677,7 +714,8 @@ class AppsProvider with ChangeNotifier { | ||||
|   Future<bool> installApk( | ||||
|       DownloadedApk file, BuildContext? firstTimeWithContext, | ||||
|       {bool needsBGWorkaround = false, | ||||
|       bool shizukuPretendToBeGooglePlay = false}) async { | ||||
|       bool shizukuPretendToBeGooglePlay = false, | ||||
|       List<DownloadedApk> additionalAPKs = const []}) async { | ||||
|     if (firstTimeWithContext != null && | ||||
|         settingsProvider.beforeNewInstallsShareToAppVerifier && | ||||
|         (await getInstalledInfo('dev.soupslurpr.appverifier')) != null) { | ||||
| @@ -693,6 +731,7 @@ class AppsProvider with ChangeNotifier { | ||||
|     if (newInfo == null) { | ||||
|       try { | ||||
|         file.file.deleteSync(recursive: true); | ||||
|         additionalAPKs.forEach((a) => a.file.deleteSync(recursive: true)); | ||||
|       } catch (e) { | ||||
|         // | ||||
|       } finally { | ||||
| @@ -720,8 +759,10 @@ class AppsProvider with ChangeNotifier { | ||||
|     } | ||||
|     int? code; | ||||
|     if (!settingsProvider.useShizuku) { | ||||
|       code = | ||||
|           await AndroidPackageInstaller.installApk(apkFilePath: file.file.path); | ||||
|       var allAPKs = [file.file.path]; | ||||
|       allAPKs.addAll(additionalAPKs.map((a) => a.file.path)); | ||||
|       code = await AndroidPackageInstaller.installApk( | ||||
|           apkFilePath: allAPKs.join(',')); | ||||
|     } else { | ||||
|       code = await ShizukuApkInstaller.installAPK(file.file.uri.toString(), | ||||
|           shizukuPretendToBeGooglePlay ? "com.android.vending" : ""); | ||||
|   | ||||
							
								
								
									
										22
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								pubspec.lock
									
									
									
									
									
								
							| @@ -14,7 +14,7 @@ packages: | ||||
|     description: | ||||
|       path: "." | ||||
|       ref: main | ||||
|       resolved-ref: ba2aa7a11edc2649d1d80c25ed9291521262f714 | ||||
|       resolved-ref: bcad19e964d377da8816718032e5dbf6dd16ba3a | ||||
|       url: "https://github.com/ImranR98/android_package_installer" | ||||
|     source: git | ||||
|     version: "0.0.1" | ||||
| @@ -449,10 +449,10 @@ packages: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: flutter_markdown | ||||
|       sha256: f0e599ba89c9946c8e051780f0ec99aba4ba15895e0380a7ab68f420046fc44e | ||||
|       sha256: "999a4e3cb3e1532a971c86d6c73a480264f6a687959d4887cb4e2990821827e4" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.7.4+1" | ||||
|     version: "0.7.4+2" | ||||
|   flutter_plugin_android_lifecycle: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -731,10 +731,10 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: permission_handler_html | ||||
|       sha256: af26edbbb1f2674af65a8f4b56e1a6f526156bc273d0e65dd8075fab51c78851 | ||||
|       sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.1.3+2" | ||||
|     version: "0.1.3+5" | ||||
|   permission_handler_platform_interface: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -945,10 +945,10 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: sqflite_common | ||||
|       sha256: "4468b24876d673418a7b7147e5a08a715b4998a7ae69227acafaab762e0e5490" | ||||
|       sha256: "761b9740ecbd4d3e66b8916d784e581861fd3c3553eda85e167bc49fdb68f709" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.5.4+5" | ||||
|     version: "2.5.4+6" | ||||
|   sqflite_darwin: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -1145,10 +1145,10 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: webview_flutter_android | ||||
|       sha256: "86c2d01c37c4578ee46560109cf2e18fb271f0d080a796f09188d0952352e057" | ||||
|       sha256: "285cedfd9441267f6cca8843458620b5fda1af75b04f5818d0441acda5d7df19" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "4.0.2" | ||||
|     version: "4.1.0" | ||||
|   webview_flutter_platform_interface: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -1161,10 +1161,10 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: webview_flutter_wkwebview | ||||
|       sha256: "3be297aa4ca78205abdd284cf55f168c35246c75b3079990ad8ba9d257681a30" | ||||
|       sha256: b7e92f129482460951d96ef9a46b49db34bd2e1621685de26e9eaafd9674e7eb | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "3.16.2" | ||||
|     version: "3.16.3" | ||||
|   win32: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|   | ||||
| @@ -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: 1.1.31+2288 | ||||
| version: 1.1.32+2289 | ||||
|  | ||||
| environment: | ||||
|   sdk: '>=3.0.0 <4.0.0' | ||||
|   | ||||
		Reference in New Issue
	
	Block a user