Added apps list selection (actions incomplete)

This commit is contained in:
Imran Remtulla
2022-09-24 21:10:29 -04:00
parent 0a9373e65a
commit 45e5544c5b
3 changed files with 195 additions and 119 deletions

View File

@@ -99,7 +99,7 @@ class MyApp extends StatelessWidget {
if (settingsProvider.updateInterval > 0) { if (settingsProvider.updateInterval > 0) {
Workmanager().registerPeriodicTask('bg-update-check', 'bg-update-check', Workmanager().registerPeriodicTask('bg-update-check', 'bg-update-check',
frequency: Duration(minutes: settingsProvider.updateInterval), frequency: Duration(minutes: settingsProvider.updateInterval),
// initialDelay: Duration(minutes: settingsProvider.updateInterval), initialDelay: Duration(minutes: settingsProvider.updateInterval),
constraints: Constraints(networkType: NetworkType.connected), constraints: Constraints(networkType: NetworkType.connected),
existingWorkPolicy: ExistingWorkPolicy.replace); existingWorkPolicy: ExistingWorkPolicy.replace);
} else { } else {

View File

@@ -12,11 +12,32 @@ class AppsPage extends StatefulWidget {
const AppsPage({super.key}); const AppsPage({super.key});
@override @override
State<AppsPage> createState() => _AppsPageState(); State<AppsPage> createState() => AppsPageState();
} }
class _AppsPageState extends State<AppsPage> { class AppsPageState extends State<AppsPage> {
AppsFilter? filter; AppsFilter? filter;
Set<String> selectedIds = {};
clearSelected() {
if (selectedIds.isNotEmpty) {
setState(() {
selectedIds.clear();
});
return true;
}
return false;
}
selectThese(List<String> appIds) {
if (selectedIds.isEmpty) {
setState(() {
for (var a in appIds) {
selectedIds.add(a);
}
});
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@@ -25,6 +46,20 @@ class _AppsPageState extends State<AppsPage> {
var existingUpdateAppIds = appsProvider.getExistingUpdates(); var existingUpdateAppIds = appsProvider.getExistingUpdates();
var sortedApps = appsProvider.apps.values.toList(); var sortedApps = appsProvider.apps.values.toList();
selectedIds = selectedIds
.where((element) => sortedApps.map((e) => e.app.id).contains(element))
.toSet();
toggleAppSelected(String appId) {
setState(() {
if (selectedIds.contains(appId)) {
selectedIds.remove(appId);
} else {
selectedIds.add(appId);
}
});
}
if (filter != null) { if (filter != null) {
sortedApps = sortedApps.where((app) { sortedApps = sortedApps.where((app) {
if (app.app.installedVersion == app.app.latestVersion && if (app.app.installedVersion == app.app.latestVersion &&
@@ -74,126 +109,163 @@ class _AppsPageState extends State<AppsPage> {
} }
return Scaffold( return Scaffold(
backgroundColor: Theme.of(context).colorScheme.surface, backgroundColor: Theme.of(context).colorScheme.surface,
floatingActionButton: body: RefreshIndicator(
Row(mainAxisAlignment: MainAxisAlignment.end, children: [ onRefresh: () {
existingUpdateAppIds.isEmpty || filter != null HapticFeedback.lightImpact();
? const SizedBox() return appsProvider.checkUpdates().catchError((e) {
: ElevatedButton.icon( ScaffoldMessenger.of(context).showSnackBar(
onPressed: appsProvider.areDownloadsRunning() SnackBar(content: Text(e.toString())),
? null );
: () { });
HapticFeedback.heavyImpact(); },
settingsProvider.getInstallPermission().then((_) { child: CustomScrollView(slivers: <Widget>[
appsProvider.downloadAndInstallLatestApp( const CustomAppBar(title: 'Apps'),
existingUpdateAppIds, context); if (appsProvider.loadingApps || sortedApps.isEmpty)
}); SliverFillRemaining(
}, child: Center(
icon: const Icon(Icons.install_mobile_outlined), child: appsProvider.loadingApps
label: const Text('Install All')), ? const CircularProgressIndicator()
const SizedBox( : Text(
width: 16, appsProvider.apps.isEmpty
), ? 'No Apps'
appsProvider.apps.isEmpty : 'No Search Results',
? const SizedBox() style: Theme.of(context).textTheme.headlineMedium,
: ElevatedButton.icon( ))),
onPressed: () { SliverList(
showDialog<List<String>?>( delegate: SliverChildBuilderDelegate(
context: context, (BuildContext context, int index) {
builder: (BuildContext ctx) { return ListTile(
return GeneratedFormModal( selectedTileColor:
title: 'Filter Apps', Theme.of(context).colorScheme.primary.withOpacity(0.1),
items: [ selected: selectedIds.contains(sortedApps[index].app.id),
[ onLongPress: () {
GeneratedFormItem( toggleAppSelected(sortedApps[index].app.id);
label: "App Name", required: false), },
GeneratedFormItem( title: Text(sortedApps[index].app.name),
label: "Author", required: false) subtitle: Text('By ${sortedApps[index].app.author}'),
], trailing: sortedApps[index].downloadProgress != null
[ ? Text(
GeneratedFormItem( 'Downloading - ${sortedApps[index].downloadProgress?.toInt()}%')
label: "Ignore Up-to-Date Apps", : (sortedApps[index].app.installedVersion != null &&
type: FormItemType.bool) sortedApps[index].app.installedVersion !=
] sortedApps[index].app.latestVersion
], ? const Text('Update Available')
defaultValues: filter == null : Text(sortedApps[index].app.installedVersion ??
? [] 'Not Installed')),
: [ onTap: () {
filter!.nameFilter, if (selectedIds.isNotEmpty) {
filter!.authorFilter, toggleAppSelected(sortedApps[index].app.id);
filter!.onlyNonLatest ? 'true' : '' } else {
]);
}).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 {
setState(() {
filter = null;
});
}
});
},
label: Text(filter == null ? 'Search' : 'Modify Search'),
icon: Icon(
filter == null ? Icons.search : Icons.manage_search)),
]),
body: RefreshIndicator(
onRefresh: () {
HapticFeedback.lightImpact();
return appsProvider.checkUpdates().catchError((e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(e.toString())),
);
});
},
child: CustomScrollView(slivers: <Widget>[
const CustomAppBar(title: 'Apps'),
if (appsProvider.loadingApps || sortedApps.isEmpty)
SliverFillRemaining(
child: Center(
child: appsProvider.loadingApps
? const CircularProgressIndicator()
: Text(
appsProvider.apps.isEmpty
? 'No Apps'
: 'No Search Results',
style:
Theme.of(context).textTheme.headlineMedium,
))),
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return ListTile(
title: Text(sortedApps[index].app.name),
subtitle: Text('By ${sortedApps[index].app.author}'),
trailing: sortedApps[index].downloadProgress != null
? Text(
'Downloading - ${sortedApps[index].downloadProgress?.toInt()}%')
: (sortedApps[index].app.installedVersion != null &&
sortedApps[index].app.installedVersion !=
sortedApps[index].app.latestVersion
? const Text('Update Available')
: Text(sortedApps[index].app.installedVersion ??
'Not Installed')),
onTap: () {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) =>
AppPage(appId: sortedApps[index].app.id)), AppPage(appId: sortedApps[index].app.id)),
); );
}, }
); },
}, childCount: sortedApps.length)) );
]))); }, childCount: sortedApps.length))
])),
persistentFooterButtons: [
Row(
children: [
TextButton.icon(
onPressed: () {
selectedIds.isEmpty
? selectThese(sortedApps.map((e) => e.app.id).toList())
: clearSelected();
},
icon: Icon(selectedIds.isEmpty
? Icons.select_all_outlined
: Icons.deselect_outlined),
label: Text(selectedIds.isEmpty
? 'Select All'
: 'Deselect ${selectedIds.length.toString()}')),
const VerticalDivider(),
const Spacer(),
selectedIds.isEmpty
? const SizedBox()
: IconButton(
onPressed: () {
// TODO: Delete selected Apps after confirming
},
icon: const Icon(Icons.install_mobile_outlined)),
selectedIds.isEmpty
? const SizedBox()
: IconButton(
onPressed: () {
// TODO: Install selected Apps if they are not up to date after confirming (replace existing button)
},
icon: const Icon(Icons.delete_outline_rounded)),
existingUpdateAppIds.isEmpty || filter != null
? const SizedBox()
: IconButton(
onPressed: appsProvider.areDownloadsRunning()
? null
: () {
HapticFeedback.heavyImpact();
settingsProvider.getInstallPermission().then((_) {
appsProvider.downloadAndInstallLatestApp(
existingUpdateAppIds, context);
});
},
icon: const Icon(Icons.install_mobile_outlined),
),
appsProvider.apps.isEmpty
? const SizedBox()
: IconButton(
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 {
setState(() {
filter = null;
});
}
});
},
icon: Icon(
filter == null ? Icons.search : Icons.manage_search))
],
),
],
);
} }
} }

View File

@@ -25,7 +25,8 @@ class _HomePageState extends State<HomePage> {
List<int> selectedIndexHistory = []; List<int> selectedIndexHistory = [];
List<NavigationPageItem> pages = [ List<NavigationPageItem> pages = [
NavigationPageItem('Apps', Icons.apps, const AppsPage()), NavigationPageItem(
'Apps', Icons.apps, AppsPage(key: GlobalKey<AppsPageState>())),
NavigationPageItem('Add App', Icons.add, const AddAppPage()), NavigationPageItem('Add App', Icons.add, const AddAppPage()),
NavigationPageItem( NavigationPageItem(
'Import/Export', Icons.import_export, const ImportExportPage()), 'Import/Export', Icons.import_export, const ImportExportPage()),
@@ -88,7 +89,10 @@ class _HomePageState extends State<HomePage> {
}); });
return false; return false;
} }
return true; return !(pages[0].widget.key as GlobalKey<AppsPageState>)
.currentState
?.clearSelected();
// return !appsPageKey.currentState?.clearSelected();
}); });
} }
} }