Added Apps search

This commit is contained in:
Imran Remtulla
2022-09-24 01:57:45 -04:00
parent d03486fc5d
commit 2cfe62142a
7 changed files with 126 additions and 20 deletions

View File

@ -112,6 +112,7 @@ class GitHub implements AppSource {
} catch (e) { } catch (e) {
return "Invalid regular expression"; return "Invalid regular expression";
} }
return null;
} }
]) ])
] ]

View File

@ -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';

View File

@ -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});
}

View File

@ -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});

View File

@ -191,6 +191,7 @@ class _ImportExportPageState extends State<ImportExportPage> {
} }
} }
} }
return null;
} }
]) ])
] ]

View File

@ -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: [

View File

@ -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 {