favorites, demoting

This commit is contained in:
randogoth 2025-02-24 22:04:57 +02:00
parent a88a61cf01
commit dacfc1d059

View file

@ -55,7 +55,9 @@ class AppInfo {
final String name; final String name;
final String packageName; final String packageName;
final List<String> tags; final List<String> tags;
DateTime? lastUsed; // New field to track last used time DateTime? lastUsed;
bool isPinned;
bool isDemoted;
AppInfo({ AppInfo({
this.icon, this.icon,
@ -63,6 +65,8 @@ class AppInfo {
required this.packageName, required this.packageName,
required this.tags, required this.tags,
this.lastUsed, this.lastUsed,
this.isPinned = false,
this.isDemoted = false,
}); });
} }
@ -114,6 +118,9 @@ class _AppLauncherScreenState extends State<AppLauncherScreen> with WidgetsBindi
List<AppInfo> loadedApps = installedApps.map((app) { List<AppInfo> loadedApps = installedApps.map((app) {
final lastUsedMillis = SharedPreferencesService().getInt("lastUsed_${app.packageName}"); final lastUsedMillis = SharedPreferencesService().getInt("lastUsed_${app.packageName}");
final storedTags = SharedPreferencesService().getString("tags_${app.packageName}"); final storedTags = SharedPreferencesService().getString("tags_${app.packageName}");
final isPinned = SharedPreferencesService().getBool("isPinned_${app.packageName}", false);
final isDemoted = SharedPreferencesService().getBool("isDemoted_${app.packageName}", false);
List<String> tags = []; List<String> tags = [];
if (storedTags != null && storedTags.trim().isNotEmpty) { if (storedTags != null && storedTags.trim().isNotEmpty) {
@ -131,6 +138,8 @@ class _AppLauncherScreenState extends State<AppLauncherScreen> with WidgetsBindi
packageName: app.packageName, packageName: app.packageName,
tags: tags, tags: tags,
lastUsed: lastUsedMillis != null ? DateTime.fromMillisecondsSinceEpoch(lastUsedMillis) : null, lastUsed: lastUsedMillis != null ? DateTime.fromMillisecondsSinceEpoch(lastUsedMillis) : null,
isPinned: isPinned,
isDemoted: isDemoted,
); );
}).toList(); }).toList();
@ -151,16 +160,22 @@ class _AppLauncherScreenState extends State<AppLauncherScreen> with WidgetsBindi
return matchesName || matchesTag; return matchesName || matchesTag;
}).toList(); }).toList();
if (context.read<AppSettings>().sortLastUsed) { // Sort pinned apps first, then by last used or alphabetically, then demoted apps
filteredApps.sort((a, b) { filteredApps.sort((a, b) {
if (a.isPinned && !b.isPinned) return -1;
if (!a.isPinned && b.isPinned) return 1;
if (a.isDemoted && !b.isDemoted) return 1;
if (!a.isDemoted && b.isDemoted) return -1;
if (context.read<AppSettings>().sortLastUsed) {
DateTime aTime = a.lastUsed ?? DateTime.fromMillisecondsSinceEpoch(0); DateTime aTime = a.lastUsed ?? DateTime.fromMillisecondsSinceEpoch(0);
DateTime bTime = b.lastUsed ?? DateTime.fromMillisecondsSinceEpoch(0); DateTime bTime = b.lastUsed ?? DateTime.fromMillisecondsSinceEpoch(0);
return bTime.compareTo(aTime); return bTime.compareTo(aTime);
});
} else { } else {
filteredApps.sort((a, b) => a.name.compareTo(b.name)); return a.name.compareTo(b.name);
} }
}); });
});
} }
Future<void> _launchApp(AppInfo app) async { Future<void> _launchApp(AppInfo app) async {
@ -197,6 +212,57 @@ class _AppLauncherScreenState extends State<AppLauncherScreen> with WidgetsBindi
} }
} }
void _showContextMenu(BuildContext context, AppInfo app) {
showModalBottomSheet(
context: context,
builder: (context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
leading: Icon(app.isPinned ? Icons.push_pin : Icons.push_pin_outlined),
title: Text(app.isPinned ? 'Unpin' : 'Pin'),
onTap: () async {
Navigator.pop(context); // Close the menu
await _togglePin(app);
},
),
ListTile(
leading: Icon(app.isDemoted ? Icons.arrow_upward : Icons.arrow_downward),
title: Text(app.isDemoted ? 'Reset' : 'Demote'),
onTap: () async {
Navigator.pop(context); // Close the menu
await _toggleDemote(app);
},
),
],
);
},
);
}
Future<void> _togglePin(AppInfo app) async {
setState(() {
app.isPinned = !app.isPinned;
if (app.isPinned) {
app.isDemoted = false; // Cannot be pinned and demoted at the same time
}
});
await SharedPreferencesService().setBool("isPinned_${app.packageName}", app.isPinned);
_filterApps();
}
Future<void> _toggleDemote(AppInfo app) async {
setState(() {
app.isDemoted = !app.isDemoted;
if (app.isDemoted) {
app.isPinned = false; // Cannot be pinned and demoted at the same time
}
});
await SharedPreferencesService().setBool("isDemoted_${app.packageName}", app.isDemoted);
_filterApps();
}
void _editAppTags(AppInfo app) { void _editAppTags(AppInfo app) {
TextEditingController tagController = TextEditingController(text: app.tags.join(" ")); TextEditingController tagController = TextEditingController(text: app.tags.join(" "));
showDialog( showDialog(
@ -303,6 +369,15 @@ class _AppLauncherScreenState extends State<AppLauncherScreen> with WidgetsBindi
); );
} }
Widget _buildTrailingIcon(AppInfo app) {
if (app.isPinned) {
return Icon(Icons.push_pin, color: Colors.grey); // Pin icon for pinned apps
} else if (app.isDemoted) {
return Icon(Icons.disabled_by_default_outlined, color: Colors.grey); // Demote icon for demoted apps
}
return SizedBox.shrink(); // No icon for normal apps
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@ -452,11 +527,13 @@ class _AppLauncherScreenState extends State<AppLauncherScreen> with WidgetsBindi
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
) )
: null, : null,
trailing: _buildTrailingIcon(app), // Add trailing icon
onTap: () => _launchApp(app), onTap: () => _launchApp(app),
onLongPress: () => _showContextMenu(context, app),
), ),
); );
}, },
), )
); );
} }
} }