feat: sort by amount of patches, display patches count, setting to enable universal patches (#593)

* feat: sort by amount of patches

* feat: display patches count in application card

* feat: setting to enable universal patches
This commit is contained in:
Ushie 2022-12-15 21:05:45 +03:00 committed by GitHub
parent 19f990c564
commit 5f81d65911
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 114 additions and 29 deletions

View File

@ -137,6 +137,8 @@
"apiURLLabel": "API URL", "apiURLLabel": "API URL",
"apiURLHint": "Configure your custom API URL", "apiURLHint": "Configure your custom API URL",
"selectApiURL": "API URL", "selectApiURL": "API URL",
"experimentalUniversalPatchesLabel": "Experimental universal patches support",
"experimentalUniversalPatchesHint": "Display all applications to use with universal patches, loading list of apps may be slower",
"experimentalPatchesLabel": "Experimental patches support", "experimentalPatchesLabel": "Experimental patches support",
"experimentalPatchesHint": "Enable usage of unsupported patches in any app version", "experimentalPatchesHint": "Enable usage of unsupported patches in any app version",
"enabledExperimentalPatches": "Experimental patches support enabled", "enabledExperimentalPatches": "Experimental patches support enabled",

View File

@ -106,6 +106,14 @@ class ManagerAPI {
await _prefs.setBool('sentryEnabled', value); await _prefs.setBool('sentryEnabled', value);
} }
bool areUniversalPatchesEnabled() {
return _prefs.getBool('universalPatchesEnabled') ?? false;
}
Future<void> enableUniversalPatchesStatus(bool value) async {
await _prefs.setBool('universalPatchesEnabled', value);
}
bool areExperimentalPatchesEnabled() { bool areExperimentalPatchesEnabled() {
return _prefs.getBool('experimentalPatchesEnabled') ?? false; return _prefs.getBool('experimentalPatchesEnabled') ?? false;
} }

View File

@ -24,6 +24,7 @@ class PatcherAPI {
late Directory _tmpDir; late Directory _tmpDir;
late File _keyStoreFile; late File _keyStoreFile;
List<Patch> _patches = []; List<Patch> _patches = [];
Map filteredPatches = <String, List<Patch>>{};
File? _outFile; File? _outFile;
Future<void> initialize() async { Future<void> initialize() async {
@ -52,10 +53,10 @@ class PatcherAPI {
} }
} }
Future<List<ApplicationWithIcon>> getFilteredInstalledApps() async { Future<List<ApplicationWithIcon>> getFilteredInstalledApps(bool showUniversalPatches) async {
List<ApplicationWithIcon> filteredApps = []; List<ApplicationWithIcon> filteredApps = [];
bool? allAppsIncluded = bool? allAppsIncluded =
_patches.any((patch) => patch.compatiblePackages.isEmpty); _patches.any((patch) => patch.compatiblePackages.isEmpty) && showUniversalPatches;
if (allAppsIncluded) { if (allAppsIncluded) {
var allPackages = await DeviceApps.getInstalledApplications( var allPackages = await DeviceApps.getInstalledApplications(
includeAppIcons: true, includeAppIcons: true,
@ -94,20 +95,18 @@ class PatcherAPI {
return filteredApps; return filteredApps;
} }
Future<List<Patch>> getFilteredPatches(String packageName) async { List<Patch> getFilteredPatches(String packageName) {
List<Patch> filteredPatches = []; if (!filteredPatches.keys.contains(packageName)) {
_patches.forEach((patch) { List<Patch> patches = _patches
if (patch.compatiblePackages.isEmpty) { .where((patch) =>
filteredPatches.add(patch); patch.compatiblePackages.isEmpty ||
} else { !patch.name.contains('settings') &&
if (!patch.name.contains('settings') && patch.compatiblePackages
patch.compatiblePackages.any((pack) => pack.name == packageName) .any((pack) => pack.name == packageName))
) { .toList();
filteredPatches.add(patch); filteredPatches[packageName] = patches;
} }
} return filteredPatches[packageName];
});
return filteredPatches;
} }
Future<List<Patch>> getAppliedPatches(List<String> appliedPatches) async { Future<List<Patch>> getAppliedPatches(List<String> appliedPatches) async {

View File

@ -92,6 +92,8 @@ class _AppSelectorViewState extends State<AppSelectorView> {
name: app.appName, name: app.appName,
pkgName: app.packageName, pkgName: app.packageName,
icon: app.icon, icon: app.icon,
patchesCount:
model.patchesCount(app.packageName),
onTap: () { onTap: () {
model.selectApp(app); model.selectApp(app);
Navigator.of(context).pop(); Navigator.of(context).pop();

View File

@ -2,7 +2,6 @@ import 'dart:io';
import 'package:device_apps/device_apps.dart'; import 'package:device_apps/device_apps.dart';
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/models/patched_application.dart'; import 'package:revanced_manager/models/patched_application.dart';
import 'package:revanced_manager/services/patcher_api.dart'; import 'package:revanced_manager/services/patcher_api.dart';
@ -10,16 +9,24 @@ import 'package:revanced_manager/services/toast.dart';
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart'; import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:stacked/stacked.dart'; import 'package:stacked/stacked.dart';
import '../../../services/manager_api.dart';
class AppSelectorViewModel extends BaseViewModel { class AppSelectorViewModel extends BaseViewModel {
final PatcherAPI _patcherAPI = locator<PatcherAPI>(); final PatcherAPI _patcherAPI = locator<PatcherAPI>();
final ManagerAPI _managerAPI = locator<ManagerAPI>();
final Toast _toast = locator<Toast>(); final Toast _toast = locator<Toast>();
final List<ApplicationWithIcon> apps = []; final List<ApplicationWithIcon> apps = [];
bool noApps = false; bool noApps = false;
int patchesCount(String packageName) {
return _patcherAPI.getFilteredPatches(packageName).length;
}
Future<void> initialize() async { Future<void> initialize() async {
apps.addAll(await _patcherAPI.getFilteredInstalledApps()); apps.addAll(await _patcherAPI.getFilteredInstalledApps(_managerAPI.areUniversalPatchesEnabled()));
apps.sort((a, b) => a.appName.compareTo(b.appName)); apps.sort(((a, b) => _patcherAPI
.getFilteredPatches(b.packageName)
.length
.compareTo(_patcherAPI.getFilteredPatches(a.packageName).length)));
noApps = apps.isEmpty; noApps = apps.isEmpty;
notifyListeners(); notifyListeners();
} }

View File

@ -113,7 +113,7 @@ class PatcherViewModel extends BaseViewModel {
List<String> selectedPatches = List<String> selectedPatches =
await _managerAPI.getSelectedPatches(selectedApp!.originalPackageName); await _managerAPI.getSelectedPatches(selectedApp!.originalPackageName);
List<Patch> patches = List<Patch> patches =
await _patcherAPI.getFilteredPatches(selectedApp!.originalPackageName); _patcherAPI.getFilteredPatches(selectedApp!.originalPackageName);
this this
.selectedPatches .selectedPatches
.addAll(patches.where((patch) => selectedPatches.contains(patch.name))); .addAll(patches.where((patch) => selectedPatches.contains(patch.name)));

View File

@ -26,7 +26,7 @@ class PatchesSelectorViewModel extends BaseViewModel {
Future<void> initialize() async { Future<void> initialize() async {
getPatchesVersion(); getPatchesVersion();
patches.addAll(await _patcherAPI.getFilteredPatches( patches.addAll(_patcherAPI.getFilteredPatches(
locator<PatcherViewModel>().selectedApp!.originalPackageName, locator<PatcherViewModel>().selectedApp!.originalPackageName,
)); ));
patches.sort((a, b) => a.name.compareTo(b.name)); patches.sort((a, b) => a.name.compareTo(b.name));

View File

@ -38,6 +38,15 @@ class SettingsViewModel extends BaseViewModel {
notifyListeners(); notifyListeners();
} }
bool areUniversalPatchesEnabled() {
return _managerAPI.areUniversalPatchesEnabled();
}
void showUniversalPatches(bool value) {
_managerAPI.enableUniversalPatchesStatus(value);
notifyListeners();
}
bool areExperimentalPatchesEnabled() { bool areExperimentalPatchesEnabled() {
return _managerAPI.areExperimentalPatchesEnabled(); return _managerAPI.areExperimentalPatchesEnabled();
} }

View File

@ -6,6 +6,7 @@ class InstalledAppItem extends StatefulWidget {
final String name; final String name;
final String pkgName; final String pkgName;
final Uint8List icon; final Uint8List icon;
final int patchesCount;
final Function()? onTap; final Function()? onTap;
const InstalledAppItem({ const InstalledAppItem({
@ -13,6 +14,7 @@ class InstalledAppItem extends StatefulWidget {
required this.name, required this.name,
required this.pkgName, required this.pkgName,
required this.icon, required this.icon,
required this.patchesCount,
this.onTap, this.onTap,
}) : super(key: key); }) : super(key: key);
@ -45,14 +47,29 @@ class _InstalledAppItemState extends State<InstalledAppItem> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Text( Row(
widget.name, crossAxisAlignment: CrossAxisAlignment.center,
maxLines: 2, children: <Widget>[
overflow: TextOverflow.visible, Text(
style: const TextStyle( widget.name,
fontSize: 16, maxLines: 2,
fontWeight: FontWeight.w500, overflow: TextOverflow.visible,
), style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
const SizedBox(width: 6),
Text(
widget.patchesCount == 1
? "${widget.patchesCount} patch"
: "${widget.patchesCount} patches",
style: TextStyle(
fontSize: 8,
color: Theme.of(context).colorScheme.secondary,
),
),
],
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
Text(widget.pkgName), Text(widget.pkgName),

View File

@ -6,6 +6,7 @@ import 'package:revanced_manager/ui/views/settings/settingsFragement/settings_ma
import 'package:revanced_manager/ui/views/settings/settingsFragement/settings_manage_sources.dart'; import 'package:revanced_manager/ui/views/settings/settingsFragement/settings_manage_sources.dart';
import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart'; import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_experimental_patches.dart'; import 'package:revanced_manager/ui/widgets/settingsView/settings_experimental_patches.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_experimental_universal_patches.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_section.dart'; import 'package:revanced_manager/ui/widgets/settingsView/settings_section.dart';
final _settingsViewModel = SettingsViewModel(); final _settingsViewModel = SettingsViewModel();
@ -20,6 +21,7 @@ class SAdvancedSection extends StatelessWidget {
children: <Widget>[ children: <Widget>[
SManageApiUrlUI(), SManageApiUrlUI(),
SManageSourcesUI(), SManageSourcesUI(),
SExperimentalUniversalPatches(),
SExperimentalPatches(), SExperimentalPatches(),
ListTile( ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0), contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),

View File

@ -0,0 +1,39 @@
import 'package:flutter/material.dart';
import 'package:flutter_i18n/widgets/I18nText.dart';
import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/settingsView/custom_switch_tile.dart';
class SExperimentalUniversalPatches extends StatefulWidget {
const SExperimentalUniversalPatches({super.key});
@override
State<SExperimentalUniversalPatches> createState() => _SExperimentalUniversalPatchesState();
}
final _settingsViewModel = SettingsViewModel();
class _SExperimentalUniversalPatchesState extends State<SExperimentalUniversalPatches> {
@override
Widget build(BuildContext context) {
return CustomSwitchTile(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
title: I18nText(
'settingsView.experimentalUniversalPatchesLabel',
child: const Text(
'',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
),
subtitle: I18nText('settingsView.experimentalUniversalPatchesHint'),
value: _settingsViewModel.areUniversalPatchesEnabled(),
onTap: (value) {
setState(() {
_settingsViewModel.showUniversalPatches(value);
});
},
);
}
}