better gestures, more settings, stability fixes
This commit is contained in:
parent
7a1395220c
commit
f005073ad5
4 changed files with 151 additions and 75 deletions
|
|
@ -6,7 +6,7 @@ plugins {
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace = "vision.flux.plasmoid"
|
namespace = "com.randogoth.plasmoid"
|
||||||
compileSdk = flutter.compileSdkVersion
|
compileSdk = flutter.compileSdkVersion
|
||||||
ndkVersion = flutter.ndkVersion
|
ndkVersion = flutter.ndkVersion
|
||||||
compileSdkVersion 36
|
compileSdkVersion 36
|
||||||
|
|
@ -22,7 +22,7 @@ android {
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||||
applicationId = "vision.flux.plasmoid"
|
applicationId = "com.randogoth.plasmoid"
|
||||||
// You can update the following values to match your application needs.
|
// You can update the following values to match your application needs.
|
||||||
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
||||||
minSdk = flutter.minSdkVersion
|
minSdk = flutter.minSdkVersion
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package vision.flux.plasmoid
|
package com.randogoth.plasmoid
|
||||||
|
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
|
|
||||||
208
lib/main.dart
208
lib/main.dart
|
|
@ -1,5 +1,4 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:typed_data';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:device_apps/device_apps.dart';
|
import 'package:device_apps/device_apps.dart';
|
||||||
|
|
@ -74,8 +73,10 @@ class AppInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
class AppLauncherScreen extends StatefulWidget {
|
class AppLauncherScreen extends StatefulWidget {
|
||||||
|
const AppLauncherScreen({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_AppLauncherScreenState createState() => _AppLauncherScreenState();
|
State<AppLauncherScreen> createState() => _AppLauncherScreenState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AppLauncherScreenState extends State<AppLauncherScreen> with WidgetsBindingObserver {
|
class _AppLauncherScreenState extends State<AppLauncherScreen> with WidgetsBindingObserver {
|
||||||
|
|
@ -84,6 +85,7 @@ class _AppLauncherScreenState extends State<AppLauncherScreen> with WidgetsBindi
|
||||||
List<AppInfo> apps = [];
|
List<AppInfo> apps = [];
|
||||||
List<AppInfo> filteredApps = [];
|
List<AppInfo> filteredApps = [];
|
||||||
bool isLoading = true;
|
bool isLoading = true;
|
||||||
|
bool _loadingApps = false;
|
||||||
StreamSubscription<ApplicationEvent>? _appsChangeSub;
|
StreamSubscription<ApplicationEvent>? _appsChangeSub;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -115,84 +117,93 @@ class _AppLauncherScreenState extends State<AppLauncherScreen> with WidgetsBindi
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadInstalledApps() async {
|
Future<void> _loadInstalledApps() async {
|
||||||
List<Application> installedApps = await DeviceApps.getInstalledApplications(
|
if (_loadingApps) return;
|
||||||
includeAppIcons: true,
|
_loadingApps = true;
|
||||||
onlyAppsWithLaunchIntent: true,
|
try {
|
||||||
includeSystemApps: true,
|
List<Application> installedApps = await DeviceApps.getInstalledApplications(
|
||||||
);
|
includeAppIcons: true,
|
||||||
|
onlyAppsWithLaunchIntent: true,
|
||||||
List<AppInfo> loadedApps = installedApps.map((app) {
|
includeSystemApps: true,
|
||||||
final lastUsedMillis = SharedPreferencesService().getInt("lastUsed_${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 = [];
|
|
||||||
|
|
||||||
if (storedTags != null && storedTags.trim().isNotEmpty) {
|
|
||||||
tags = storedTags.split(" ");
|
|
||||||
} else if (app.category != null) {
|
|
||||||
String categoryStr = app.category.toString().split('.').last;
|
|
||||||
if (categoryStr.toLowerCase() != 'undefined') {
|
|
||||||
tags.add('@${categoryStr.toLowerCase()}');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return AppInfo(
|
|
||||||
icon: app is ApplicationWithIcon ? app.icon : null,
|
|
||||||
name: app.appName,
|
|
||||||
packageName: app.packageName,
|
|
||||||
tags: tags,
|
|
||||||
lastUsed: lastUsedMillis != null ? DateTime.fromMillisecondsSinceEpoch(lastUsedMillis) : null,
|
|
||||||
isPinned: isPinned,
|
|
||||||
isDemoted: isDemoted,
|
|
||||||
);
|
);
|
||||||
}).toList();
|
|
||||||
|
|
||||||
setState(() {
|
if (!mounted) return;
|
||||||
apps = loadedApps;
|
|
||||||
filteredApps = apps;
|
List<AppInfo> loadedApps = installedApps.map((app) {
|
||||||
isLoading = false;
|
final lastUsedMillis = SharedPreferencesService().getInt("lastUsed_${app.packageName}");
|
||||||
});
|
final storedTags = SharedPreferencesService().getString("tags_${app.packageName}");
|
||||||
_filterApps();
|
final isPinned = SharedPreferencesService().getBool("isPinned_${app.packageName}", false);
|
||||||
|
final isDemoted = SharedPreferencesService().getBool("isDemoted_${app.packageName}", false);
|
||||||
|
|
||||||
|
List<String> tags = [];
|
||||||
|
|
||||||
|
if (storedTags != null && storedTags.trim().isNotEmpty) {
|
||||||
|
tags = storedTags.split(" ");
|
||||||
|
} else {
|
||||||
|
String categoryStr = app.category.toString().split('.').last;
|
||||||
|
if (categoryStr.toLowerCase() != 'undefined') {
|
||||||
|
tags.add('@${categoryStr.toLowerCase()}');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return AppInfo(
|
||||||
|
icon: app is ApplicationWithIcon ? app.icon : null,
|
||||||
|
name: app.appName,
|
||||||
|
packageName: app.packageName,
|
||||||
|
tags: tags,
|
||||||
|
lastUsed: lastUsedMillis != null ? DateTime.fromMillisecondsSinceEpoch(lastUsedMillis) : null,
|
||||||
|
isPinned: isPinned,
|
||||||
|
isDemoted: isDemoted,
|
||||||
|
);
|
||||||
|
}).toList();
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
apps = loadedApps;
|
||||||
|
isLoading = false;
|
||||||
|
});
|
||||||
|
_filterApps();
|
||||||
|
} finally {
|
||||||
|
_loadingApps = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _filterApps() {
|
void _filterApps() {
|
||||||
String query = searchController.text.toLowerCase();
|
String query = searchController.text.toLowerCase();
|
||||||
setState(() {
|
setState(() {
|
||||||
|
final hideDemoted = context.read<AppSettings>().hideDemoted;
|
||||||
filteredApps = apps.where((app) {
|
filteredApps = apps.where((app) {
|
||||||
|
if (hideDemoted && app.isDemoted) return false;
|
||||||
bool matchesName = app.name.toLowerCase().contains(query);
|
bool matchesName = app.name.toLowerCase().contains(query);
|
||||||
bool matchesTag = app.tags.any((tag) => tag.toLowerCase().contains(query));
|
bool matchesTag = app.tags.any((tag) => tag.toLowerCase().contains(query));
|
||||||
return matchesName || matchesTag;
|
return matchesName || matchesTag;
|
||||||
}).toList();
|
}).toList();
|
||||||
|
|
||||||
// Sort pinned apps first, then by last used or alphabetically, then demoted apps
|
// Sort pinned apps first, then by last used or alphabetically, then demoted apps
|
||||||
|
final sortLastUsed = context.read<AppSettings>().sortLastUsed;
|
||||||
filteredApps.sort((a, b) {
|
filteredApps.sort((a, b) {
|
||||||
if (a.isPinned && !b.isPinned) return -1;
|
if (a.isPinned && !b.isPinned) return -1;
|
||||||
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 (!a.isDemoted && b.isDemoted) return -1;
|
if (!a.isDemoted && b.isDemoted) return -1;
|
||||||
|
|
||||||
if (context.read<AppSettings>().sortLastUsed) {
|
if (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);
|
final timeCompare = bTime.compareTo(aTime);
|
||||||
} else {
|
if (timeCompare != 0) return timeCompare;
|
||||||
return a.name.compareTo(b.name);
|
|
||||||
}
|
}
|
||||||
|
return a.name.compareTo(b.name);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _launchApp(AppInfo app) async {
|
Future<void> _launchApp(AppInfo app) async {
|
||||||
bool launched = await DeviceApps.openApp(app.packageName);
|
bool launched = await DeviceApps.openApp(app.packageName);
|
||||||
|
if (!mounted) return;
|
||||||
if (launched) {
|
if (launched) {
|
||||||
DateTime now = DateTime.now();
|
DateTime now = DateTime.now();
|
||||||
await SharedPreferencesService().setInt("lastUsed_${app.packageName}", now.millisecondsSinceEpoch);
|
await SharedPreferencesService().setInt("lastUsed_${app.packageName}", now.millisecondsSinceEpoch);
|
||||||
setState(() {
|
setState(() { app.lastUsed = now; });
|
||||||
app.lastUsed = now;
|
_filterApps();
|
||||||
_filterApps();
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text('Could not launch ${app.name}')),
|
SnackBar(content: Text('Could not launch ${app.name}')),
|
||||||
|
|
@ -201,16 +212,17 @@ class _AppLauncherScreenState extends State<AppLauncherScreen> with WidgetsBindi
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> _handleDismiss(DismissDirection direction, AppInfo app) async {
|
Future<bool> _handleDismiss(DismissDirection direction, AppInfo app) async {
|
||||||
if (direction == DismissDirection.endToStart) {
|
if (direction == DismissDirection.startToEnd) {
|
||||||
_openAppInfo(app);
|
await (app.isDemoted ? _toggleDemote(app) : _togglePin(app));
|
||||||
} else if (direction == DismissDirection.startToEnd) {
|
} else if (direction == DismissDirection.endToStart) {
|
||||||
_editAppTags(app);
|
await (app.isPinned ? _togglePin(app) : _toggleDemote(app));
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _openAppInfo(AppInfo app) async {
|
Future<void> _openAppInfo(AppInfo app) async {
|
||||||
bool launched = await DeviceApps.openAppSettings(app.packageName);
|
bool launched = await DeviceApps.openAppSettings(app.packageName);
|
||||||
|
if (!mounted) return;
|
||||||
if (!launched) {
|
if (!launched) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text('Could not launch ${app.name} Settings')),
|
SnackBar(content: Text('Could not launch ${app.name} Settings')),
|
||||||
|
|
@ -226,19 +238,19 @@ class _AppLauncherScreenState extends State<AppLauncherScreen> with WidgetsBindi
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: Icon(app.isPinned ? Icons.push_pin : Icons.push_pin_outlined),
|
leading: Icon(Icons.edit),
|
||||||
title: Text(app.isPinned ? 'Unpin' : 'Pin'),
|
title: Text('Edit Tags'),
|
||||||
onTap: () async {
|
onTap: () {
|
||||||
Navigator.pop(context); // Close the menu
|
Navigator.pop(context);
|
||||||
await _togglePin(app);
|
_editAppTags(app);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: Icon(app.isDemoted ? Icons.arrow_upward : Icons.arrow_downward),
|
leading: Icon(Icons.info_outline),
|
||||||
title: Text(app.isDemoted ? 'Reset' : 'Demote'),
|
title: Text('App Info'),
|
||||||
onTap: () async {
|
onTap: () {
|
||||||
Navigator.pop(context); // Close the menu
|
Navigator.pop(context);
|
||||||
await _toggleDemote(app);
|
_openAppInfo(app);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -255,6 +267,9 @@ class _AppLauncherScreenState extends State<AppLauncherScreen> with WidgetsBindi
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
await SharedPreferencesService().setBool("isPinned_${app.packageName}", app.isPinned);
|
await SharedPreferencesService().setBool("isPinned_${app.packageName}", app.isPinned);
|
||||||
|
if (app.isPinned) { // pinning clears demote
|
||||||
|
await SharedPreferencesService().setBool("isDemoted_${app.packageName}", false);
|
||||||
|
}
|
||||||
_filterApps();
|
_filterApps();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -266,6 +281,9 @@ class _AppLauncherScreenState extends State<AppLauncherScreen> with WidgetsBindi
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
await SharedPreferencesService().setBool("isDemoted_${app.packageName}", app.isDemoted);
|
await SharedPreferencesService().setBool("isDemoted_${app.packageName}", app.isDemoted);
|
||||||
|
if (app.isDemoted) { // demoting clears pin
|
||||||
|
await SharedPreferencesService().setBool("isPinned_${app.packageName}", false);
|
||||||
|
}
|
||||||
_filterApps();
|
_filterApps();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -290,6 +308,7 @@ class _AppLauncherScreenState extends State<AppLauncherScreen> with WidgetsBindi
|
||||||
..addAll(tagController.text.split(" ").where((t) => t.isNotEmpty));
|
..addAll(tagController.text.split(" ").where((t) => t.isNotEmpty));
|
||||||
});
|
});
|
||||||
await SharedPreferencesService().setString("tags_${app.packageName}", app.tags.join(" "));
|
await SharedPreferencesService().setString("tags_${app.packageName}", app.tags.join(" "));
|
||||||
|
if (!mounted) return;
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
_filterApps();
|
_filterApps();
|
||||||
},
|
},
|
||||||
|
|
@ -376,12 +395,13 @@ class _AppLauncherScreenState extends State<AppLauncherScreen> with WidgetsBindi
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildTrailingIcon(AppInfo app) {
|
Widget _buildTrailingIcon(AppInfo app) {
|
||||||
|
if (!context.read<AppSettings>().showStatusIcons) return SizedBox.shrink();
|
||||||
if (app.isPinned) {
|
if (app.isPinned) {
|
||||||
return Icon(Icons.push_pin, color: Colors.grey); // Pin icon for pinned apps
|
return Icon(Icons.push_pin, color: Colors.grey);
|
||||||
} else if (app.isDemoted) {
|
} else if (app.isDemoted) {
|
||||||
return Icon(Icons.disabled_by_default_outlined, color: Colors.grey); // Demote icon for demoted apps
|
return Icon(Icons.arrow_downward, color: Colors.grey);
|
||||||
}
|
}
|
||||||
return SizedBox.shrink(); // No icon for normal apps
|
return SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -416,6 +436,9 @@ class _AppLauncherScreenState extends State<AppLauncherScreen> with WidgetsBindi
|
||||||
onSelected: (String value) async {
|
onSelected: (String value) async {
|
||||||
final settings = context.read<AppSettings>();
|
final settings = context.read<AppSettings>();
|
||||||
switch (value) {
|
switch (value) {
|
||||||
|
case 'Refresh':
|
||||||
|
_loadInstalledApps();
|
||||||
|
break;
|
||||||
case 'Toggle Tags':
|
case 'Toggle Tags':
|
||||||
settings.updateSetting('showTags', !settings.showTags);
|
settings.updateSetting('showTags', !settings.showTags);
|
||||||
break;
|
break;
|
||||||
|
|
@ -436,6 +459,13 @@ class _AppLauncherScreenState extends State<AppLauncherScreen> with WidgetsBindi
|
||||||
settings.updateSetting('autoFocus', !settings.autoFocus);
|
settings.updateSetting('autoFocus', !settings.autoFocus);
|
||||||
if (!settings.autoFocus) FocusScope.of(context).unfocus();
|
if (!settings.autoFocus) FocusScope.of(context).unfocus();
|
||||||
break;
|
break;
|
||||||
|
case 'Toggle StatusIcons':
|
||||||
|
settings.updateSetting('showStatusIcons', !settings.showStatusIcons);
|
||||||
|
break;
|
||||||
|
case 'Toggle HideDemoted':
|
||||||
|
settings.updateSetting('hideDemoted', !settings.hideDemoted);
|
||||||
|
_filterApps();
|
||||||
|
break;
|
||||||
case 'About':
|
case 'About':
|
||||||
_showInfoPage(context, "About", aboutContent);
|
_showInfoPage(context, "About", aboutContent);
|
||||||
break;
|
break;
|
||||||
|
|
@ -444,6 +474,13 @@ class _AppLauncherScreenState extends State<AppLauncherScreen> with WidgetsBindi
|
||||||
itemBuilder: (BuildContext context) {
|
itemBuilder: (BuildContext context) {
|
||||||
final settings = context.read<AppSettings>();
|
final settings = context.read<AppSettings>();
|
||||||
return [
|
return [
|
||||||
|
PopupMenuItem<String>(
|
||||||
|
value: 'Refresh',
|
||||||
|
child: ListTile(
|
||||||
|
leading: Icon(Icons.refresh),
|
||||||
|
title: Text('Refresh App List'),
|
||||||
|
),
|
||||||
|
),
|
||||||
PopupMenuItem<String>(
|
PopupMenuItem<String>(
|
||||||
value: 'Toggle Tags',
|
value: 'Toggle Tags',
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
|
|
@ -458,6 +495,20 @@ class _AppLauncherScreenState extends State<AppLauncherScreen> with WidgetsBindi
|
||||||
title: Text(settings.showIcons ? 'Hide Icons' : 'Show Icons'),
|
title: Text(settings.showIcons ? 'Hide Icons' : 'Show Icons'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
PopupMenuItem<String>(
|
||||||
|
value: 'Toggle StatusIcons',
|
||||||
|
child: ListTile(
|
||||||
|
leading: Icon(settings.showStatusIcons ? Icons.label_off_outlined : Icons.label_outlined),
|
||||||
|
title: Text(settings.showStatusIcons ? 'Hide Status Icons' : 'Show Status Icons'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
PopupMenuItem<String>(
|
||||||
|
value: 'Toggle HideDemoted',
|
||||||
|
child: ListTile(
|
||||||
|
leading: Icon(settings.hideDemoted ? Icons.visibility : Icons.visibility_off),
|
||||||
|
title: Text(settings.hideDemoted ? 'Show Demoted Apps' : 'Hide Demoted Apps'),
|
||||||
|
),
|
||||||
|
),
|
||||||
PopupMenuItem<String>(
|
PopupMenuItem<String>(
|
||||||
value: 'Toggle Color Icons',
|
value: 'Toggle Color Icons',
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
|
|
@ -508,18 +559,29 @@ class _AppLauncherScreenState extends State<AppLauncherScreen> with WidgetsBindi
|
||||||
final app = filteredApps[index];
|
final app = filteredApps[index];
|
||||||
return Dismissible(
|
return Dismissible(
|
||||||
key: Key(app.packageName),
|
key: Key(app.packageName),
|
||||||
|
direction: app.isPinned
|
||||||
|
? DismissDirection.endToStart
|
||||||
|
: app.isDemoted
|
||||||
|
? DismissDirection.startToEnd
|
||||||
|
: DismissDirection.horizontal,
|
||||||
confirmDismiss: (direction) => _handleDismiss(direction, app),
|
confirmDismiss: (direction) => _handleDismiss(direction, app),
|
||||||
background: Container(
|
background: Container(
|
||||||
color: Colors.orange,
|
color: app.isDemoted ? Colors.grey : Colors.teal,
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
padding: EdgeInsets.only(left: 20.0),
|
padding: EdgeInsets.only(left: 20.0),
|
||||||
child: Icon(Icons.edit, color: Colors.white),
|
child: Icon(
|
||||||
|
app.isDemoted ? Icons.arrow_upward : Icons.push_pin,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
secondaryBackground: Container(
|
secondaryBackground: Container(
|
||||||
color: Colors.green,
|
color: app.isPinned ? Colors.grey : Colors.deepOrange,
|
||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
padding: EdgeInsets.only(right: 20.0),
|
padding: EdgeInsets.only(right: 20.0),
|
||||||
child: Icon(Icons.info, color: Colors.white),
|
child: Icon(
|
||||||
|
app.isPinned ? Icons.push_pin_outlined : Icons.arrow_downward,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
leading: context.read<AppSettings>().showIcons ? _buildAppIcon(app) : null,
|
leading: context.read<AppSettings>().showIcons ? _buildAppIcon(app) : null,
|
||||||
|
|
@ -554,12 +616,14 @@ Tap an app to launch it immediately.
|
||||||
|
|
||||||
## Tagging:
|
## Tagging:
|
||||||
|
|
||||||
Swipe right on any item to edit its tags. For shared files, a tag dialog appears immediately upon sharing.
|
Long-press an app and choose **Edit Tags** to assign tags.
|
||||||
Tags help you organize items and are searchable using prefixes like @context, #general, or +project.
|
Tags help you organize items and are searchable using prefixes like @context, #general, or +project.
|
||||||
|
|
||||||
## Gestures & Settings:
|
## Gestures & Settings:
|
||||||
|
|
||||||
Swipe left on an app to view its settings.
|
Swipe right on an app to pin it to the top of the list (swipe right again to unpin).
|
||||||
|
Swipe left to demote it to the bottom (swipe left again to restore).
|
||||||
|
Long-press an app to edit its tags or open its system settings page.
|
||||||
Use the menu button (plasmoid) to toggle settings such as icon visibility, color mode, theme, sorting (alphabetically or by last used), and auto-focus.
|
Use the menu button (plasmoid) to toggle settings such as icon visibility, color mode, theme, sorting (alphabetically or by last used), and auto-focus.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@ class AppSettings extends ChangeNotifier {
|
||||||
bool _darkTheme = false;
|
bool _darkTheme = false;
|
||||||
bool _sortLastUsed = true;
|
bool _sortLastUsed = true;
|
||||||
bool _autoFocus = true;
|
bool _autoFocus = true;
|
||||||
|
bool _showStatusIcons = true;
|
||||||
|
bool _hideDemoted = false;
|
||||||
|
|
||||||
bool get showTags => _showTags;
|
bool get showTags => _showTags;
|
||||||
bool get showIcons => _showIcons;
|
bool get showIcons => _showIcons;
|
||||||
|
|
@ -15,6 +17,8 @@ class AppSettings extends ChangeNotifier {
|
||||||
bool get darkTheme => _darkTheme;
|
bool get darkTheme => _darkTheme;
|
||||||
bool get sortLastUsed => _sortLastUsed;
|
bool get sortLastUsed => _sortLastUsed;
|
||||||
bool get autoFocus => _autoFocus;
|
bool get autoFocus => _autoFocus;
|
||||||
|
bool get showStatusIcons => _showStatusIcons;
|
||||||
|
bool get hideDemoted => _hideDemoted;
|
||||||
|
|
||||||
final SharedPreferencesService _prefsService = SharedPreferencesService();
|
final SharedPreferencesService _prefsService = SharedPreferencesService();
|
||||||
|
|
||||||
|
|
@ -29,6 +33,8 @@ class AppSettings extends ChangeNotifier {
|
||||||
_darkTheme = _prefsService.getBool('darkTheme', false);
|
_darkTheme = _prefsService.getBool('darkTheme', false);
|
||||||
_sortLastUsed = _prefsService.getBool('sortLastUsed', true);
|
_sortLastUsed = _prefsService.getBool('sortLastUsed', true);
|
||||||
_autoFocus = _prefsService.getBool('autoFocus', true);
|
_autoFocus = _prefsService.getBool('autoFocus', true);
|
||||||
|
_showStatusIcons = _prefsService.getBool('showStatusIcons', true);
|
||||||
|
_hideDemoted = _prefsService.getBool('hideDemoted', false);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,6 +58,12 @@ class AppSettings extends ChangeNotifier {
|
||||||
case 'autoFocus':
|
case 'autoFocus':
|
||||||
_autoFocus = value;
|
_autoFocus = value;
|
||||||
break;
|
break;
|
||||||
|
case 'showStatusIcons':
|
||||||
|
_showStatusIcons = value;
|
||||||
|
break;
|
||||||
|
case 'hideDemoted':
|
||||||
|
_hideDemoted = value;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
await _prefsService.setBool(key, value);
|
await _prefsService.setBool(key, value);
|
||||||
notifyListeners(); // Notify listeners after updating the setting
|
notifyListeners(); // Notify listeners after updating the setting
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue