diff --git a/assets/i18n/en_US.json b/assets/i18n/en_US.json index 8b13f9ee..ea0c717a 100644 --- a/assets/i18n/en_US.json +++ b/assets/i18n/en_US.json @@ -84,7 +84,8 @@ "patchItem": { "unsupportedWarningButton": "Warning", "unsupportedDialogTitle": "Warning", - "unsupportedDialogText": "Selecting this patch may result in patching errors.\n\nApp version: {packageVersion}\nCurrent supported versions:\n{supportedVersions}" + "unsupportedDialogText": "Selecting this patch may result in patching errors.\n\nApp version: {packageVersion}\nCurrent supported versions:\n{supportedVersions}", + "unsupportedPatchVersion": "Patch is not supported for this app version. Enable experimental toggle in settings to proceed." }, "installerView": { "widgetTitle": "Installer", @@ -132,6 +133,9 @@ "apiURLLabel": "API URL", "apiURLHint": "Configure your custom API URL", "selectApiURL": "Select URL", + "experimentalPatchesLabel": "Experimental Patch support", + "experimentalPatchesHint": "Enable to use unsupported patches in any app version", + "enabledExperimentalPatches": "Experimental patches enabled", "aboutLabel": "About", "snackbarMessage": "Copied to clipboard", "sentryLabel": "Sentry Logging", diff --git a/lib/services/manager_api.dart b/lib/services/manager_api.dart index 5ba3c5a5..f3fb56db 100644 --- a/lib/services/manager_api.dart +++ b/lib/services/manager_api.dart @@ -90,6 +90,14 @@ class ManagerAPI { // await _prefs.setBool('sentryEnabled', value); // } + bool areExperimentalPatchesEnabled() { + return _prefs.getBool('experimentalPatchesEnabled') ?? false; + } + + Future enableExperimentalPatchesStatus(bool value) async { + await _prefs.setBool('experimentalPatchesEnabled', value); + } + Future deleteTempFolder() async { final Directory dir = Directory('/data/local/tmp/revanced-manager'); if (await dir.exists()) { diff --git a/lib/ui/views/settings/settings_view.dart b/lib/ui/views/settings/settings_view.dart index 69d49883..213040aa 100644 --- a/lib/ui/views/settings/settings_view.dart +++ b/lib/ui/views/settings/settings_view.dart @@ -136,6 +136,23 @@ class SettingsView extends StatelessWidget { subtitle: 'settingsView.sourcesLabelHint', onTap: () => model.showSourcesDialog(context), ), + CustomSwitchTile( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + title: I18nText( + 'settingsView.experimentalPatchesLabel', + child: const Text( + '', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.w500, + ), + ), + ), + subtitle: + I18nText('settingsView.experimentalPatchesHint'), + value: model.areExperimentalPatchesEnabled(), + onTap: (value) => + model.useExperimentalPatches(value)), ListTile( contentPadding: const EdgeInsets.symmetric(horizontal: 20.0), diff --git a/lib/ui/views/settings/settings_viewmodel.dart b/lib/ui/views/settings/settings_viewmodel.dart index 15dc7867..9216d2f3 100644 --- a/lib/ui/views/settings/settings_viewmodel.dart +++ b/lib/ui/views/settings/settings_viewmodel.dart @@ -325,6 +325,16 @@ class SettingsViewModel extends BaseViewModel { // notifyListeners(); // } + bool areExperimentalPatchesEnabled() { + return _managerAPI.areExperimentalPatchesEnabled(); + } + + void useExperimentalPatches(bool value) { + _managerAPI.enableExperimentalPatchesStatus(value); + _toast.showBottom('settingsView.enabledExperimentalPatches'); + notifyListeners(); + } + void deleteKeystore() { _managerAPI.deleteKeystore(); _toast.showBottom('settingsView.deletedKeystore'); diff --git a/lib/ui/widgets/patchesSelectorView/patch_item.dart b/lib/ui/widgets/patchesSelectorView/patch_item.dart index 8d6c8ce2..5772958e 100644 --- a/lib/ui/widgets/patchesSelectorView/patch_item.dart +++ b/lib/ui/widgets/patchesSelectorView/patch_item.dart @@ -1,5 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; +import 'package:revanced_manager/app/app.locator.dart'; +import 'package:revanced_manager/services/manager_api.dart'; +import 'package:revanced_manager/services/toast.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_card.dart'; @@ -15,6 +18,8 @@ class PatchItem extends StatefulWidget { bool isSelected; final Function(bool) onChanged; final Widget? child; + final toast = locator(); + final _managerAPI = locator(); PatchItem( {Key? key, @@ -40,8 +45,21 @@ class _PatchItemState extends State { return Padding( padding: const EdgeInsets.symmetric(vertical: 4.0), child: CustomCard( + backgroundColor: widget.isUnsupported && + widget._managerAPI.areExperimentalPatchesEnabled() == false + ? Theme.of(context).colorScheme.brightness == Brightness.light + ? Colors.grey[400] + : Colors.grey[700] + : null, onTap: () { - setState(() => widget.isSelected = !widget.isSelected); + setState(() { + if (widget.isUnsupported) { + widget.isSelected = false; + widget.toast.showBottom('patchItem.unsupportedPatchVersion'); + } else { + widget.isSelected = !widget.isSelected; + } + }); widget.onChanged(widget.isSelected); }, child: Column( @@ -83,7 +101,9 @@ class _PatchItemState extends State { overflow: TextOverflow.visible, style: TextStyle( fontSize: 14, - color: Theme.of(context).colorScheme.onSecondaryContainer, + color: Theme.of(context) + .colorScheme + .onSecondaryContainer, ), ), ], @@ -101,7 +121,18 @@ class _PatchItemState extends State { color: Theme.of(context).colorScheme.primary, ), onChanged: (newValue) { - setState(() => widget.isSelected = newValue!); + setState(() { + if (widget.isUnsupported && + widget._managerAPI + .areExperimentalPatchesEnabled() == + false) { + widget.isSelected = false; + widget.toast + .showBottom('patchItem.unsupportedPatchVersion'); + } else { + widget.isSelected = newValue!; + } + }); widget.onChanged(widget.isSelected); }, ), diff --git a/lib/ui/widgets/shared/custom_card.dart b/lib/ui/widgets/shared/custom_card.dart index 5092458f..6e8c1be5 100644 --- a/lib/ui/widgets/shared/custom_card.dart +++ b/lib/ui/widgets/shared/custom_card.dart @@ -5,22 +5,25 @@ class CustomCard extends StatelessWidget { final Widget child; final Function()? onTap; final EdgeInsetsGeometry? padding; + final Color? backgroundColor; - const CustomCard({ - Key? key, - this.isFilled = true, - required this.child, - this.onTap, - this.padding, - }) : super(key: key); + const CustomCard( + {Key? key, + this.isFilled = true, + required this.child, + this.onTap, + this.padding, + this.backgroundColor}) + : super(key: key); @override Widget build(BuildContext context) { return Material( type: isFilled ? MaterialType.card : MaterialType.transparency, color: isFilled - ? Theme.of(context).colorScheme.secondaryContainer.withOpacity(0.4) - : Colors.transparent, + ? backgroundColor?.withOpacity(0.4) ?? + Theme.of(context).colorScheme.secondaryContainer.withOpacity(0.4) + : backgroundColor ?? Colors.transparent, borderRadius: BorderRadius.circular(16), child: InkWell( onTap: onTap,