mirror of
https://github.com/ImranR98/Obtainium.git
synced 2025-07-22 17:19:42 +02:00
Added apps list selection (actions incomplete)
This commit is contained in:
@@ -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 {
|
||||||
|
@@ -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))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user