mirror of
				https://github.com/ImranR98/Obtainium.git
				synced 2025-10-31 13:33:28 +01:00 
			
		
		
		
	Compare commits
	
		
			4 Commits
		
	
	
		
			v0.1.7-bet
			...
			v0.1.8-bet
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 52b97662c6 | ||
|  | f63da4b538 | ||
|  | c30c692d87 | ||
|  | d643d5a474 | 
| @@ -9,9 +9,10 @@ import 'package:permission_handler/permission_handler.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| import 'package:workmanager/workmanager.dart'; | ||||
| import 'package:dynamic_color/dynamic_color.dart'; | ||||
| import 'package:device_info_plus/device_info_plus.dart'; | ||||
|  | ||||
| const String currentReleaseTag = | ||||
|     'v0.1.7-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES | ||||
|     'v0.1.8-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES | ||||
|  | ||||
| @pragma('vm:entry-point') | ||||
| void bgTaskCallback() { | ||||
| @@ -43,10 +44,12 @@ void bgTaskCallback() { | ||||
|  | ||||
| void main() async { | ||||
|   WidgetsFlutterBinding.ensureInitialized(); | ||||
|   if ((await DeviceInfoPlugin().androidInfo).version.sdkInt! >= 29) { | ||||
|     SystemChrome.setSystemUIOverlayStyle( | ||||
|       const SystemUiOverlayStyle(systemNavigationBarColor: Colors.transparent), | ||||
|     ); | ||||
|     SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); | ||||
|   } | ||||
|   Workmanager().initialize( | ||||
|     bgTaskCallback, | ||||
|   ); | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter/services.dart'; | ||||
| import 'package:obtainium/providers/apps_provider.dart'; | ||||
| import 'package:obtainium/providers/settings_provider.dart'; | ||||
| import 'package:url_launcher/url_launcher_string.dart'; | ||||
| import 'package:webview_flutter/webview_flutter.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
|  | ||||
| @@ -17,6 +19,7 @@ class _AppPageState extends State<AppPage> { | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     var appsProvider = context.watch<AppsProvider>(); | ||||
|     var settingsProvider = context.watch<SettingsProvider>(); | ||||
|     AppInMemory? app = appsProvider.apps[widget.appId]; | ||||
|     if (app?.app.installedVersion != null) { | ||||
|       appsProvider.getUpdate(app!.app.id); | ||||
| @@ -25,9 +28,57 @@ class _AppPageState extends State<AppPage> { | ||||
|       appBar: AppBar( | ||||
|         title: Text('${app?.app.author}/${app?.app.name}'), | ||||
|       ), | ||||
|       body: WebView( | ||||
|       body: settingsProvider.showAppWebpage | ||||
|           ? WebView( | ||||
|               initialUrl: app?.app.url, | ||||
|               javascriptMode: JavascriptMode.unrestricted, | ||||
|             ) | ||||
|           : Column( | ||||
|               mainAxisAlignment: MainAxisAlignment.center, | ||||
|               crossAxisAlignment: CrossAxisAlignment.stretch, | ||||
|               children: [ | ||||
|                 Text( | ||||
|                   app?.app.name ?? 'App', | ||||
|                   textAlign: TextAlign.center, | ||||
|                   style: Theme.of(context).textTheme.displayLarge, | ||||
|                 ), | ||||
|                 Text( | ||||
|                   'By ${app?.app.author ?? 'Unknown'}', | ||||
|                   textAlign: TextAlign.center, | ||||
|                   style: Theme.of(context).textTheme.headlineMedium, | ||||
|                 ), | ||||
|                 const SizedBox( | ||||
|                   height: 32, | ||||
|                 ), | ||||
|                 GestureDetector( | ||||
|                     onTap: () { | ||||
|                       if (app?.app.url != null) { | ||||
|                         launchUrlString(app?.app.url ?? '', | ||||
|                             mode: LaunchMode.externalApplication); | ||||
|                       } | ||||
|                     }, | ||||
|                     child: Text( | ||||
|                       app?.app.url ?? '', | ||||
|                       textAlign: TextAlign.center, | ||||
|                       style: const TextStyle( | ||||
|                           decoration: TextDecoration.underline, | ||||
|                           fontStyle: FontStyle.italic, | ||||
|                           fontSize: 12), | ||||
|                     )), | ||||
|                 const SizedBox( | ||||
|                   height: 32, | ||||
|                 ), | ||||
|                 Text( | ||||
|                   'Latest Version: ${app?.app.latestVersion ?? 'Unknown'}', | ||||
|                   textAlign: TextAlign.center, | ||||
|                   style: Theme.of(context).textTheme.bodyLarge, | ||||
|                 ), | ||||
|                 Text( | ||||
|                   'Installed Version: ${app?.app.installedVersion ?? 'None'}', | ||||
|                   textAlign: TextAlign.center, | ||||
|                   style: Theme.of(context).textTheme.bodyLarge, | ||||
|                 ), | ||||
|               ], | ||||
|             ), | ||||
|       bottomSheet: Padding( | ||||
|           padding: EdgeInsets.fromLTRB( | ||||
| @@ -100,8 +151,10 @@ class _AppPageState extends State<AppPage> { | ||||
|                                       }); | ||||
|                                 }, | ||||
|                           style: TextButton.styleFrom( | ||||
|                               foregroundColor: Theme.of(context).errorColor, | ||||
|                               surfaceTintColor: Theme.of(context).errorColor), | ||||
|                               foregroundColor: | ||||
|                                   Theme.of(context).colorScheme.error, | ||||
|                               surfaceTintColor: | ||||
|                                   Theme.of(context).colorScheme.error), | ||||
|                           child: const Text('Remove'), | ||||
|                         ), | ||||
|                       ])), | ||||
|   | ||||
| @@ -42,7 +42,7 @@ class _AppsPageState extends State<AppsPage> { | ||||
|               : appsProvider.apps.isEmpty | ||||
|                   ? Text( | ||||
|                       'No Apps', | ||||
|                       style: Theme.of(context).textTheme.headline4, | ||||
|                       style: Theme.of(context).textTheme.headlineMedium, | ||||
|                     ) | ||||
|                   : RefreshIndicator( | ||||
|                       onRefresh: () { | ||||
|   | ||||
| @@ -110,7 +110,21 @@ class _SettingsPageState extends State<SettingsPage> { | ||||
|                         } | ||||
|                       }), | ||||
|                   const SizedBox( | ||||
|                     height: 32, | ||||
|                     height: 16, | ||||
|                   ), | ||||
|                   Row( | ||||
|                     mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                     children: [ | ||||
|                       const Text('Show Source Webpage in App View'), | ||||
|                       Switch( | ||||
|                           value: settingsProvider.showAppWebpage, | ||||
|                           onChanged: (value) { | ||||
|                             settingsProvider.showAppWebpage = value; | ||||
|                           }) | ||||
|                     ], | ||||
|                   ), | ||||
|                   const SizedBox( | ||||
|                     height: 16, | ||||
|                   ), | ||||
|                   Row( | ||||
|                     mainAxisAlignment: MainAxisAlignment.spaceAround, | ||||
| @@ -127,7 +141,7 @@ class _SettingsPageState extends State<SettingsPage> { | ||||
|                                     ); | ||||
|                                   }); | ||||
|                                 }, | ||||
|                           child: const Text('Export Apps')), | ||||
|                           child: const Text('Export App List')), | ||||
|                       ElevatedButton( | ||||
|                           onPressed: () { | ||||
|                             HapticFeedback.lightImpact(); | ||||
| @@ -140,7 +154,7 @@ class _SettingsPageState extends State<SettingsPage> { | ||||
|  | ||||
|                                   return AlertDialog( | ||||
|                                     scrollable: true, | ||||
|                                     title: const Text('Import Apps'), | ||||
|                                     title: const Text('Import App List'), | ||||
|                                     content: Column(children: [ | ||||
|                                       const Text( | ||||
|                                           'Copy the contents of the Obtainium export file and paste them into the field below:'), | ||||
| @@ -193,7 +207,7 @@ class _SettingsPageState extends State<SettingsPage> { | ||||
|                                                     .showSnackBar( | ||||
|                                                   SnackBar( | ||||
|                                                       content: Text( | ||||
|                                                           '$value Apps Imported')), | ||||
|                                                           '$value App${value == 1 ? '' : 's'} Imported')), | ||||
|                                                 ); | ||||
|                                               }).catchError((e) { | ||||
|                                                 ScaffoldMessenger.of(context) | ||||
| @@ -212,7 +226,7 @@ class _SettingsPageState extends State<SettingsPage> { | ||||
|                                   ); | ||||
|                                 }); | ||||
|                           }, | ||||
|                           child: const Text('Import Apps')) | ||||
|                           child: const Text('Import App List')) | ||||
|                     ], | ||||
|                   ), | ||||
|                   const Spacer(), | ||||
| @@ -235,7 +249,7 @@ class _SettingsPageState extends State<SettingsPage> { | ||||
|                         icon: const Icon(Icons.code), | ||||
|                         label: Text( | ||||
|                           'Source', | ||||
|                           style: Theme.of(context).textTheme.caption, | ||||
|                           style: Theme.of(context).textTheme.bodySmall, | ||||
|                         ), | ||||
|                       ) | ||||
|                     ], | ||||
|   | ||||
| @@ -108,6 +108,7 @@ class AppsProvider with ChangeNotifier { | ||||
|       if (apps[id] == null) { | ||||
|         throw 'App not found'; | ||||
|       } | ||||
|       // If the App has more than one APK, the user should pick one | ||||
|       String? apkUrl = apps[id]!.app.apkUrls[apps[id]!.app.preferredApkIndex]; | ||||
|       if (apps[id]!.app.apkUrls.length > 1) { | ||||
|         apkUrl = await showDialog( | ||||
| @@ -116,6 +117,19 @@ class AppsProvider with ChangeNotifier { | ||||
|               return APKPicker(app: apps[id]!.app, initVal: apkUrl); | ||||
|             }); | ||||
|       } | ||||
|       // If the picked APK comes from an origin different from the source, get user confirmation | ||||
|       if (apkUrl != null && | ||||
|           !apkUrl.toLowerCase().startsWith(apps[id]!.app.url.toLowerCase())) { | ||||
|         if (await showDialog( | ||||
|                 context: context, | ||||
|                 builder: (BuildContext ctx) { | ||||
|                   return APKOriginWarningDialog( | ||||
|                       sourceUrl: apps[id]!.app.url, apkUrl: apkUrl!); | ||||
|                 }) != | ||||
|             true) { | ||||
|           apkUrl = null; | ||||
|         } | ||||
|       } | ||||
|       if (apkUrl != null) { | ||||
|         int urlInd = apps[id]!.app.apkUrls.indexOf(apkUrl); | ||||
|         if (urlInd != apps[id]!.app.preferredApkIndex) { | ||||
| @@ -331,7 +345,7 @@ class _APKPickerState extends State<APKPicker> { | ||||
|             child: const Text('Cancel')), | ||||
|         TextButton( | ||||
|             onPressed: () { | ||||
|               HapticFeedback.mediumImpact(); | ||||
|               HapticFeedback.heavyImpact(); | ||||
|               Navigator.of(context).pop(apkUrl); | ||||
|             }, | ||||
|             child: const Text('Continue')) | ||||
| @@ -339,3 +353,40 @@ class _APKPickerState extends State<APKPicker> { | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | ||||
| class APKOriginWarningDialog extends StatefulWidget { | ||||
|   const APKOriginWarningDialog( | ||||
|       {super.key, required this.sourceUrl, required this.apkUrl}); | ||||
|  | ||||
|   final String sourceUrl; | ||||
|   final String apkUrl; | ||||
|  | ||||
|   @override | ||||
|   State<APKOriginWarningDialog> createState() => _APKOriginWarningDialogState(); | ||||
| } | ||||
|  | ||||
| class _APKOriginWarningDialogState extends State<APKOriginWarningDialog> { | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return AlertDialog( | ||||
|       scrollable: true, | ||||
|       title: const Text('Warning'), | ||||
|       content: Text( | ||||
|           'The App source is \'${Uri.parse(widget.sourceUrl).host}\' but the release package comes from \'${Uri.parse(widget.apkUrl).host}\'. Continue?'), | ||||
|       actions: [ | ||||
|         TextButton( | ||||
|             onPressed: () { | ||||
|               HapticFeedback.lightImpact(); | ||||
|               Navigator.of(context).pop(null); | ||||
|             }, | ||||
|             child: const Text('Cancel')), | ||||
|         TextButton( | ||||
|             onPressed: () { | ||||
|               HapticFeedback.heavyImpact(); | ||||
|               Navigator.of(context).pop(true); | ||||
|             }, | ||||
|             child: const Text('Continue')) | ||||
|       ], | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -69,4 +69,13 @@ class SettingsProvider with ChangeNotifier { | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   bool get showAppWebpage { | ||||
|     return prefs?.getBool('showAppWebpage') ?? true; | ||||
|   } | ||||
|  | ||||
|   set showAppWebpage(bool show) { | ||||
|     prefs?.setBool('showAppWebpage', show); | ||||
|     notifyListeners(); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -170,12 +170,19 @@ class GitLab implements AppSource { | ||||
|       var entry = parsedHtml.querySelector('entry'); | ||||
|       var entryContent = | ||||
|           parse(parseFragment(entry?.querySelector('content')!.innerHtml).text); | ||||
|       var apkUrlList = getLinksFromParsedHTML( | ||||
|       var apkUrlList = [ | ||||
|         ...getLinksFromParsedHTML( | ||||
|             entryContent, | ||||
|             RegExp( | ||||
|                 '^${escapeRegEx(standardUri.path)}/uploads/[^/]+/[^/]+\\.apk\$', | ||||
|                 caseSensitive: false), | ||||
|           standardUri.origin); | ||||
|             standardUri.origin), | ||||
|         // GitLab releases may contain links to externally hosted APKs | ||||
|         ...getLinksFromParsedHTML(entryContent, | ||||
|                 RegExp('/[^/]+\\.apk\$', caseSensitive: false), '') | ||||
|             .where((element) => Uri.parse(element).host != '') | ||||
|             .toList() | ||||
|       ]; | ||||
|       if (apkUrlList.isEmpty) { | ||||
|         throw 'No APK found'; | ||||
|       } | ||||
|   | ||||
							
								
								
									
										58
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						
									
										58
									
								
								pubspec.lock
									
									
									
									
									
								
							| @@ -92,13 +92,55 @@ packages: | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "0.7.8" | ||||
|   device_info_plus: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: device_info_plus | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "4.1.2" | ||||
|   device_info_plus_linux: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: device_info_plus_linux | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "3.0.0" | ||||
|   device_info_plus_macos: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: device_info_plus_macos | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "3.0.0" | ||||
|   device_info_plus_platform_interface: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: device_info_plus_platform_interface | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "3.0.0" | ||||
|   device_info_plus_web: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: device_info_plus_web | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "3.0.0" | ||||
|   device_info_plus_windows: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: device_info_plus_windows | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "4.0.0" | ||||
|   dynamic_color: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: dynamic_color | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "1.5.3" | ||||
|     version: "1.5.4" | ||||
|   fake_async: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -152,7 +194,7 @@ packages: | ||||
|       name: flutter_local_notifications | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "9.8.0+1" | ||||
|     version: "9.9.1" | ||||
|   flutter_local_notifications_linux: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -435,7 +477,7 @@ packages: | ||||
|       name: shared_preferences_platform_interface | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "2.0.0" | ||||
|     version: "2.1.0" | ||||
|   shared_preferences_web: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -496,7 +538,7 @@ packages: | ||||
|       name: test_api | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "0.4.12" | ||||
|     version: "0.4.13" | ||||
|   timezone: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -573,7 +615,7 @@ packages: | ||||
|       name: vector_math | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "2.1.2" | ||||
|     version: "2.1.3" | ||||
|   webview_flutter: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
| @@ -587,14 +629,14 @@ packages: | ||||
|       name: webview_flutter_android | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "2.9.5" | ||||
|     version: "2.10.0" | ||||
|   webview_flutter_platform_interface: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: webview_flutter_platform_interface | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "1.9.2" | ||||
|     version: "1.9.3" | ||||
|   webview_flutter_wkwebview: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -639,4 +681,4 @@ packages: | ||||
|     version: "3.1.1" | ||||
| sdks: | ||||
|   dart: ">=2.19.0-79.0.dev <3.0.0" | ||||
|   flutter: ">=3.1.0-0.0.pre.1036" | ||||
|   flutter: ">=3.3.0" | ||||
|   | ||||
							
								
								
									
										11
									
								
								pubspec.yaml
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								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.1.7+8 # When changing this, update the tag in main() accordingly | ||||
| version: 0.1.8+9 # When changing this, update the tag in main() accordingly | ||||
|  | ||||
| environment: | ||||
|   sdk: '>=2.19.0-79.0.dev <3.0.0' | ||||
| @@ -35,21 +35,22 @@ dependencies: | ||||
|  | ||||
|   # The following adds the Cupertino Icons font to your application. | ||||
|   # Use with the CupertinoIcons class for iOS style icons. | ||||
|   cupertino_icons: ^1.0.2 | ||||
|   cupertino_icons: ^1.0.5 | ||||
|   path_provider: ^2.0.11 | ||||
|   flutter_fgbg: ^0.2.0 # Try removing reliance on this | ||||
|   flutter_local_notifications: ^9.8.0+1 | ||||
|   flutter_local_notifications: ^9.9.1 | ||||
|   provider: ^6.0.3 | ||||
|   http: ^0.13.5 | ||||
|   webview_flutter: ^3.0.4 | ||||
|   workmanager: ^0.5.0 | ||||
|   dynamic_color: ^1.5.3 | ||||
|   dynamic_color: ^1.5.4 | ||||
|   install_plugin_v2: ^1.0.0 # Try replacing this | ||||
|   html: ^0.15.0 | ||||
|   shared_preferences: ^2.0.15 | ||||
|   url_launcher: ^6.1.5 | ||||
|   permission_handler: ^10.0.0 | ||||
|   fluttertoast: ^8.0.9 | ||||
|   device_info_plus: ^4.1.2 | ||||
|  | ||||
|  | ||||
| dev_dependencies: | ||||
| @@ -62,7 +63,7 @@ dev_dependencies: | ||||
|   # activated in the `analysis_options.yaml` file located at the root of your | ||||
|   # package. See that file for information about deactivating specific lint | ||||
|   # rules and activating additional ones. | ||||
|   flutter_lints: ^2.0.0 | ||||
|   flutter_lints: ^2.0.1 | ||||
|  | ||||
| flutter_icons: | ||||
|   android: true | ||||
|   | ||||
		Reference in New Issue
	
	Block a user