mirror of
				https://github.com/ImranR98/Obtainium.git
				synced 2025-10-30 21:13:28 +01:00 
			
		
		
		
	Added Apps search
This commit is contained in:
		| @@ -112,6 +112,7 @@ class GitHub implements AppSource { | |||||||
|               } catch (e) { |               } catch (e) { | ||||||
|                 return "Invalid regular expression"; |                 return "Invalid regular expression"; | ||||||
|               } |               } | ||||||
|  |               return null; | ||||||
|             } |             } | ||||||
|           ]) |           ]) | ||||||
|     ] |     ] | ||||||
|   | |||||||
| @@ -1,6 +1,5 @@ | |||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:flutter/services.dart'; | import 'package:flutter/services.dart'; | ||||||
| import 'package:obtainium/components/generated_form.dart'; |  | ||||||
| import 'package:obtainium/components/generated_form_modal.dart'; | import 'package:obtainium/components/generated_form_modal.dart'; | ||||||
| import 'package:obtainium/providers/apps_provider.dart'; | import 'package:obtainium/providers/apps_provider.dart'; | ||||||
| import 'package:obtainium/providers/settings_provider.dart'; | import 'package:obtainium/providers/settings_provider.dart'; | ||||||
|   | |||||||
| @@ -1,6 +1,8 @@ | |||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:flutter/services.dart'; | import 'package:flutter/services.dart'; | ||||||
| import 'package:obtainium/components/custom_app_bar.dart'; | import 'package:obtainium/components/custom_app_bar.dart'; | ||||||
|  | import 'package:obtainium/components/generated_form.dart'; | ||||||
|  | import 'package:obtainium/components/generated_form_modal.dart'; | ||||||
| import 'package:obtainium/pages/app.dart'; | import 'package:obtainium/pages/app.dart'; | ||||||
| import 'package:obtainium/providers/apps_provider.dart'; | import 'package:obtainium/providers/apps_provider.dart'; | ||||||
| import 'package:obtainium/providers/settings_provider.dart'; | import 'package:obtainium/providers/settings_provider.dart'; | ||||||
| @@ -14,12 +16,47 @@ class AppsPage extends StatefulWidget { | |||||||
| } | } | ||||||
|  |  | ||||||
| class _AppsPageState extends State<AppsPage> { | class _AppsPageState extends State<AppsPage> { | ||||||
|  |   AppsFilter? filter; | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context) { |   Widget build(BuildContext context) { | ||||||
|     var appsProvider = context.watch<AppsProvider>(); |     var appsProvider = context.watch<AppsProvider>(); | ||||||
|     var settingsProvider = context.watch<SettingsProvider>(); |     var settingsProvider = context.watch<SettingsProvider>(); | ||||||
|     var existingUpdateAppIds = appsProvider.getExistingUpdates(); |     var existingUpdateAppIds = appsProvider.getExistingUpdates(); | ||||||
|     var sortedApps = appsProvider.apps.values.toList(); |     var sortedApps = appsProvider.apps.values.toList(); | ||||||
|  |  | ||||||
|  |     if (filter != null) { | ||||||
|  |       sortedApps = sortedApps.where((app) { | ||||||
|  |         if (app.app.installedVersion == app.app.latestVersion && | ||||||
|  |             filter!.onlyNonLatest) { | ||||||
|  |           return false; | ||||||
|  |         } | ||||||
|  |         if (filter!.nameFilter.isEmpty && filter!.authorFilter.isEmpty) { | ||||||
|  |           return true; | ||||||
|  |         } | ||||||
|  |         List<String> nameTokens = filter!.nameFilter | ||||||
|  |             .split(' ') | ||||||
|  |             .where((element) => element.trim().isNotEmpty) | ||||||
|  |             .toList(); | ||||||
|  |         List<String> authorTokens = filter!.authorFilter | ||||||
|  |             .split(' ') | ||||||
|  |             .where((element) => element.trim().isNotEmpty) | ||||||
|  |             .toList(); | ||||||
|  |  | ||||||
|  |         for (var t in nameTokens) { | ||||||
|  |           if (!app.app.name.toLowerCase().contains(t.toLowerCase())) { | ||||||
|  |             return false; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |         for (var t in authorTokens) { | ||||||
|  |           if (!app.app.author.toLowerCase().contains(t.toLowerCase())) { | ||||||
|  |             return false; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |         return true; | ||||||
|  |       }).toList(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     sortedApps.sort((a, b) { |     sortedApps.sort((a, b) { | ||||||
|       int result = 0; |       int result = 0; | ||||||
|       if (settingsProvider.sortColumn == SortColumnSettings.authorName) { |       if (settingsProvider.sortColumn == SortColumnSettings.authorName) { | ||||||
| @@ -31,26 +68,83 @@ class _AppsPageState extends State<AppsPage> { | |||||||
|       } |       } | ||||||
|       return result; |       return result; | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     if (settingsProvider.sortOrder == SortOrderSettings.ascending) { |     if (settingsProvider.sortOrder == SortOrderSettings.ascending) { | ||||||
|       sortedApps = sortedApps.reversed.toList(); |       sortedApps = sortedApps.reversed.toList(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return Scaffold( |     return Scaffold( | ||||||
|         backgroundColor: Theme.of(context).colorScheme.surface, |         backgroundColor: Theme.of(context).colorScheme.surface, | ||||||
|         floatingActionButton: existingUpdateAppIds.isEmpty |         floatingActionButton: | ||||||
|             ? null |             Row(mainAxisAlignment: MainAxisAlignment.end, children: [ | ||||||
|             : ElevatedButton.icon( |           existingUpdateAppIds.isEmpty || filter != null | ||||||
|                 onPressed: appsProvider.areDownloadsRunning() |               ? const SizedBox() | ||||||
|                     ? null |               : ElevatedButton.icon( | ||||||
|                     : () { |                   onPressed: appsProvider.areDownloadsRunning() | ||||||
|                         HapticFeedback.heavyImpact(); |                       ? null | ||||||
|                         settingsProvider.getInstallPermission().then((_) { |                       : () { | ||||||
|                           appsProvider.downloadAndInstallLatestApp( |                           HapticFeedback.heavyImpact(); | ||||||
|                               existingUpdateAppIds, context); |                           settingsProvider.getInstallPermission().then((_) { | ||||||
|  |                             appsProvider.downloadAndInstallLatestApp( | ||||||
|  |                                 existingUpdateAppIds, context); | ||||||
|  |                           }); | ||||||
|  |                         }, | ||||||
|  |                   icon: const Icon(Icons.install_mobile_outlined), | ||||||
|  |                   label: const Text('Install All')), | ||||||
|  |           const SizedBox( | ||||||
|  |             width: 16, | ||||||
|  |           ), | ||||||
|  |           appsProvider.apps.isEmpty | ||||||
|  |               ? const SizedBox() | ||||||
|  |               : ElevatedButton.icon( | ||||||
|  |                   onPressed: () { | ||||||
|  |                     showDialog<List<String>?>( | ||||||
|  |                         context: context, | ||||||
|  |                         builder: (BuildContext ctx) { | ||||||
|  |                           return GeneratedFormModal( | ||||||
|  |                               title: 'Filter Apps', | ||||||
|  |                               items: [ | ||||||
|  |                                 [ | ||||||
|  |                                   GeneratedFormItem( | ||||||
|  |                                       label: "App Name", required: false), | ||||||
|  |                                   GeneratedFormItem( | ||||||
|  |                                       label: "Author", required: false) | ||||||
|  |                                 ], | ||||||
|  |                                 [ | ||||||
|  |                                   GeneratedFormItem( | ||||||
|  |                                       label: "Ignore Up-to-Date Apps", | ||||||
|  |                                       type: FormItemType.bool) | ||||||
|  |                                 ] | ||||||
|  |                               ], | ||||||
|  |                               defaultValues: filter == null | ||||||
|  |                                   ? [] | ||||||
|  |                                   : [ | ||||||
|  |                                       filter!.nameFilter, | ||||||
|  |                                       filter!.authorFilter, | ||||||
|  |                                       filter!.onlyNonLatest ? 'true' : '' | ||||||
|  |                                     ]); | ||||||
|  |                         }).then((values) { | ||||||
|  |                       if (values != null && | ||||||
|  |                           values | ||||||
|  |                               .where((element) => element.isNotEmpty) | ||||||
|  |                               .isNotEmpty) { | ||||||
|  |                         setState(() { | ||||||
|  |                           filter = AppsFilter( | ||||||
|  |                               nameFilter: values[0], | ||||||
|  |                               authorFilter: values[1], | ||||||
|  |                               onlyNonLatest: values[2] == "true"); | ||||||
|                         }); |                         }); | ||||||
|                       }, |                       } else { | ||||||
|                 icon: const Icon(Icons.install_mobile_outlined), |                         setState(() { | ||||||
|                 label: const Text('Install All')), |                           filter = null; | ||||||
|  |                         }); | ||||||
|  |                       } | ||||||
|  |                     }); | ||||||
|  |                   }, | ||||||
|  |                   label: Text(filter == null ? 'Search' : 'Modify Search'), | ||||||
|  |                   icon: Icon( | ||||||
|  |                       filter == null ? Icons.search : Icons.manage_search)), | ||||||
|  |         ]), | ||||||
|         body: RefreshIndicator( |         body: RefreshIndicator( | ||||||
|             onRefresh: () { |             onRefresh: () { | ||||||
|               HapticFeedback.lightImpact(); |               HapticFeedback.lightImpact(); | ||||||
| @@ -58,13 +152,15 @@ class _AppsPageState extends State<AppsPage> { | |||||||
|             }, |             }, | ||||||
|             child: CustomScrollView(slivers: <Widget>[ |             child: CustomScrollView(slivers: <Widget>[ | ||||||
|               const CustomAppBar(title: 'Apps'), |               const CustomAppBar(title: 'Apps'), | ||||||
|               if (appsProvider.loadingApps || appsProvider.apps.isEmpty) |               if (appsProvider.loadingApps || sortedApps.isEmpty) | ||||||
|                 SliverFillRemaining( |                 SliverFillRemaining( | ||||||
|                     child: Center( |                     child: Center( | ||||||
|                         child: appsProvider.loadingApps |                         child: appsProvider.loadingApps | ||||||
|                             ? const CircularProgressIndicator() |                             ? const CircularProgressIndicator() | ||||||
|                             : Text( |                             : Text( | ||||||
|                                 'No Apps', |                                 appsProvider.apps.isEmpty | ||||||
|  |                                     ? 'No Apps' | ||||||
|  |                                     : 'No Search Results', | ||||||
|                                 style: |                                 style: | ||||||
|                                     Theme.of(context).textTheme.headlineMedium, |                                     Theme.of(context).textTheme.headlineMedium, | ||||||
|                               ))), |                               ))), | ||||||
| @@ -97,3 +193,14 @@ class _AppsPageState extends State<AppsPage> { | |||||||
|             ]))); |             ]))); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | class AppsFilter { | ||||||
|  |   late String nameFilter; | ||||||
|  |   late String authorFilter; | ||||||
|  |   late bool onlyNonLatest; | ||||||
|  |  | ||||||
|  |   AppsFilter( | ||||||
|  |       {this.nameFilter = "", | ||||||
|  |       this.authorFilter = "", | ||||||
|  |       this.onlyNonLatest = false}); | ||||||
|  | } | ||||||
|   | |||||||
| @@ -5,7 +5,6 @@ import 'package:obtainium/pages/add_app.dart'; | |||||||
| import 'package:obtainium/pages/apps.dart'; | import 'package:obtainium/pages/apps.dart'; | ||||||
| import 'package:obtainium/pages/import_export.dart'; | import 'package:obtainium/pages/import_export.dart'; | ||||||
| import 'package:obtainium/pages/settings.dart'; | import 'package:obtainium/pages/settings.dart'; | ||||||
| import 'package:obtainium/pages/test_page.dart'; |  | ||||||
|  |  | ||||||
| class HomePage extends StatefulWidget { | class HomePage extends StatefulWidget { | ||||||
|   const HomePage({super.key}); |   const HomePage({super.key}); | ||||||
|   | |||||||
| @@ -191,6 +191,7 @@ class _ImportExportPageState extends State<ImportExportPage> { | |||||||
|                                                           } |                                                           } | ||||||
|                                                         } |                                                         } | ||||||
|                                                       } |                                                       } | ||||||
|  |                                                       return null; | ||||||
|                                                     } |                                                     } | ||||||
|                                                   ]) |                                                   ]) | ||||||
|                                             ] |                                             ] | ||||||
|   | |||||||
| @@ -27,7 +27,7 @@ class _SettingsPageState extends State<SettingsPage> { | |||||||
|               child: Padding( |               child: Padding( | ||||||
|                   padding: const EdgeInsets.all(16), |                   padding: const EdgeInsets.all(16), | ||||||
|                   child: settingsProvider.prefs == null |                   child: settingsProvider.prefs == null | ||||||
|                       ? Container() |                       ? const SizedBox() | ||||||
|                       : Column( |                       : Column( | ||||||
|                           crossAxisAlignment: CrossAxisAlignment.start, |                           crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|                           children: [ |                           children: [ | ||||||
|   | |||||||
| @@ -127,8 +127,7 @@ abstract class AppSource { | |||||||
|       String standardUrl, List<String> additionalData); |       String standardUrl, List<String> additionalData); | ||||||
|   AppNames getAppNames(String standardUrl); |   AppNames getAppNames(String standardUrl); | ||||||
|   late List<List<GeneratedFormItem>> additionalDataFormItems; |   late List<List<GeneratedFormItem>> additionalDataFormItems; | ||||||
|   late List<String> |   late List<String> additionalDataDefaults; | ||||||
|       additionalDataDefaults; // TODO: Make these integrate into generated form |  | ||||||
| } | } | ||||||
|  |  | ||||||
| abstract class MassAppSource { | abstract class MassAppSource { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user