mirror of
https://github.com/revanced/revanced-manager
synced 2024-05-14 13:56:57 +02:00
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:
parent
19f990c564
commit
5f81d65911
@ -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",
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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();
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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)));
|
||||||
|
@ -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));
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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),
|
||||||
|
@ -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),
|
||||||
|
@ -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);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user