mirror of
				https://github.com/ImranR98/Obtainium.git
				synced 2025-10-22 18:33:45 +02:00 
			
		
		
		
	Custom link support (#918)
This commit is contained in:
		| @@ -8,7 +8,7 @@ | ||||
|         <activity | ||||
|             android:name=".MainActivity" | ||||
|             android:exported="true" | ||||
|             android:launchMode="singleTop" | ||||
|             android:launchMode="singleInstance" | ||||
|             android:theme="@style/LaunchTheme" | ||||
|             android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" | ||||
|             android:hardwareAccelerated="true" | ||||
| @@ -30,6 +30,14 @@ | ||||
|                     android:name="com.android_package_installer.content.SESSION_API_PACKAGE_INSTALLED" | ||||
|                     android:exported="false"/> | ||||
|             </intent-filter> | ||||
|             <intent-filter> | ||||
|                 <action android:name="android.intent.action.VIEW" /> | ||||
|                 <category android:name="android.intent.category.DEFAULT" /> | ||||
|                 <category android:name="android.intent.category.BROWSABLE" /> | ||||
|                 <data android:scheme="obtainium" | ||||
|                       android:host="add" /> | ||||
|             </intent-filter> | ||||
|          | ||||
|         </activity> | ||||
|         <!-- Don't delete the meta-data below. | ||||
|              This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --> | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| import 'dart:math'; | ||||
|  | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter/services.dart'; | ||||
| @@ -21,10 +23,10 @@ class AddAppPage extends StatefulWidget { | ||||
|   const AddAppPage({super.key}); | ||||
|  | ||||
|   @override | ||||
|   State<AddAppPage> createState() => _AddAppPageState(); | ||||
|   State<AddAppPage> createState() => AddAppPageState(); | ||||
| } | ||||
|  | ||||
| class _AddAppPageState extends State<AddAppPage> { | ||||
| class AddAppPageState extends State<AddAppPage> { | ||||
|   bool gettingAppInfo = false; | ||||
|   bool searching = false; | ||||
|  | ||||
| @@ -36,9 +38,57 @@ class _AddAppPageState extends State<AddAppPage> { | ||||
|   bool additionalSettingsValid = true; | ||||
|   bool inferAppIdIfOptional = true; | ||||
|   List<String> pickedCategories = []; | ||||
|   int searchnum = 0; | ||||
|   SourceProvider sourceProvider = SourceProvider(); | ||||
|  | ||||
|   linkFn(String input) { | ||||
|     try { | ||||
|       if (input.isEmpty) { | ||||
|         throw UnsupportedURLError(); | ||||
|       } | ||||
|       sourceProvider.getSource(input); | ||||
|       changeUserInput(input, true, false); | ||||
|     } catch (e) { | ||||
|       showError(e, context); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   changeUserInput(String input, bool valid, bool isBuilding) { | ||||
|     userInput = input; | ||||
|     if (!isBuilding) { | ||||
|       setState(() { | ||||
|         var prevHost = pickedSource?.host; | ||||
|         try { | ||||
|           var naturalSource = | ||||
|               valid ? sourceProvider.getSource(userInput) : null; | ||||
|           if (naturalSource != null && | ||||
|               naturalSource.runtimeType.toString() != | ||||
|                   HTML().runtimeType.toString()) { | ||||
|             // If input has changed to match a regular source, reset the override | ||||
|             pickedSourceOverride = null; | ||||
|           } | ||||
|         } catch (e) { | ||||
|           // ignore | ||||
|         } | ||||
|         var source = valid | ||||
|             ? sourceProvider.getSource(userInput, | ||||
|                 overrideSource: pickedSourceOverride) | ||||
|             : null; | ||||
|         if (pickedSource.runtimeType != source.runtimeType || | ||||
|             (prevHost != null && prevHost != source?.host)) { | ||||
|           pickedSource = source; | ||||
|           additionalSettings = source != null | ||||
|               ? getDefaultValuesFromFormItems( | ||||
|                   source.combinedAppSpecificSettingFormItems) | ||||
|               : {}; | ||||
|           additionalSettingsValid = source != null | ||||
|               ? !sourceProvider.ifRequiredAppSpecificSettingsExist(source) | ||||
|               : true; | ||||
|           inferAppIdIfOptional = true; | ||||
|         } | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     AppsProvider appsProvider = context.read<AppsProvider>(); | ||||
| @@ -48,47 +98,6 @@ class _AddAppPageState extends State<AddAppPage> { | ||||
|  | ||||
|     bool doingSomething = gettingAppInfo || searching; | ||||
|  | ||||
|     changeUserInput(String input, bool valid, bool isBuilding, | ||||
|         {bool isSearch = false}) { | ||||
|       userInput = input; | ||||
|       if (!isBuilding) { | ||||
|         setState(() { | ||||
|           if (isSearch) { | ||||
|             searchnum++; | ||||
|           } | ||||
|           var prevHost = pickedSource?.host; | ||||
|           try { | ||||
|             var naturalSource = | ||||
|                 valid ? sourceProvider.getSource(userInput) : null; | ||||
|             if (naturalSource != null && | ||||
|                 naturalSource.runtimeType.toString() != | ||||
|                     HTML().runtimeType.toString()) { | ||||
|               // If input has changed to match a regular source, reset the override | ||||
|               pickedSourceOverride = null; | ||||
|             } | ||||
|           } catch (e) { | ||||
|             // ignore | ||||
|           } | ||||
|           var source = valid | ||||
|               ? sourceProvider.getSource(userInput, | ||||
|                   overrideSource: pickedSourceOverride) | ||||
|               : null; | ||||
|           if (pickedSource.runtimeType != source.runtimeType || | ||||
|               (prevHost != null && prevHost != source?.host)) { | ||||
|             pickedSource = source; | ||||
|             additionalSettings = source != null | ||||
|                 ? getDefaultValuesFromFormItems( | ||||
|                     source.combinedAppSpecificSettingFormItems) | ||||
|                 : {}; | ||||
|             additionalSettingsValid = source != null | ||||
|                 ? !sourceProvider.ifRequiredAppSpecificSettingsExist(source) | ||||
|                 : true; | ||||
|             inferAppIdIfOptional = true; | ||||
|           } | ||||
|         }); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     Future<bool> getTrackOnlyConfirmationIfNeeded(bool userPickedTrackOnly, | ||||
|         {bool ignoreHideSetting = false}) async { | ||||
|       var useTrackOnly = userPickedTrackOnly || pickedSource!.enforceTrackOnly; | ||||
| @@ -205,7 +214,7 @@ class _AddAppPageState extends State<AddAppPage> { | ||||
|           children: [ | ||||
|             Expanded( | ||||
|                 child: GeneratedForm( | ||||
|                     key: Key(searchnum.toString()), | ||||
|                     key: Key(Random().nextInt(10000).toString()), | ||||
|                     items: [ | ||||
|                       [ | ||||
|                         GeneratedFormTextField('appSourceURL', | ||||
| @@ -325,7 +334,7 @@ class _AddAppPageState extends State<AddAppPage> { | ||||
|                     ); | ||||
|                   }); | ||||
|           if (selectedUrls != null && selectedUrls.isNotEmpty) { | ||||
|             changeUserInput(selectedUrls[0], true, false, isSearch: true); | ||||
|             changeUserInput(selectedUrls[0], true, false); | ||||
|           } | ||||
|         } | ||||
|       } catch (e) { | ||||
|   | ||||
| @@ -1,4 +1,7 @@ | ||||
| import 'dart:async'; | ||||
|  | ||||
| import 'package:animations/animations.dart'; | ||||
| import 'package:app_links/app_links.dart'; | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter/services.dart'; | ||||
| @@ -30,53 +33,95 @@ class _HomePageState extends State<HomePage> { | ||||
|   bool isReversing = false; | ||||
|   int prevAppCount = -1; | ||||
|   bool prevIsLoading = true; | ||||
|   late AppLinks _appLinks; | ||||
|   StreamSubscription<Uri>? _linkSubscription; | ||||
|  | ||||
|   List<NavigationPageItem> pages = [ | ||||
|     NavigationPageItem(tr('appsString'), Icons.apps, | ||||
|         AppsPage(key: GlobalKey<AppsPageState>())), | ||||
|     NavigationPageItem(tr('addApp'), Icons.add, const AddAppPage()), | ||||
|     NavigationPageItem( | ||||
|         tr('addApp'), Icons.add, AddAppPage(key: GlobalKey<AddAppPageState>())), | ||||
|     NavigationPageItem( | ||||
|         tr('importExport'), Icons.import_export, const ImportExportPage()), | ||||
|     NavigationPageItem(tr('settings'), Icons.settings, const SettingsPage()) | ||||
|   ]; | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
|     initDeepLinks(); | ||||
|   } | ||||
|  | ||||
|   Future<void> initDeepLinks() async { | ||||
|     _appLinks = AppLinks(); | ||||
|  | ||||
|     goToAddApp(Uri uri) async { | ||||
|       switchToPage(1); | ||||
|       while ( | ||||
|           (pages[1].widget.key as GlobalKey<AddAppPageState>?)?.currentState == | ||||
|               null) { | ||||
|         await Future.delayed(const Duration(microseconds: 1)); | ||||
|       } | ||||
|       (pages[1].widget.key as GlobalKey<AddAppPageState>?) | ||||
|           ?.currentState | ||||
|           ?.linkFn(uri.path.length > 1 ? uri.path.substring(1) : ""); | ||||
|     } | ||||
|  | ||||
|     // Check initial link if app was in cold state (terminated) | ||||
|     final appLink = await _appLinks.getInitialAppLink(); | ||||
|     if (appLink != null) { | ||||
|       await goToAddApp(appLink); | ||||
|     } | ||||
|  | ||||
|     // Handle link when app is in warm state (front or background) | ||||
|     _linkSubscription = _appLinks.uriLinkStream.listen((uri) async { | ||||
|       await goToAddApp(uri); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   setIsReversing(int targetIndex) { | ||||
|     bool reversing = selectedIndexHistory.isNotEmpty && | ||||
|         selectedIndexHistory.last > targetIndex; | ||||
|     setState(() { | ||||
|       isReversing = reversing; | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   switchToPage(int index) async { | ||||
|     setIsReversing(index); | ||||
|     if (index == 0) { | ||||
|       while ((pages[0].widget.key as GlobalKey<AppsPageState>).currentState != | ||||
|           null) { | ||||
|         // Avoid duplicate GlobalKey error | ||||
|         await Future.delayed(const Duration(microseconds: 1)); | ||||
|       } | ||||
|       setState(() { | ||||
|         selectedIndexHistory.clear(); | ||||
|       }); | ||||
|     } else if (selectedIndexHistory.isEmpty || | ||||
|         (selectedIndexHistory.isNotEmpty && | ||||
|             selectedIndexHistory.last != index)) { | ||||
|       // while (index == 1 && | ||||
|       //     (pages[0].widget.key as GlobalKey<AppsPageState>).currentState != | ||||
|       //         null) { | ||||
|       //   // Avoid duplicate GlobalKey error | ||||
|       //   await Future.delayed(const Duration(microseconds: 1)); | ||||
|       // } | ||||
|       setState(() { | ||||
|         int existingInd = selectedIndexHistory.indexOf(index); | ||||
|         if (existingInd >= 0) { | ||||
|           selectedIndexHistory.removeAt(existingInd); | ||||
|         } | ||||
|         selectedIndexHistory.add(index); | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     AppsProvider appsProvider = context.watch<AppsProvider>(); | ||||
|     SettingsProvider settingsProvider = context.watch<SettingsProvider>(); | ||||
|  | ||||
|     setIsReversing(int targetIndex) { | ||||
|       bool reversing = selectedIndexHistory.isNotEmpty && | ||||
|           selectedIndexHistory.last > targetIndex; | ||||
|       setState(() { | ||||
|         isReversing = reversing; | ||||
|       }); | ||||
|     } | ||||
|  | ||||
|     switchToPage(int index) async { | ||||
|       setIsReversing(index); | ||||
|       if (index == 0) { | ||||
|         while ((pages[0].widget.key as GlobalKey<AppsPageState>).currentState != | ||||
|             null) { | ||||
|           // Avoid duplicate GlobalKey error | ||||
|           await Future.delayed(const Duration(microseconds: 1)); | ||||
|         } | ||||
|         setState(() { | ||||
|           selectedIndexHistory.clear(); | ||||
|         }); | ||||
|       } else if (selectedIndexHistory.isEmpty || | ||||
|           (selectedIndexHistory.isNotEmpty && | ||||
|               selectedIndexHistory.last != index)) { | ||||
|         setState(() { | ||||
|           int existingInd = selectedIndexHistory.indexOf(index); | ||||
|           if (existingInd >= 0) { | ||||
|             selectedIndexHistory.removeAt(existingInd); | ||||
|           } | ||||
|           selectedIndexHistory.add(index); | ||||
|         }); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if (!prevIsLoading && | ||||
|         prevAppCount >= 0 && | ||||
|         appsProvider.apps.length > prevAppCount && | ||||
| @@ -143,4 +188,10 @@ class _HomePageState extends State<HomePage> { | ||||
|               ?.clearSelected(); | ||||
|         }); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   void dispose() { | ||||
|     super.dispose(); | ||||
|     _linkSubscription?.cancel(); | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										16
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								pubspec.lock
									
									
									
									
									
								
							| @@ -42,6 +42,14 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.0.10" | ||||
|   app_links: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: app_links | ||||
|       sha256: "4e392b5eba997df356ca6021f28431ce1cfeb16758699553a94b13add874a3bb" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "3.5.0" | ||||
|   archive: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -350,6 +358,14 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "8.2.4" | ||||
|   gtk: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: gtk | ||||
|       sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.1.0" | ||||
|   hsluv: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|   | ||||
| @@ -67,6 +67,7 @@ dependencies: | ||||
|   connectivity_plus: ^5.0.0 | ||||
|   shared_storage: ^0.8.0 | ||||
|   crypto: ^3.0.3 | ||||
|   app_links: ^3.5.0 | ||||
|  | ||||
| dev_dependencies: | ||||
|   flutter_test: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user