diff --git a/assets/translations/de.json b/assets/translations/de.json index 90f4ca9..0662300 100644 --- a/assets/translations/de.json +++ b/assets/translations/de.json @@ -220,6 +220,7 @@ "importFromURLsInFile": "Importieren von URLs aus Datei ( z.B. OPML)", "versionDetection": "Versionserkennung", "standardVersionDetection": "Standardversionserkennung", + "groupByCategory": "Group by Category", "removeAppQuestion": { "one": "App entfernen?", "other": "App entfernen?" diff --git a/assets/translations/en.json b/assets/translations/en.json index a48c185..9296a27 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -220,6 +220,7 @@ "importFromURLsInFile": "Import from URLs in File (like OPML)", "versionDetection": "Version Detection", "standardVersionDetection": "Standard version detection", + "groupByCategory": "Group by Category", "removeAppQuestion": { "one": "Remove App?", "other": "Remove Apps?" diff --git a/assets/translations/fa.json b/assets/translations/fa.json index eb16617..969d2b1 100644 --- a/assets/translations/fa.json +++ b/assets/translations/fa.json @@ -220,6 +220,7 @@ "importFromURLsInFile": "وارد کردن از آدرس های اینترنتی موجود در فایل (مانند OPML)", "versionDetection": "تشخیص نسخه", "standardVersionDetection": "تشخیص نسخه استاندارد", + "groupByCategory": "Group by Category", "removeAppQuestion": { "one": "برنامه حذف شود؟", "other": "برنامه ها حذف شوند؟" diff --git a/assets/translations/fr.json b/assets/translations/fr.json index e4d99f8..d159d88 100644 --- a/assets/translations/fr.json +++ b/assets/translations/fr.json @@ -220,6 +220,7 @@ "importFromURLsInFile": "Importer à partir d'URL dans un fichier (comme OPML)", "versionDetection": "Détection des versions", "standardVersionDetection": "Détection de version standard", + "groupByCategory": "Group by Category", "removeAppQuestion": { "one": "Supprimer l'application ?", "other": "Supprimer les applications ?" diff --git a/assets/translations/hu.json b/assets/translations/hu.json index c157ef7..638a74a 100644 --- a/assets/translations/hu.json +++ b/assets/translations/hu.json @@ -219,6 +219,7 @@ "importFromURLsInFile": "Importálás fájlban található URL-ből (mint pl. OPML)", "versionDetection": "Verzió érzékelés", "standardVersionDetection": "Alapért. verzió érzékelés", + "groupByCategory": "Group by Category", "removeAppQuestion": { "one": "Eltávolítja az alkalmazást?", "other": "Eltávolítja az alkalmazást?" diff --git a/assets/translations/it.json b/assets/translations/it.json index 9363303..c3e15cc 100644 --- a/assets/translations/it.json +++ b/assets/translations/it.json @@ -220,6 +220,7 @@ "importFromURLsInFile": "Importa da URL in file (come OPML)", "versionDetection": "Rilevamento di versione", "standardVersionDetection": "Rilevamento di versione standard", + "groupByCategory": "Group by Category", "removeAppQuestion": { "one": "Rimuovere l'App?", "other": "Rimuovere le App?" diff --git a/assets/translations/ja.json b/assets/translations/ja.json index 58ba851..40cd428 100644 --- a/assets/translations/ja.json +++ b/assets/translations/ja.json @@ -220,6 +220,7 @@ "importFromURLsInFile": "ファイル(OPMLなど)内のURLからインポート", "versionDetection": "バージョン検出", "standardVersionDetection": "標準のバージョン検出", + "groupByCategory": "Group by Category", "removeAppQuestion": { "one": "アプリを削除しますか?", "other": "アプリを削除しますか?" diff --git a/assets/translations/zh.json b/assets/translations/zh.json index 4f892ae..466ed23 100644 --- a/assets/translations/zh.json +++ b/assets/translations/zh.json @@ -220,6 +220,7 @@ "importFromURLsInFile": "Import from URLs in File (like OPML)", "versionDetection": "Version Detection", "standardVersionDetection": "Standard version detection", + "groupByCategory": "Group by Category", "removeAppQuestion": { "one": "删除应用?", "other": "删除应用?" diff --git a/lib/main.dart b/lib/main.dart index 02ce25a..e9251aa 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -21,7 +21,7 @@ import 'package:easy_localization/src/easy_localization_controller.dart'; // ignore: implementation_imports import 'package:easy_localization/src/localization.dart'; -const String currentVersion = '0.11.16'; +const String currentVersion = '0.11.17'; const String currentReleaseTag = 'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES diff --git a/lib/pages/apps.dart b/lib/pages/apps.dart index d8b59f6..616b065 100644 --- a/lib/pages/apps.dart +++ b/lib/pages/apps.dart @@ -187,6 +187,25 @@ class AppsPageState extends State { } listedApps = [...tempPinned, ...tempNotPinned]; + List getListedCategories() { + var temp = listedApps + .map((e) => e.app.categories.isNotEmpty ? e.app.categories : [null]); + return temp.isNotEmpty + ? { + ...temp.reduce((v, e) => [...v, ...e]) + }.toList() + : []; + } + + var listedCategories = getListedCategories(); + listedCategories.sort((a, b) { + return a != null && b != null + ? a.compareTo(b) + : a == null + ? 1 + : -1; + }); + showChangeLogDialog( String? changesUrl, AppSource appSource, String changeLog, int index) { showDialog( @@ -402,17 +421,37 @@ class AppsPageState extends State { ], ); - var transparent = const Color.fromARGB(0, 0, 0, 0).value; + var transparent = + Theme.of(context).colorScheme.background.withAlpha(0).value; + List stops = [ + ...listedApps[index] + .app + .categories + .asMap() + .entries + .map((e) => + ((e.key / (listedApps[index].app.categories.length - 1)))) + .toList(), + 1 + ]; + if (stops.length == 2) { + stops[0] = 1; + } return Container( decoration: BoxDecoration( - border: Border.symmetric( - vertical: BorderSide( - width: 4, - color: Color(listedApps[index].app.categories.isNotEmpty - ? settingsProvider.categories[ - listedApps[index].app.categories.first] ?? - transparent - : transparent)))), + gradient: LinearGradient( + stops: stops, + begin: const Alignment(-1, 0), + end: const Alignment(-0.97, 0), + colors: [ + ...listedApps[index] + .app + .categories + .map((e) => + Color(settingsProvider.categories[e]!).withAlpha(255)) + .toList(), + Color(transparent) + ])), child: ListTile( tileColor: listedApps[index].app.pinned ? Colors.grey.withOpacity(0.1) @@ -465,6 +504,28 @@ class AppsPageState extends State { )); } + getCategoryCollapsibleTile(int index) { + var tiles = listedApps + .asMap() + .entries + .where((e) => + e.value.app.categories.contains(listedCategories[index]) || + e.value.app.categories.isEmpty && listedCategories[index] == null) + .map((e) => getSingleAppHorizTile(e.key)) + .toList(); + + capFirstChar(String str) => str[0].toUpperCase() + str.substring(1); + return ExpansionTile( + initiallyExpanded: true, + title: Text( + capFirstChar(listedCategories[index] ?? tr('noCategory')), + style: const TextStyle(fontWeight: FontWeight.bold), + ), + controlAffinity: ListTileControlAffinity.leading, + trailing: Text(tiles.length.toString()), + children: tiles); + } + getSelectAllButton() { return selectedApps.isEmpty ? TextButton.icon( @@ -903,6 +964,22 @@ class AppsPageState extends State { ); } + getDisplayedList() { + return settingsProvider.groupByCategory && + !(listedCategories.isEmpty || + (listedCategories.length == 1 && listedCategories[0] == null)) + ? SliverList( + delegate: + SliverChildBuilderDelegate((BuildContext context, int index) { + return getCategoryCollapsibleTile(index); + }, childCount: listedCategories.length)) + : SliverList( + delegate: + SliverChildBuilderDelegate((BuildContext context, int index) { + return getSingleAppHorizTile(index); + }, childCount: listedApps.length)); + } + return Scaffold( backgroundColor: Theme.of(context).colorScheme.surface, body: RefreshIndicator( @@ -922,11 +999,7 @@ class AppsPageState extends State { child: CustomScrollView(slivers: [ CustomAppBar(title: tr('appsString')), ...getLoadingWidgets(), - SliverList( - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - return getSingleAppHorizTile(index); - }, childCount: listedApps.length)) + getDisplayedList() ])), persistentFooterButtons: appsProvider.apps.isEmpty ? null diff --git a/lib/pages/settings.dart b/lib/pages/settings.dart index 6e707e4..6888f7a 100644 --- a/lib/pages/settings.dart +++ b/lib/pages/settings.dart @@ -262,6 +262,18 @@ class _SettingsPageState extends State { }) ], ), + height16, + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(tr('groupByCategory')), + Switch( + value: settingsProvider.groupByCategory, + onChanged: (value) { + settingsProvider.groupByCategory = value; + }) + ], + ), const Divider( height: 16, ), diff --git a/lib/providers/settings_provider.dart b/lib/providers/settings_provider.dart index a7c6bc1..9f0d8f9 100644 --- a/lib/providers/settings_provider.dart +++ b/lib/providers/settings_provider.dart @@ -139,6 +139,15 @@ class SettingsProvider with ChangeNotifier { notifyListeners(); } + bool get groupByCategory { + return prefs?.getBool('groupByCategory') ?? false; + } + + set groupByCategory(bool show) { + prefs?.setBool('groupByCategory', show); + notifyListeners(); + } + String? getSettingString(String settingId) { return prefs?.getString(settingId); } diff --git a/pubspec.yaml b/pubspec.yaml index 82a25b7..eca32fa 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,7 +17,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 0.11.16+138 # When changing this, update the tag in main() accordingly +version: 0.11.17+139 # When changing this, update the tag in main() accordingly environment: sdk: '>=2.18.2 <3.0.0'