feat: disable changing patches selection by default (#1132)

Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
This commit is contained in:
aAbed 2023-08-15 14:50:27 +05:45 committed by GitHub
parent dcd5ba41cf
commit c400619338
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 381 additions and 61 deletions

View file

@ -11,6 +11,7 @@
"noButton": "No",
"warning": "Warning",
"notice": "Notice",
"noShowAgain": "Don't show this again",
"new": "New",
"navigationView": {
"dashboardTab": "Dashboard",
@ -136,7 +137,10 @@
"unsupportedPatchVersion": "Patch is not supported for this app version. Enable the experimental toggle in settings to proceed.",
"newPatchDialogText": "This is a new patch that has been added since the last time you have patched this app.",
"newPatch": "New patch"
"newPatch": "New patch",
"patchesChangeWarningDialogText": "It is recommended to use the default selection of patches because changing it may cause unexpected issues.\n\nIf you know what you are doing, you can enable \"Enable changing selection\" in the settings.",
"patchesChangeWarningDialogButton": "Use default selection"
},
"installerView": {
"widgetTitle": "Installer",
@ -205,6 +209,11 @@
"logsLabel": "Logs",
"logsHint": "Share Manager's logs",
"enablePatchesSelectionLabel": "Enable changing selection",
"enablePatchesSelectionHint": "Enable changing the selection of patches.",
"enablePatchesSelectionWarningText": "Changing the default selection of patches may cause unexpected issues.\n\nEnable anyways?",
"disablePatchesSelectionWarningText": "You are about to disable changing the selection of patches.\nThe default selection of patches will be restored.\n\nDisable anyways?",
"autoUpdatePatchesLabel": "Auto update patches",
"autoUpdatePatchesHint": "Automatically update ReVanced Patches to the latest version",
"experimentalUniversalPatchesLabel": "Experimental universal patches support",

View file

@ -2,6 +2,8 @@ import 'dart:convert';
import 'dart:io';
import 'package:device_apps/device_apps.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_i18n/widgets/I18nText.dart';
import 'package:injectable/injectable.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:path_provider/path_provider.dart';
@ -11,6 +13,7 @@ import 'package:revanced_manager/models/patched_application.dart';
import 'package:revanced_manager/services/github_api.dart';
import 'package:revanced_manager/services/revanced_api.dart';
import 'package:revanced_manager/services/root_api.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
import 'package:revanced_manager/utils/check_for_supported_patch.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:timeago/timeago.dart';
@ -107,6 +110,41 @@ class ManagerAPI {
return _prefs.getBool('patchesAutoUpdate') ?? false;
}
bool isPatchesChangeEnabled() {
if (getPatchedApps().isNotEmpty && !isChangingToggleModified()) {
for (final apps in getPatchedApps()) {
if (getSavedPatches(apps.originalPackageName)
.indexWhere((patch) => patch.excluded) !=
-1) {
setPatchesChangeWarning(false);
setPatchesChangeEnabled(true);
break;
}
}
}
return _prefs.getBool('patchesChangeEnabled') ?? false;
}
void setPatchesChangeEnabled(bool value) {
_prefs.setBool('patchesChangeEnabled', value);
}
bool showPatchesChangeWarning() {
return _prefs.getBool('showPatchesChangeWarning') ?? true;
}
void setPatchesChangeWarning(bool value) {
_prefs.setBool('showPatchesChangeWarning', !value);
}
bool isChangingToggleModified() {
return _prefs.getBool('isChangingToggleModified') ?? false;
}
void setChangingToggleModified(bool value) {
_prefs.setBool('isChangingToggleModified', value);
}
Future<void> setPatchesAutoUpdate(bool value) async {
await _prefs.setBool('patchesAutoUpdate', value);
}
@ -515,6 +553,63 @@ class ManagerAPI {
return unsavedApps;
}
Future<void> showPatchesChangeWarningDialog(BuildContext context) {
final ValueNotifier<bool> noShow =
ValueNotifier(!showPatchesChangeWarning());
return showDialog(
barrierDismissible: false,
context: context,
builder: (context) => WillPopScope(
onWillPop: () async => false,
child: AlertDialog(
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
title: I18nText('warning'),
content: ValueListenableBuilder(
valueListenable: noShow,
builder: (context, value, child) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
I18nText(
'patchItem.patchesChangeWarningDialogText',
child: const Text(
'',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
),
const SizedBox(height: 8),
CheckboxListTile(
value: value,
contentPadding: EdgeInsets.zero,
title: I18nText(
'noShowAgain',
),
onChanged: (selected) {
noShow.value = selected!;
},
),
],
);
},
),
actions: [
CustomMaterialButton(
label: I18nText('okButton'),
onPressed: () {
setPatchesChangeWarning(noShow.value);
Navigator.of(context).pop();
},
),
],
),
),
);
}
Future<void> reAssessSavedApps() async {
final List<PatchedApplication> patchedApps = getPatchedApps();
final List<PatchedApplication> unsavedApps =

View file

@ -191,6 +191,10 @@ class PatcherViewModel extends BaseViewModel {
this
.selectedPatches
.addAll(patches.where((patch) => selectedPatches.contains(patch.name)));
if (!_managerAPI.isPatchesChangeEnabled()) {
this.selectedPatches.clear();
this.selectedPatches.addAll(patches.where((patch) => !patch.excluded));
}
if (!_managerAPI.areExperimentalPatchesEnabled()) {
this.selectedPatches.removeWhere((patch) => !isPatchSupported(patch));
}

View file

@ -20,6 +20,17 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
String _query = '';
final _managerAPI = locator<ManagerAPI>();
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
if (!_managerAPI.isPatchesChangeEnabled() &&
_managerAPI.showPatchesChangeWarning()) {
_managerAPI.showPatchesChangeWarningDialog(context);
}
});
}
@override
Widget build(BuildContext context) {
return ViewModelBuilder<PatchesSelectorViewModel>.reactive(
@ -87,7 +98,8 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
),
),
CustomPopupMenu(
onSelected: (value) => {model.onMenuSelection(value)},
onSelected: (value) =>
{model.onMenuSelection(value, context)},
children: {
0: I18nText(
'patchesSelectorView.loadPatchesSelection',
@ -152,7 +164,11 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
'patchesSelectorView.defaultTooltip',
),
onPressed: () {
model.selectDefaultPatches();
if (_managerAPI.isPatchesChangeEnabled()) {
model.selectDefaultPatches();
} else {
model.showPatchesChangeDialog(context);
}
},
),
const SizedBox(width: 8),
@ -163,7 +179,11 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
'patchesSelectorView.noneTooltip',
),
onPressed: () {
model.clearPatches();
if (_managerAPI.isPatchesChangeEnabled()) {
model.clearPatches();
} else {
model.showPatchesChangeDialog(context);
}
},
),
],
@ -179,13 +199,14 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
supportedPackageVersions:
model.getSupportedVersions(patch),
isUnsupported: !isPatchSupported(patch),
isChangeEnabled: _managerAPI.isPatchesChangeEnabled(),
isNew: model.isPatchNew(
patch,
model.getAppInfo().packageName,
),
isSelected: model.isSelected(patch),
onChanged: (value) =>
model.selectPatch(patch, value),
model.selectPatch(patch, value, context),
);
} else {
return Container();
@ -215,10 +236,14 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
supportedPackageVersions:
model.getSupportedVersions(patch),
isUnsupported: !isPatchSupported(patch),
isChangeEnabled: _managerAPI.isPatchesChangeEnabled(),
isNew: false,
isSelected: model.isSelected(patch),
onChanged: (value) =>
model.selectPatch(patch, value),
onChanged: (value) => model.selectPatch(
patch,
false,
context,
),
);
} else {
return Container();

View file

@ -1,4 +1,6 @@
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_i18n/widgets/I18nText.dart';
import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/models/patch.dart';
import 'package:revanced_manager/models/patched_application.dart';
@ -6,6 +8,7 @@ import 'package:revanced_manager/services/manager_api.dart';
import 'package:revanced_manager/services/patcher_api.dart';
import 'package:revanced_manager/services/toast.dart';
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
import 'package:revanced_manager/utils/check_for_supported_patch.dart';
import 'package:stacked/stacked.dart';
@ -45,31 +48,70 @@ class PatchesSelectorViewModel extends BaseViewModel {
);
}
void selectPatch(Patch patch, bool isSelected) {
if (isSelected && !selectedPatches.contains(patch)) {
selectedPatches.add(patch);
void selectPatch(Patch patch, bool isSelected, BuildContext context) {
if (_managerAPI.isPatchesChangeEnabled()) {
if (isSelected && !selectedPatches.contains(patch)) {
selectedPatches.add(patch);
} else {
selectedPatches.remove(patch);
}
notifyListeners();
} else {
selectedPatches.remove(patch);
showPatchesChangeDialog(context);
}
notifyListeners();
}
Future<void> showPatchesChangeDialog(BuildContext context) async {
return showDialog(
context: context,
builder: (context) => AlertDialog(
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
title: I18nText('warning'),
content: I18nText(
'patchItem.patchesChangeWarningDialogText',
child: const Text(
'',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
),
actions: [
CustomMaterialButton(
isFilled: false,
label: I18nText('okButton'),
onPressed: () => Navigator.of(context).pop(),
),
CustomMaterialButton(
label: I18nText('patchItem.patchesChangeWarningDialogButton'),
onPressed: () {
Navigator.of(context)
..pop()
..pop();
},
),
],
),
);
}
void selectDefaultPatches() {
selectedPatches.clear();
if (_managerAPI.areExperimentalPatchesEnabled() == false) {
if (locator<PatcherViewModel>().selectedApp?.originalPackageName != null) {
selectedPatches.addAll(
patches.where(
(element) => element.excluded == false && isPatchSupported(element),
),
_patcherAPI
.getFilteredPatches(
locator<PatcherViewModel>().selectedApp!.originalPackageName,
)
.where(
(element) =>
!element.excluded &&
(_managerAPI.areExperimentalPatchesEnabled() ||
isPatchSupported(element)),
),
);
}
if (_managerAPI.areExperimentalPatchesEnabled()) {
selectedPatches
.addAll(patches.where((element) => element.excluded == false));
}
notifyListeners();
}
@ -133,10 +175,10 @@ class PatchesSelectorViewModel extends BaseViewModel {
}
}
void onMenuSelection(value) {
void onMenuSelection(value, BuildContext context) {
switch (value) {
case 0:
loadSelectedPatches();
loadSelectedPatches(context);
break;
}
}
@ -150,18 +192,25 @@ class PatchesSelectorViewModel extends BaseViewModel {
);
}
Future<void> loadSelectedPatches() async {
final List<String> selectedPatches = await _managerAPI.getSelectedPatches(
locator<PatcherViewModel>().selectedApp!.originalPackageName,
);
if (selectedPatches.isNotEmpty) {
this.selectedPatches.clear();
this.selectedPatches.addAll(
patches.where((patch) => selectedPatches.contains(patch.name)),
);
Future<void> loadSelectedPatches(BuildContext context) async {
if (_managerAPI.isPatchesChangeEnabled()) {
final List<String> selectedPatches = await _managerAPI.getSelectedPatches(
locator<PatcherViewModel>().selectedApp!.originalPackageName,
);
if (selectedPatches.isNotEmpty) {
this.selectedPatches.clear();
this.selectedPatches.addAll(
patches.where((patch) => selectedPatches.contains(patch.name)),
);
if (!_managerAPI.areExperimentalPatchesEnabled()) {
this.selectedPatches.removeWhere((patch) => !isPatchSupported(patch));
}
} else {
locator<Toast>().showBottom('patchesSelectorView.noSavedPatches');
}
notifyListeners();
} else {
locator<Toast>().showBottom('patchesSelectorView.noSavedPatches');
showPatchesChangeDialog(context);
}
notifyListeners();
}
}

View file

@ -3,6 +3,8 @@ import 'package:cr_file_saver/file_saver.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:logcat/logcat.dart';
import 'package:path_provider/path_provider.dart';
import 'package:revanced_manager/app/app.locator.dart';
@ -10,8 +12,10 @@ import 'package:revanced_manager/app/app.router.dart';
import 'package:revanced_manager/services/manager_api.dart';
import 'package:revanced_manager/services/toast.dart';
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.dart';
import 'package:revanced_manager/ui/views/settings/settingsFragment/settings_update_language.dart';
import 'package:revanced_manager/ui/views/settings/settingsFragment/settings_update_theme.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
import 'package:share_extend/share_extend.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
@ -19,6 +23,9 @@ import 'package:stacked_services/stacked_services.dart';
class SettingsViewModel extends BaseViewModel {
final NavigationService _navigationService = locator<NavigationService>();
final ManagerAPI _managerAPI = locator<ManagerAPI>();
final PatchesSelectorViewModel _patchesSelectorViewModel =
PatchesSelectorViewModel();
final PatcherViewModel _patcherViewModel = locator<PatcherViewModel>();
final Toast _toast = locator<Toast>();
final SUpdateLanguage sUpdateLanguage = SUpdateLanguage();
@ -37,6 +44,88 @@ class SettingsViewModel extends BaseViewModel {
notifyListeners();
}
bool isPatchesChangeEnabled() {
return _managerAPI.isPatchesChangeEnabled();
}
Future<void> showPatchesChangeEnableDialog(
bool value,
BuildContext context,
) async {
if (value) {
return showDialog(
context: context,
builder: (context) => AlertDialog(
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
title: I18nText('warning'),
content: I18nText(
'settingsView.enablePatchesSelectionWarningText',
child: const Text(
'',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
),
actions: [
CustomMaterialButton(
isFilled: false,
label: I18nText('noButton'),
onPressed: () {
Navigator.of(context).pop();
},
),
CustomMaterialButton(
label: I18nText('yesButton'),
onPressed: () {
_managerAPI.setChangingToggleModified(true);
_managerAPI.setPatchesChangeEnabled(true);
Navigator.of(context).pop();
},
),
],
),
);
} else {
return showDialog(
context: context,
builder: (context) => AlertDialog(
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
title: I18nText('warning'),
content: I18nText(
'settingsView.disablePatchesSelectionWarningText',
child: const Text(
'',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
),
actions: [
CustomMaterialButton(
isFilled: false,
label: I18nText('noButton'),
onPressed: () {
Navigator.of(context).pop();
},
),
CustomMaterialButton(
label: I18nText('yesButton'),
onPressed: () {
_managerAPI.setChangingToggleModified(true);
_patchesSelectorViewModel.selectDefaultPatches();
_managerAPI.setPatchesChangeEnabled(false);
Navigator.of(context).pop();
},
),
],
),
);
}
}
bool areUniversalPatchesEnabled() {
return _managerAPI.areUniversalPatchesEnabled();
}
@ -90,26 +179,30 @@ class SettingsViewModel extends BaseViewModel {
}
}
Future<void> importPatches() async {
try {
final FilePickerResult? result = await FilePicker.platform.pickFiles(
type: FileType.custom,
allowedExtensions: ['json'],
);
if (result != null && result.files.single.path != null) {
final File inFile = File(result.files.single.path!);
inFile.copySync(_managerAPI.storedPatchesFile);
inFile.delete();
if (locator<PatcherViewModel>().selectedApp != null) {
locator<PatcherViewModel>().loadLastSelectedPatches();
Future<void> importPatches(BuildContext context) async {
if (isPatchesChangeEnabled()) {
try {
final FilePickerResult? result = await FilePicker.platform.pickFiles(
type: FileType.custom,
allowedExtensions: ['json'],
);
if (result != null && result.files.single.path != null) {
final File inFile = File(result.files.single.path!);
inFile.copySync(_managerAPI.storedPatchesFile);
inFile.delete();
if (_patcherViewModel.selectedApp != null) {
_patcherViewModel.loadLastSelectedPatches();
}
_toast.showBottom('settingsView.importedPatches');
}
_toast.showBottom('settingsView.importedPatches');
} on Exception catch (e) {
if (kDebugMode) {
print(e);
}
_toast.showBottom('settingsView.jsonSelectorErrorMessage');
}
} on Exception catch (e) {
if (kDebugMode) {
print(e);
}
_toast.showBottom('settingsView.jsonSelectorErrorMessage');
} else {
_managerAPI.showPatchesChangeWarningDialog(context);
}
}

View file

@ -19,6 +19,7 @@ class PatchItem extends StatefulWidget {
required this.isNew,
required this.isSelected,
required this.onChanged,
required this.isChangeEnabled,
this.child,
}) : super(key: key);
final String name;
@ -30,6 +31,7 @@ class PatchItem extends StatefulWidget {
final bool isNew;
bool isSelected;
final Function(bool) onChanged;
final bool isChangeEnabled;
final Widget? child;
final toast = locator<Toast>();
final _managerAPI = locator<ManagerAPI>();
@ -58,11 +60,13 @@ class _PatchItemState extends State<PatchItem> {
!widget._managerAPI.areExperimentalPatchesEnabled()) {
widget.isSelected = false;
widget.toast.showBottom('patchItem.unsupportedPatchVersion');
} else {
} else if (widget.isChangeEnabled) {
widget.isSelected = !widget.isSelected;
}
});
widget.onChanged(widget.isSelected);
if (!widget.isUnsupported || widget._managerAPI.areExperimentalPatchesEnabled()) {
widget.onChanged(widget.isSelected);
}
},
child: Column(
children: <Widget>[
@ -124,11 +128,13 @@ class _PatchItemState extends State<PatchItem> {
widget.toast.showBottom(
'patchItem.unsupportedPatchVersion',
);
} else {
} else if (widget.isChangeEnabled) {
widget.isSelected = newValue!;
}
});
widget.onChanged(widget.isSelected);
if (!widget.isUnsupported || widget._managerAPI.areExperimentalPatchesEnabled()) {
widget.onChanged(widget.isSelected);
}
},
),
),

View file

@ -5,6 +5,7 @@ import 'package:flutter_i18n/widgets/I18nText.dart';
import 'package:revanced_manager/ui/views/settings/settingsFragment/settings_manage_api_url.dart';
import 'package:revanced_manager/ui/views/settings/settingsFragment/settings_manage_sources.dart';
import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_enable_patches_selection.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_auto_update_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';
@ -25,6 +26,7 @@ class SAdvancedSection extends StatelessWidget {
SManageSourcesUI(),
// SManageKeystorePasswordUI(),
SAutoUpdatePatches(),
SEnablePatchesSelection(),
SExperimentalUniversalPatches(),
SExperimentalPatches(),
ListTile(

View file

@ -0,0 +1,37 @@
import 'package:flutter/material.dart';
import 'package:flutter_i18n/widgets/I18nText.dart';
import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart';
class SEnablePatchesSelection extends StatefulWidget {
const SEnablePatchesSelection({super.key});
@override
State<SEnablePatchesSelection> createState() => _SEnablePatchesSelectionState();
}
final _settingsViewModel = SettingsViewModel();
class _SEnablePatchesSelectionState extends State<SEnablePatchesSelection> {
@override
Widget build(BuildContext context) {
return SwitchListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
title: I18nText(
'settingsView.enablePatchesSelectionLabel',
child: const Text(
'',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
),
subtitle: I18nText('settingsView.enablePatchesSelectionHint'),
value: _settingsViewModel.isPatchesChangeEnabled(),
onChanged: (value) async {
await _settingsViewModel.showPatchesChangeEnableDialog(value, context);
setState(() {});
},
);
}
}

View file

@ -43,7 +43,7 @@ class SExportSection extends StatelessWidget {
),
),
subtitle: I18nText('settingsView.importPatchesHint'),
onTap: () => _settingsViewModel.importPatches(),
onTap: () => _settingsViewModel.importPatches(context),
),
ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
@ -73,10 +73,10 @@ class SExportSection extends StatelessWidget {
),
),
subtitle: I18nText('settingsView.importKeystoreHint'),
onTap: () async{
onTap: () async {
await _settingsViewModel.importKeystore();
final sManageKeystorePassword = SManageKeystorePassword();
if(context.mounted){
if (context.mounted) {
sManageKeystorePassword.showKeystoreDialog(context);
}
},