mirror of
				https://github.com/ImranR98/Obtainium.git
				synced 2025-10-30 21:13:28 +01:00 
			
		
		
		
	Merge pull request #177 from ImranR98/dev
Added very basic categorization support
This commit is contained in:
		| @@ -199,6 +199,14 @@ | ||||
|     "downloadNotifDescription": "Notifies the user of the progress in downloading an App", | ||||
|     "noAPKFound": "No APK found", | ||||
|     "noVersionDetection": "No version detection", | ||||
|     "categorize": "Categorize", | ||||
|     "categories": "Categories", | ||||
|     "category": "Category", | ||||
|     "noCategory": "No Category", | ||||
|     "deleteCategoryQuestion": "Delete Category?", | ||||
|     "categoryDeleteWarning": "All Apps in {} will be set to uncategorized.", | ||||
|     "addCategory": "Add Category", | ||||
|     "label": "Label", | ||||
|     "tooManyRequestsTryAgainInMinutes": { | ||||
|         "one": "Zu viele Anfragen (Rate begrenzt) - versuchen Sie es in {} Minute erneut", | ||||
|         "other": "Zu viele Anfragen (Rate begrenzt) - versuchen Sie es in {} Minuten erneut" | ||||
|   | ||||
| @@ -199,6 +199,14 @@ | ||||
|     "downloadNotifDescription": "Notifies the user of the progress in downloading an App", | ||||
|     "noAPKFound": "No APK found", | ||||
|     "noVersionDetection": "No version detection", | ||||
|     "categorize": "Categorize", | ||||
|     "categories": "Categories", | ||||
|     "category": "Category", | ||||
|     "noCategory": "No Category", | ||||
|     "deleteCategoryQuestion": "Delete Category?", | ||||
|     "categoryDeleteWarning": "All Apps in {} will be set to uncategorized.", | ||||
|     "addCategory": "Add Category", | ||||
|     "label": "Label", | ||||
|     "tooManyRequestsTryAgainInMinutes": { | ||||
|         "one": "Too many requests (rate limited) - try again in {} minute", | ||||
|         "other": "Too many requests (rate limited) - try again in {} minutes" | ||||
|   | ||||
| @@ -199,6 +199,14 @@ | ||||
|     "downloadNotifDescription": "Notifies the user of the progress in downloading an App", | ||||
|     "noAPKFound": "No APK found", | ||||
|     "noVersionDetection": "No version detection", | ||||
|     "categorize": "Categorize", | ||||
|     "categories": "Categories", | ||||
|     "category": "Category", | ||||
|     "noCategory": "No Category", | ||||
|     "deleteCategoryQuestion": "Delete Category?", | ||||
|     "categoryDeleteWarning": "All Apps in {} will be set to uncategorized.", | ||||
|     "addCategory": "Add Category", | ||||
|     "label": "Label", | ||||
|     "tooManyRequestsTryAgainInMinutes": { | ||||
|         "one": "Túl sok kérés (korlátozott arány) – próbálja újra {} perc múlva", | ||||
|         "other": "Túl sok kérés (korlátozott arány) – próbálja újra {} perc múlva" | ||||
|   | ||||
| @@ -199,6 +199,14 @@ | ||||
|     "downloadNotifDescription": "Notifies the user of the progress in downloading an App", | ||||
|     "noAPKFound": "No APK found", | ||||
|     "noVersionDetection": "No version detection", | ||||
|     "categorize": "Categorize", | ||||
|     "categories": "Categories", | ||||
|     "category": "Category", | ||||
|     "noCategory": "No Category", | ||||
|     "deleteCategoryQuestion": "Delete Category?", | ||||
|     "categoryDeleteWarning": "All Apps in {} will be set to uncategorized.", | ||||
|     "addCategory": "Add Category", | ||||
|     "label": "Label", | ||||
|     "tooManyRequestsTryAgainInMinutes": { | ||||
|         "one": "Troppe richieste (traffico limitato) - riprova tra {} minuto", | ||||
|         "other": "Troppe richieste (traffico limitato) - riprova tra {} minuti" | ||||
|   | ||||
| @@ -199,6 +199,14 @@ | ||||
|     "downloadNotifDescription": "アプリのダウンロード状況を通知する", | ||||
|     "noAPKFound": "APKが見つかりません", | ||||
|     "noVersionDetection": "バージョン検出を行わない", | ||||
|     "categorize": "Categorize", | ||||
|     "categories": "Categories", | ||||
|     "category": "Category", | ||||
|     "noCategory": "No Category", | ||||
|     "deleteCategoryQuestion": "Delete Category?", | ||||
|     "categoryDeleteWarning": "All Apps in {} will be set to uncategorized.", | ||||
|     "addCategory": "Add Category", | ||||
|     "label": "Label", | ||||
|     "tooManyRequestsTryAgainInMinutes": { | ||||
|         "one": "リクエストが多すぎます(レート制限)- {}分後に再試行してください", | ||||
|         "other": "リクエストが多すぎます(レート制限)- {}分後に再試行してください" | ||||
|   | ||||
| @@ -199,6 +199,14 @@ | ||||
|     "downloadNotifDescription": "通知用户下载进度", | ||||
|     "noAPKFound": "未找到安装包", | ||||
|     "noVersionDetection": "无版本检测", | ||||
|     "categorize": "Categorize", | ||||
|     "categories": "Categories", | ||||
|     "category": "Category", | ||||
|     "noCategory": "No Category", | ||||
|     "deleteCategoryQuestion": "Delete Category?", | ||||
|     "categoryDeleteWarning": "All Apps in {} will be set to uncategorized.", | ||||
|     "addCategory": "Add Category", | ||||
|     "label": "Label", | ||||
|     "tooManyRequestsTryAgainInMinutes": { | ||||
|         "one": "请求过多 (API 限制) - 在 {} 分钟后重试", | ||||
|         "other": "请求过多 (API 限制) - 在 {} 分钟后重试" | ||||
|   | ||||
| @@ -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.8.23'; | ||||
| const String currentVersion = '0.9.0'; | ||||
| const String currentReleaseTag = | ||||
|     'v$currentVersion-beta'; // KEEP THIS IN SYNC WITH GITHUB RELEASES | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter/services.dart'; | ||||
| import 'package:obtainium/components/generated_form.dart'; | ||||
| import 'package:obtainium/components/generated_form_modal.dart'; | ||||
| import 'package:obtainium/custom_errors.dart'; | ||||
| import 'package:obtainium/main.dart'; | ||||
| @@ -33,6 +34,7 @@ class _AppPageState extends State<AppPage> { | ||||
|       }); | ||||
|     } | ||||
|  | ||||
|     var categories = settingsProvider.categories; | ||||
|     var sourceProvider = SourceProvider(); | ||||
|     AppInMemory? app = appsProvider.apps[widget.appId]; | ||||
|     var source = app != null ? sourceProvider.getSource(app.app.url) : null; | ||||
| @@ -148,7 +150,51 @@ class _AppPageState extends State<AppPage> { | ||||
|                           textAlign: TextAlign.center, | ||||
|                           style: const TextStyle( | ||||
|                               fontStyle: FontStyle.italic, fontSize: 12), | ||||
|                         ), | ||||
|                         const SizedBox( | ||||
|                           height: 32, | ||||
|                         ), | ||||
|                         app?.app.category != null | ||||
|                             ? Chip( | ||||
|                                 label: Text(app!.app.category!), | ||||
|                                 backgroundColor: | ||||
|                                     Color(categories[app.app.category!] ?? 0x0), | ||||
|                                 onDeleted: () { | ||||
|                                   app.app.category = null; | ||||
|                                   appsProvider.saveApps([app.app]); | ||||
|                                 }, | ||||
|                                 visualDensity: VisualDensity.compact, | ||||
|                               ) | ||||
|                             : Row( | ||||
|                                 mainAxisAlignment: MainAxisAlignment.center, | ||||
|                                 children: [ | ||||
|                                     TextButton( | ||||
|                                         onPressed: () { | ||||
|                                           showDialog<Map<String, String>?>( | ||||
|                                               context: context, | ||||
|                                               builder: (BuildContext ctx) { | ||||
|                                                 return GeneratedFormModal( | ||||
|                                                     title: 'Pick a Category', | ||||
|                                                     items: [ | ||||
|                                                       [ | ||||
|                                                         settingsProvider | ||||
|                                                             .getCategoryFormItem() | ||||
|                                                       ] | ||||
|                                                     ]); | ||||
|                                               }).then((value) { | ||||
|                                             if (value != null && app != null) { | ||||
|                                               String? cat = (value['category'] | ||||
|                                                           ?.isNotEmpty ?? | ||||
|                                                       false) | ||||
|                                                   ? value['category'] | ||||
|                                                   : null; | ||||
|                                               app.app.category = cat; | ||||
|                                               appsProvider.saveApps([app.app]); | ||||
|                                             } | ||||
|                                           }); | ||||
|                                         }, | ||||
|                                         child: Text(tr('categorize'))) | ||||
|                                   ]) | ||||
|                       ], | ||||
|                     )), | ||||
|                   ], | ||||
|   | ||||
| @@ -80,9 +80,7 @@ class AppsPageState extends State<AppsPage> { | ||||
|             !(filter!.includeNonInstalled)) { | ||||
|           return false; | ||||
|         } | ||||
|         if (filter!.nameFilter.isEmpty && filter!.authorFilter.isEmpty) { | ||||
|           return true; | ||||
|         } | ||||
|         if (filter!.nameFilter.isNotEmpty || filter!.authorFilter.isNotEmpty) { | ||||
|           List<String> nameTokens = filter!.nameFilter | ||||
|               .split(' ') | ||||
|               .where((element) => element.trim().isNotEmpty) | ||||
| @@ -103,6 +101,11 @@ class AppsPageState extends State<AppsPage> { | ||||
|               return false; | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|         if (filter!.categoryFilter.isNotEmpty && | ||||
|             filter!.categoryFilter != app.app.category) { | ||||
|           return false; | ||||
|         } | ||||
|         return true; | ||||
|       }).toList(); | ||||
|     } | ||||
| @@ -225,7 +228,15 @@ class AppsPageState extends State<AppsPage> { | ||||
|               String? changesUrl = SourceProvider() | ||||
|                   .getSource(sortedApps[index].app.url) | ||||
|                   .changeLogPageFromStandardUrl(sortedApps[index].app.url); | ||||
|               return ListTile( | ||||
|               return Container( | ||||
|                   decoration: BoxDecoration( | ||||
|                       border: Border.symmetric( | ||||
|                           vertical: BorderSide( | ||||
|                               width: 3, | ||||
|                               color: Color(settingsProvider.categories[ | ||||
|                                       sortedApps[index].app.category] ?? | ||||
|                                   const Color.fromARGB(0, 0, 0, 0).value)))), | ||||
|                   child: ListTile( | ||||
|                     tileColor: sortedApps[index].app.pinned | ||||
|                         ? Colors.grey.withOpacity(0.1) | ||||
|                         : Colors.transparent, | ||||
| @@ -249,9 +260,11 @@ class AppsPageState extends State<AppsPage> { | ||||
|                       style: TextStyle( | ||||
|                         fontWeight: sortedApps[index].app.pinned | ||||
|                             ? FontWeight.bold | ||||
|                           : FontWeight.normal), | ||||
|                             : FontWeight.normal, | ||||
|                       ), | ||||
|                 subtitle: Text(tr('byX', args: [sortedApps[index].app.author]), | ||||
|                     ), | ||||
|                     subtitle: Text( | ||||
|                         tr('byX', args: [sortedApps[index].app.author]), | ||||
|                         style: TextStyle( | ||||
|                             fontWeight: sortedApps[index].app.pinned | ||||
|                                 ? FontWeight.bold | ||||
| @@ -277,9 +290,14 @@ class AppsPageState extends State<AppsPage> { | ||||
|                                         overflow: TextOverflow.fade, | ||||
|                                         textAlign: TextAlign.end, | ||||
|                                       )), | ||||
|                               sortedApps[index].app.installedVersion != null && | ||||
|                                   sortedApps[index].app.installedVersion != | ||||
|                                           sortedApps[index].app.latestVersion | ||||
|                                               null && | ||||
|                                           sortedApps[index] | ||||
|                                                   .app | ||||
|                                                   .installedVersion != | ||||
|                                               sortedApps[index] | ||||
|                                                   .app | ||||
|                                                   .latestVersion | ||||
|                                       ? GestureDetector( | ||||
|                                           onTap: changesUrl == null | ||||
|                                               ? null | ||||
| @@ -288,13 +306,16 @@ class AppsPageState extends State<AppsPage> { | ||||
|                                                       mode: LaunchMode | ||||
|                                                           .externalApplication); | ||||
|                                                 }, | ||||
|                                       child: appsProvider.areDownloadsRunning() | ||||
|                                           child: appsProvider | ||||
|                                                   .areDownloadsRunning() | ||||
|                                               ? Text(tr('pleaseWait')) | ||||
|                                               : Text( | ||||
|                                                   '${tr('updateAvailable')}${sortedApps[index].app.additionalSettings['trackOnly'] == 'true' ? ' ${tr('estimateInBracketsShort')}' : ''}', | ||||
|                                                   style: TextStyle( | ||||
|                                                   fontStyle: FontStyle.italic, | ||||
|                                                   decoration: changesUrl == null | ||||
|                                                       fontStyle: | ||||
|                                                           FontStyle.italic, | ||||
|                                                       decoration: changesUrl == | ||||
|                                                               null | ||||
|                                                           ? TextDecoration.none | ||||
|                                                           : TextDecoration | ||||
|                                                               .underline), | ||||
| @@ -314,7 +335,7 @@ class AppsPageState extends State<AppsPage> { | ||||
|                         ); | ||||
|                       } | ||||
|                     }, | ||||
|               ); | ||||
|                   )); | ||||
|             }, childCount: sortedApps.length)) | ||||
|           ])), | ||||
|       persistentFooterButtons: [ | ||||
| @@ -708,6 +729,10 @@ class AppsPageState extends State<AppsPage> { | ||||
|                                         label: tr('nonInstalledApps'), | ||||
|                                         type: FormItemType.bool, | ||||
|                                         defaultValue: vals['nonInstalledApps']) | ||||
|                                   ], | ||||
|                                   [ | ||||
|                                     settingsProvider.getCategoryFormItem( | ||||
|                                         initCategory: vals['category'] ?? '') | ||||
|                                   ] | ||||
|                                 ]); | ||||
|                           }).then((values) { | ||||
| @@ -734,19 +759,22 @@ class AppsFilter { | ||||
|   late String authorFilter; | ||||
|   late bool includeUptodate; | ||||
|   late bool includeNonInstalled; | ||||
|   late String categoryFilter; | ||||
|  | ||||
|   AppsFilter( | ||||
|       {this.nameFilter = '', | ||||
|       this.authorFilter = '', | ||||
|       this.includeUptodate = true, | ||||
|       this.includeNonInstalled = true}); | ||||
|       this.includeNonInstalled = true, | ||||
|       this.categoryFilter = ''}); | ||||
|  | ||||
|   Map<String, String> toValuesMap() { | ||||
|     return { | ||||
|       'appName': nameFilter, | ||||
|       'author': authorFilter, | ||||
|       'upToDateApps': includeUptodate ? 'true' : '', | ||||
|       'nonInstalledApps': includeNonInstalled ? 'true' : '' | ||||
|       'nonInstalledApps': includeNonInstalled ? 'true' : '', | ||||
|       'category': categoryFilter | ||||
|     }; | ||||
|   } | ||||
|  | ||||
| @@ -755,11 +783,13 @@ class AppsFilter { | ||||
|     authorFilter = values['author']!; | ||||
|     includeUptodate = values['upToDateApps'] == 'true'; | ||||
|     includeNonInstalled = values['nonInstalledApps'] == 'true'; | ||||
|     categoryFilter = values['category']!; | ||||
|   } | ||||
|  | ||||
|   bool isIdenticalTo(AppsFilter other) => | ||||
|       authorFilter.trim() == other.authorFilter.trim() && | ||||
|       nameFilter.trim() == other.nameFilter.trim() && | ||||
|       includeUptodate == other.includeUptodate && | ||||
|       includeNonInstalled == other.includeNonInstalled; | ||||
|       includeNonInstalled == other.includeNonInstalled && | ||||
|       categoryFilter.trim() == other.categoryFilter.trim(); | ||||
| } | ||||
|   | ||||
| @@ -1,8 +1,12 @@ | ||||
| import 'dart:math'; | ||||
|  | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:obtainium/components/custom_app_bar.dart'; | ||||
| import 'package:obtainium/components/generated_form.dart'; | ||||
| import 'package:obtainium/components/generated_form_modal.dart'; | ||||
| import 'package:obtainium/custom_errors.dart'; | ||||
| import 'package:obtainium/providers/apps_provider.dart'; | ||||
| import 'package:obtainium/providers/logs_provider.dart'; | ||||
| import 'package:obtainium/providers/settings_provider.dart'; | ||||
| import 'package:obtainium/providers/source_provider.dart'; | ||||
| @@ -17,11 +21,27 @@ class SettingsPage extends StatefulWidget { | ||||
|   State<SettingsPage> createState() => _SettingsPageState(); | ||||
| } | ||||
|  | ||||
| // Generates a random light color | ||||
| // Courtesy of ChatGPT 😭 (with a bugfix 🥳) | ||||
| Color generateRandomLightColor() { | ||||
|   // Create a random number generator | ||||
|   final Random random = Random(); | ||||
|  | ||||
|   // Generate random hue, saturation, and value values | ||||
|   final double hue = random.nextDouble() * 360; | ||||
|   final double saturation = 0.5 + random.nextDouble() * 0.5; | ||||
|   final double value = 0.9 + random.nextDouble() * 0.1; | ||||
|  | ||||
|   // Create a HSV color with the random values | ||||
|   return HSVColor.fromAHSV(1.0, hue, saturation, value).toColor(); | ||||
| } | ||||
|  | ||||
| class _SettingsPageState extends State<SettingsPage> { | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     SettingsProvider settingsProvider = context.watch<SettingsProvider>(); | ||||
|     SourceProvider sourceProvider = SourceProvider(); | ||||
|     AppsProvider appsProvider = context.read<AppsProvider>(); | ||||
|     if (settingsProvider.prefs == null) { | ||||
|       settingsProvider.initializeSettings(); | ||||
|     } | ||||
| @@ -157,6 +177,8 @@ class _SettingsPageState extends State<SettingsPage> { | ||||
|       height: 16, | ||||
|     ); | ||||
|  | ||||
|     var categories = settingsProvider.categories; | ||||
|  | ||||
|     return Scaffold( | ||||
|         backgroundColor: Theme.of(context).colorScheme.surface, | ||||
|         body: CustomScrollView(slivers: <Widget>[ | ||||
| @@ -232,6 +254,94 @@ class _SettingsPageState extends State<SettingsPage> { | ||||
|                                   color: Theme.of(context).colorScheme.primary), | ||||
|                             ), | ||||
|                             ...sourceSpecificFields, | ||||
|                             intervalDropdown, | ||||
|                             const Divider( | ||||
|                               height: 48, | ||||
|                             ), | ||||
|                             Text( | ||||
|                               tr('categories'), | ||||
|                               style: TextStyle( | ||||
|                                   color: Theme.of(context).colorScheme.primary), | ||||
|                             ), | ||||
|                             height16, | ||||
|                             Wrap( | ||||
|                               children: [ | ||||
|                                 ...categories.entries.toList().map((e) { | ||||
|                                   return Padding( | ||||
|                                       padding: const EdgeInsets.symmetric( | ||||
|                                           horizontal: 4), | ||||
|                                       child: Chip( | ||||
|                                         label: Text(e.key), | ||||
|                                         backgroundColor: Color(e.value), | ||||
|                                         visualDensity: VisualDensity.compact, | ||||
|                                         onDeleted: () { | ||||
|                                           showDialog<Map<String, String>?>( | ||||
|                                               context: context, | ||||
|                                               builder: (BuildContext ctx) { | ||||
|                                                 return GeneratedFormModal( | ||||
|                                                     title: tr( | ||||
|                                                         'deleteCategoryQuestion'), | ||||
|                                                     message: tr( | ||||
|                                                         'categoryDeleteWarning', | ||||
|                                                         args: [e.key]), | ||||
|                                                     items: []); | ||||
|                                               }).then((value) { | ||||
|                                             if (value != null) { | ||||
|                                               setState(() { | ||||
|                                                 categories.remove(e.key); | ||||
|                                                 settingsProvider.categories = | ||||
|                                                     categories; | ||||
|                                               }); | ||||
|                                               appsProvider.saveApps(appsProvider | ||||
|                                                   .apps.values | ||||
|                                                   .where((element) => | ||||
|                                                       element.app.category == | ||||
|                                                       e.key) | ||||
|                                                   .map((e) { | ||||
|                                                 var a = e.app; | ||||
|                                                 a.category = null; | ||||
|                                                 return a; | ||||
|                                               }).toList()); | ||||
|                                             } | ||||
|                                           }); | ||||
|                                         }, | ||||
|                                       )); | ||||
|                                 }), | ||||
|                                 Padding( | ||||
|                                     padding: const EdgeInsets.symmetric( | ||||
|                                         horizontal: 4), | ||||
|                                     child: IconButton( | ||||
|                                       onPressed: () { | ||||
|                                         showDialog<Map<String, String>?>( | ||||
|                                             context: context, | ||||
|                                             builder: (BuildContext ctx) { | ||||
|                                               return GeneratedFormModal( | ||||
|                                                   title: tr('addCategory'), | ||||
|                                                   items: [ | ||||
|                                                     [ | ||||
|                                                       GeneratedFormItem('label', | ||||
|                                                           label: tr('label')) | ||||
|                                                     ] | ||||
|                                                   ]); | ||||
|                                             }).then((value) { | ||||
|                                           String? label = value?['label']; | ||||
|                                           if (label != null) { | ||||
|                                             setState(() { | ||||
|                                               categories[label] = | ||||
|                                                   generateRandomLightColor() | ||||
|                                                       .value; | ||||
|                                               settingsProvider.categories = | ||||
|                                                   categories; | ||||
|                                             }); | ||||
|                                           } | ||||
|                                         }); | ||||
|                                       }, | ||||
|                                       icon: const Icon(Icons.add), | ||||
|                                       visualDensity: VisualDensity.compact, | ||||
|                                       tooltip: tr('add'), | ||||
|                                     )) | ||||
|                               ], | ||||
|                             ) | ||||
|                           ], | ||||
|                         ))), | ||||
|           SliverToBoxAdapter( | ||||
|   | ||||
| @@ -1,9 +1,12 @@ | ||||
| // Exposes functions used to save/load app settings | ||||
|  | ||||
| import 'dart:convert'; | ||||
|  | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:fluttertoast/fluttertoast.dart'; | ||||
| import 'package:obtainium/app_sources/github.dart'; | ||||
| import 'package:obtainium/components/generated_form.dart'; | ||||
| import 'package:permission_handler/permission_handler.dart'; | ||||
| import 'package:shared_preferences/shared_preferences.dart'; | ||||
|  | ||||
| @@ -144,4 +147,20 @@ class SettingsProvider with ChangeNotifier { | ||||
|     prefs?.setString(settingId, value); | ||||
|     notifyListeners(); | ||||
|   } | ||||
|  | ||||
|   Map<String, int> get categories => | ||||
|       Map<String, int>.from(jsonDecode(prefs?.getString('categories') ?? '{}')); | ||||
|  | ||||
|   set categories(Map<String, int> cats) { | ||||
|     prefs?.setString('categories', jsonEncode(cats)); | ||||
|   } | ||||
|  | ||||
|   getCategoryFormItem({String initCategory = ''}) => | ||||
|       GeneratedFormItem('category', | ||||
|           label: tr('category'), | ||||
|           opts: [ | ||||
|             MapEntry('', tr('noCategory')), | ||||
|             ...categories.entries.map((e) => MapEntry(e.key, e.key)).toList() | ||||
|           ], | ||||
|           defaultValue: initCategory); | ||||
| } | ||||
|   | ||||
| @@ -19,6 +19,7 @@ import 'package:obtainium/app_sources/steammobile.dart'; | ||||
| import 'package:obtainium/components/generated_form.dart'; | ||||
| import 'package:obtainium/custom_errors.dart'; | ||||
| import 'package:obtainium/mass_app_sources/githubstars.dart'; | ||||
| import 'package:obtainium/providers/settings_provider.dart'; | ||||
|  | ||||
| class AppNames { | ||||
|   late String author; | ||||
| @@ -47,6 +48,7 @@ class App { | ||||
|   late Map<String, String> additionalSettings; | ||||
|   late DateTime? lastUpdateCheck; | ||||
|   bool pinned = false; | ||||
|   String? category; | ||||
|   App( | ||||
|       this.id, | ||||
|       this.url, | ||||
| @@ -58,7 +60,8 @@ class App { | ||||
|       this.preferredApkIndex, | ||||
|       this.additionalSettings, | ||||
|       this.lastUpdateCheck, | ||||
|       this.pinned); | ||||
|       this.pinned, | ||||
|       {this.category}); | ||||
|  | ||||
|   @override | ||||
|   String toString() { | ||||
| @@ -107,7 +110,8 @@ class App { | ||||
|         json['lastUpdateCheck'] == null | ||||
|             ? null | ||||
|             : DateTime.fromMicrosecondsSinceEpoch(json['lastUpdateCheck']), | ||||
|         json['pinned'] ?? false); | ||||
|         json['pinned'] ?? false, | ||||
|         category: json['category']); | ||||
|   } | ||||
|  | ||||
|   Map<String, dynamic> toJson() => { | ||||
| @@ -121,7 +125,8 @@ class App { | ||||
|         'preferredApkIndex': preferredApkIndex, | ||||
|         'additionalSettings': jsonEncode(additionalSettings), | ||||
|         'lastUpdateCheck': lastUpdateCheck?.microsecondsSinceEpoch, | ||||
|         'pinned': pinned | ||||
|         'pinned': pinned, | ||||
|         'category': category | ||||
|       }; | ||||
| } | ||||
|  | ||||
| @@ -350,7 +355,8 @@ class SourceProvider { | ||||
|         apk.apkUrls.length - 1, | ||||
|         additionalSettings, | ||||
|         DateTime.now(), | ||||
|         currentApp?.pinned ?? false); | ||||
|         currentApp?.pinned ?? false, | ||||
|         category: currentApp?.category); | ||||
|   } | ||||
|  | ||||
|   // Returns errors in [results, errors] instead of throwing them | ||||
|   | ||||
| @@ -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.8.23+87 # When changing this, update the tag in main() accordingly | ||||
| version: 0.9.0+88 # When changing this, update the tag in main() accordingly | ||||
|  | ||||
| environment: | ||||
|   sdk: '>=2.18.2 <3.0.0' | ||||
|   | ||||
		Reference in New Issue
	
	Block a user