diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/Utils.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/Utils.java index 037744c7e..bc18772de 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/Utils.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/Utils.java @@ -722,8 +722,8 @@ public class Utils { Preference preference = group.getPreference(i); final Sort preferenceSort; - if (preference instanceof PreferenceGroup) { - sortPreferenceGroups((PreferenceGroup) preference); + if (preference instanceof PreferenceGroup subGroup) { + sortPreferenceGroups(subGroup); preferenceSort = groupSort; // Sort value for groups is for it's content, not itself. } else { // Allow individual preferences to set a key sorting. diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/ThemeHelper.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/ThemeHelper.java index 9606587dd..c4887b8e3 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/ThemeHelper.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/ThemeHelper.java @@ -90,4 +90,12 @@ public class ThemeHelper { public static int getForegroundColor() { return isDarkTheme() ? getLightThemeColor() : getDarkThemeColor(); } + + public static int getToolbarBackgroundColor() { + final String colorName = isDarkTheme() + ? "yt_black3" + : "yt_white1"; + + return getColorInt(colorName); + } } diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/LicenseActivityHook.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/LicenseActivityHook.java index 1812ca5c5..8ce9172b8 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/LicenseActivityHook.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/LicenseActivityHook.java @@ -1,26 +1,31 @@ package app.revanced.extension.youtube.settings; +import static app.revanced.extension.shared.Utils.getResourceIdentifier; + import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; +import android.os.Build; import android.preference.PreferenceFragment; +import android.util.TypedValue; import android.view.ViewGroup; -import android.widget.ImageButton; import android.widget.TextView; +import android.widget.Toolbar; + +import androidx.annotation.RequiresApi; + +import java.util.Objects; + import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.settings.AppLanguage; import app.revanced.extension.shared.settings.BaseSettings; import app.revanced.extension.youtube.ThemeHelper; +import app.revanced.extension.youtube.patches.VersionCheckPatch; import app.revanced.extension.youtube.settings.preference.ReVancedPreferenceFragment; import app.revanced.extension.youtube.settings.preference.ReturnYouTubeDislikePreferenceFragment; import app.revanced.extension.youtube.settings.preference.SponsorBlockPreferenceFragment; -import java.util.Objects; - -import static app.revanced.extension.shared.Utils.getChildView; -import static app.revanced.extension.shared.Utils.getResourceIdentifier; - /** * Hooks LicenseActivity. *

@@ -29,6 +34,14 @@ import static app.revanced.extension.shared.Utils.getResourceIdentifier; @SuppressWarnings("unused") public class LicenseActivityHook { + private static ViewGroup.LayoutParams toolbarLayoutParams; + + public static void setToolbarLayoutParams(Toolbar toolbar) { + if (toolbarLayoutParams != null) { + toolbar.setLayoutParams(toolbarLayoutParams); + } + } + /** * Injection point. * Overrides the ReVanced settings language. @@ -42,17 +55,36 @@ public class LicenseActivityHook { return Utils.getContext(); } + /** + * Injection point. + */ + public static boolean useCairoSettingsFragment(boolean original) { + // Early targets have layout issues and it's better to always force off. + if (!VersionCheckPatch.IS_19_34_OR_GREATER) { + return false; + } + if (Settings.RESTORE_OLD_SETTINGS_MENUS.get()) { + return false; + } + + // On the first launch of a clean install, forcing the cairo menu can give a + // half broken appearance because all the preference icons may not be available yet. + // 19.34+ cairo settings are always on, so it doesn't need to be forced anyway. + // Cairo setting will show on the next launch of the app. + return original; + } + /** * Injection point. *

* Hooks LicenseActivity#onCreate in order to inject our own fragment. */ + @RequiresApi(api = Build.VERSION_CODES.N) public static void initialize(Activity licenseActivity) { try { ThemeHelper.setActivityTheme(licenseActivity); - licenseActivity.setContentView( - getResourceIdentifier("revanced_settings_with_toolbar", "layout")); - setBackButton(licenseActivity); + licenseActivity.setContentView(getResourceIdentifier( + "revanced_settings_with_toolbar", "layout")); PreferenceFragment fragment; String toolbarTitleResourceName; @@ -75,7 +107,7 @@ public class LicenseActivityHook { return; } - setToolbarTitle(licenseActivity, toolbarTitleResourceName); + createToolbar(licenseActivity, toolbarTitleResourceName); //noinspection deprecation licenseActivity.getFragmentManager() @@ -87,28 +119,36 @@ public class LicenseActivityHook { } } - private static void setToolbarTitle(Activity activity, String toolbarTitleResourceName) { - ViewGroup toolbar = activity.findViewById(getToolbarResourceId()); - TextView toolbarTextView = Objects.requireNonNull(getChildView(toolbar, false, - view -> view instanceof TextView)); - toolbarTextView.setText(getResourceIdentifier(toolbarTitleResourceName, "string")); - } - + @RequiresApi(api = Build.VERSION_CODES.N) @SuppressLint("UseCompatLoadingForDrawables") - private static void setBackButton(Activity activity) { - ViewGroup toolbar = activity.findViewById(getToolbarResourceId()); - ImageButton imageButton = Objects.requireNonNull(getChildView(toolbar, false, - view -> view instanceof ImageButton)); - imageButton.setImageDrawable(ReVancedPreferenceFragment.getBackButtonDrawable()); - imageButton.setOnClickListener(view -> activity.onBackPressed()); - } + private static void createToolbar(Activity activity, String toolbarTitleResourceName) { + // Replace dummy placeholder toolbar. + // This is required to fix submenu title alignment issue with Android ASOP 15+ + ViewGroup toolBarParent = activity.findViewById( + getResourceIdentifier("revanced_toolbar_parent", "id")); + ViewGroup dummyToolbar = toolBarParent.findViewById(getResourceIdentifier( + "revanced_toolbar", "id")); + toolbarLayoutParams = dummyToolbar.getLayoutParams(); + toolBarParent.removeView(dummyToolbar); - private static int getToolbarResourceId() { - final int toolbarResourceId = getResourceIdentifier("revanced_toolbar", "id"); - if (toolbarResourceId == 0) { - throw new IllegalStateException("Could not find back button resource"); + Toolbar toolbar = new Toolbar(toolBarParent.getContext()); + toolbar.setBackgroundColor(ThemeHelper.getToolbarBackgroundColor()); + toolbar.setNavigationIcon(ReVancedPreferenceFragment.getBackButtonDrawable()); + toolbar.setNavigationOnClickListener(view -> activity.onBackPressed()); + toolbar.setTitle(getResourceIdentifier(toolbarTitleResourceName, "string")); + + final int margin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, + Utils.getContext().getResources().getDisplayMetrics()); + toolbar.setTitleMarginStart(margin); + toolbar.setTitleMarginEnd(margin); + TextView toolbarTextView = Utils.getChildView(toolbar, false, + view -> view instanceof TextView); + if (toolbarTextView != null) { + toolbarTextView.setTextColor(ThemeHelper.getForegroundColor()); } - return toolbarResourceId; + setToolbarLayoutParams(toolbar); + + toolBarParent.addView(toolbar, 0); } } diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java index c14b8b7d3..5817e5aca 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/Settings.java @@ -204,6 +204,7 @@ public class Settings extends BaseSettings { public static final BooleanSetting HIDE_PLAYER_FLYOUT_WATCH_IN_VR = new BooleanSetting("revanced_hide_player_flyout_watch_in_vr", TRUE); // General layout + public static final BooleanSetting RESTORE_OLD_SETTINGS_MENUS = new BooleanSetting("revanced_restore_old_settings_menus", FALSE, true); public static final EnumSetting CHANGE_FORM_FACTOR = new EnumSetting<>("revanced_change_form_factor", FormFactor.DEFAULT, true, "revanced_change_form_factor_user_dialog_message"); public static final BooleanSetting BYPASS_IMAGE_REGION_RESTRICTIONS = new BooleanSetting("revanced_bypass_image_region_restrictions", FALSE, true); public static final BooleanSetting GRADIENT_LOADING_SCREEN = new BooleanSetting("revanced_gradient_loading_screen", FALSE, true); diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/ReVancedPreferenceFragment.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/ReVancedPreferenceFragment.java index 02804c8c9..00cf12a35 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/ReVancedPreferenceFragment.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/ReVancedPreferenceFragment.java @@ -30,6 +30,7 @@ import app.revanced.extension.shared.settings.EnumSetting; import app.revanced.extension.shared.settings.preference.AbstractPreferenceFragment; import app.revanced.extension.youtube.ThemeHelper; import app.revanced.extension.youtube.patches.playback.speed.CustomPlaybackSpeedPatch; +import app.revanced.extension.youtube.settings.LicenseActivityHook; import app.revanced.extension.youtube.settings.Settings; /** @@ -140,9 +141,6 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment { .getParent(); // Fix required for Android 15 and YT 19.45+ - // FIXME: - // On Android 15 the text layout is not aligned the same as the parent - // screen and it looks a little off. Otherwise this works. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { rootView.setOnApplyWindowInsetsListener((v, insets) -> { Insets statusInsets = insets.getInsets(WindowInsets.Type.statusBars()); @@ -169,6 +167,8 @@ public class ReVancedPreferenceFragment extends AbstractPreferenceFragment { toolbarTextView.setTextColor(ThemeHelper.getForegroundColor()); } + LicenseActivityHook.setToolbarLayoutParams(toolbar); + rootView.addView(toolbar, 0); return false; } diff --git a/patches/api/patches.api b/patches/api/patches.api index d9e095cca..a4075b866 100644 --- a/patches/api/patches.api +++ b/patches/api/patches.api @@ -603,16 +603,19 @@ public final class app/revanced/patches/shared/misc/mapping/ResourceMappingPatch } public final class app/revanced/patches/shared/misc/settings/SettingsPatchKt { + public static final fun settingsPatch (Ljava/util/List;Ljava/util/Set;)Lapp/revanced/patcher/patch/ResourcePatch; public static final fun settingsPatch (Lkotlin/Pair;Ljava/util/Set;)Lapp/revanced/patcher/patch/ResourcePatch; - public static synthetic fun settingsPatch$default (Lkotlin/Pair;Ljava/util/Set;ILjava/lang/Object;)Lapp/revanced/patcher/patch/ResourcePatch; + public static synthetic fun settingsPatch$default (Ljava/util/List;Ljava/util/Set;ILjava/lang/Object;)Lapp/revanced/patcher/patch/ResourcePatch; } public abstract class app/revanced/patches/shared/misc/settings/preference/BasePreference { public static final field Companion Lapp/revanced/patches/shared/misc/settings/preference/BasePreference$Companion; - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun equals (Ljava/lang/Object;)Z + public final fun getIcon ()Ljava/lang/String; public final fun getKey ()Ljava/lang/String; + public final fun getLayout ()Ljava/lang/String; public final fun getSummaryKey ()Ljava/lang/String; public final fun getTag ()Ljava/lang/String; public final fun getTitleKey ()Ljava/lang/String; @@ -635,17 +638,19 @@ public abstract class app/revanced/patches/shared/misc/settings/preference/BaseP public abstract class app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$BasePreferenceCollection { public fun ()V - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun getIcon ()Ljava/lang/String; public final fun getKey ()Ljava/lang/String; + public final fun getLayout ()Ljava/lang/String; public final fun getPreferences ()Ljava/util/Set; public final fun getTitleKey ()Ljava/lang/String; public abstract fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreference; } public class app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen : app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$BasePreferenceCollection { - public fun (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;)V - public synthetic fun (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;)V + public synthetic fun (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun addPreferences ([Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;)V public final fun getCategories ()Ljava/util/Set; public synthetic fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreference; @@ -653,8 +658,8 @@ public class app/revanced/patches/shared/misc/settings/preference/BasePreference } public class app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen$Category : app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$BasePreferenceCollection { - public fun (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V - public synthetic fun (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V + public synthetic fun (Lapp/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen$Screen;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun addPreferences ([Lapp/revanced/patches/shared/misc/settings/preference/BasePreference;)V public synthetic fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/BasePreference; public fun transform ()Lapp/revanced/patches/shared/misc/settings/preference/PreferenceCategory; @@ -673,8 +678,8 @@ public final class app/revanced/patches/shared/misc/settings/preference/InputTyp } public final class app/revanced/patches/shared/misc/settings/preference/IntentPreference : app/revanced/patches/shared/misc/settings/preference/BasePreference { - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun equals (Ljava/lang/Object;)Z public final fun getIntent ()Lapp/revanced/patches/shared/misc/settings/preference/IntentPreference$Intent; public fun hashCode ()I @@ -694,8 +699,8 @@ public final class app/revanced/patches/shared/misc/settings/preference/ListPref public fun ()V public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/util/resource/ArrayResource;Lapp/revanced/util/resource/ArrayResource;)V public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/util/resource/ArrayResource;Lapp/revanced/util/resource/ArrayResource;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getEntries ()Lapp/revanced/util/resource/ArrayResource; public final fun getEntriesKey ()Ljava/lang/String; public final fun getEntryValues ()Lapp/revanced/util/resource/ArrayResource; @@ -704,22 +709,22 @@ public final class app/revanced/patches/shared/misc/settings/preference/ListPref } public final class app/revanced/patches/shared/misc/settings/preference/NonInteractivePreference : app/revanced/patches/shared/misc/settings/preference/BasePreference { - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getSelectable ()Z public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; } public class app/revanced/patches/shared/misc/settings/preference/PreferenceCategory : app/revanced/patches/shared/misc/settings/preference/BasePreference { - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getPreferences ()Ljava/util/Set; public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; } public class app/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference : app/revanced/patches/shared/misc/settings/preference/BasePreference { - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting;Ljava/lang/String;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getPreferences ()Ljava/util/Set; public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; } @@ -728,6 +733,7 @@ public final class app/revanced/patches/shared/misc/settings/preference/Preferen public static final field BY_KEY Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting; public static final field BY_TITLE Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting; public static final field UNSORTED Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting; + public final fun appendSortType (Ljava/lang/String;)Ljava/lang/String; public static fun getEntries ()Lkotlin/enums/EnumEntries; public final fun getKeySuffix ()Ljava/lang/String; public static fun valueOf (Ljava/lang/String;)Lapp/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference$Sorting; @@ -746,8 +752,8 @@ public final class app/revanced/patches/shared/misc/settings/preference/SummaryT public final class app/revanced/patches/shared/misc/settings/preference/SwitchPreference : app/revanced/patches/shared/misc/settings/preference/BasePreference { public fun ()V - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getSummaryOffKey ()Ljava/lang/String; public final fun getSummaryOnKey ()Ljava/lang/String; public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; @@ -755,8 +761,8 @@ public final class app/revanced/patches/shared/misc/settings/preference/SwitchPr public final class app/revanced/patches/shared/misc/settings/preference/TextPreference : app/revanced/patches/shared/misc/settings/preference/BasePreference { public fun ()V - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/InputType;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/InputType;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/InputType;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lapp/revanced/patches/shared/misc/settings/preference/InputType;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getInputType ()Lapp/revanced/patches/shared/misc/settings/preference/InputType; public fun serialize (Lorg/w3c/dom/Document;Lkotlin/jvm/functions/Function1;)Lorg/w3c/dom/Element; } diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/SettingsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/SettingsPatch.kt index 7c343961c..d6d9df94c 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/SettingsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/SettingsPatch.kt @@ -1,5 +1,6 @@ package app.revanced.patches.shared.misc.settings +import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.resourcePatch import app.revanced.patches.all.misc.resources.addResource import app.revanced.patches.all.misc.resources.addResources @@ -12,15 +13,22 @@ import app.revanced.util.getNode import app.revanced.util.insertFirst import org.w3c.dom.Node +// TODO: Delete this on next major version bump. +@Deprecated("Use non deprecated settings patch function") +fun settingsPatch ( + rootPreference: Pair, + preferences: Set, +) = settingsPatch(listOf(rootPreference), preferences) + /** * A resource patch that adds settings to a settings fragment. * - * @param rootPreference A pair of an intent preference and the name of the fragment file to add it to. - * If null, no preference will be added. + * @param rootPreferences List of intent preferences and the name of the fragment file to add it to. + * File names that do not exist are ignored and not processed. * @param preferences A set of preferences to add to the ReVanced fragment. */ -fun settingsPatch( - rootPreference: Pair? = null, +fun settingsPatch ( + rootPreferences: List>? = null, preferences: Set, ) = resourcePatch { dependsOn(addResourcesPatch) @@ -46,10 +54,20 @@ fun settingsPatch( } // Add the root preference to an existing fragment if needed. - rootPreference?.let { (intentPreference, fragment) -> - document("res/xml/$fragment.xml").use { document -> - document.getNode("PreferenceScreen").addPreference(intentPreference, true) + rootPreferences?.let { + var modified = false + + it.forEach { (intent, fileName) -> + val preferenceFileName = "res/xml/$fileName.xml" + if (get(preferenceFileName).exists()) { + document(preferenceFileName).use { document -> + document.getNode("PreferenceScreen").addPreference(intent, true) + } + modified = true + } } + + if (!modified) throw PatchException("No declared preference files exists: $rootPreferences") } // Add all preferences to the ReVanced fragment. diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreference.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreference.kt index 509d0eea3..7c5cbf2cd 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreference.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreference.kt @@ -9,6 +9,8 @@ import org.w3c.dom.Element * * @param key The key of the preference. If null, other parameters must be specified. * @param titleKey The key of the preference title. + * @param icon The preference icon resource name. + * @param layout Layout declaration. * @param summaryKey The key of the preference summary. * @param tag The tag or full class name of the preference. */ @@ -17,6 +19,8 @@ abstract class BasePreference( val key: String? = null, val titleKey: String = "${key}_title", val summaryKey: String? = "${key}_summary", + val icon: String? = null, + val layout: String? = null, val tag: String ) { /** @@ -33,6 +37,11 @@ abstract class BasePreference( key?.let { setAttribute("android:key", it) } setAttribute("android:title", "@string/${titleKey}") summaryKey?.let { addSummary(it) } + icon?.let { + setAttribute("android:icon", it) + setAttribute("app:iconSpaceReserved", "true") + } + layout?.let { setAttribute("android:layout", layout) } } override fun hashCode(): Int { diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen.kt index 648f5eefd..8590e9965 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/BasePreferenceScreen.kt @@ -24,16 +24,20 @@ abstract class BasePreferenceScreen( key: String? = null, titleKey: String = "${key}_title", private val summaryKey: String? = "${key}_summary", + icon: String? = null, + layout: String? = null, preferences: MutableSet = mutableSetOf(), val categories: MutableSet = mutableSetOf(), private val sorting: Sorting = Sorting.BY_TITLE, - ) : BasePreferenceCollection(key, titleKey, preferences) { + ) : BasePreferenceCollection(key, titleKey, icon, layout, preferences) { override fun transform(): PreferenceScreenPreference { return PreferenceScreenPreference( key, titleKey, summaryKey, + icon, + layout, sorting, // Screens and preferences are sorted at runtime by extension code, // so title sorting uses the localized language in use. @@ -56,12 +60,17 @@ abstract class BasePreferenceScreen( open inner class Category( key: String? = null, titleKey: String = "${key}_title", + icon: String? = null, + layout: String? = null, preferences: MutableSet = mutableSetOf(), - ) : BasePreferenceCollection(key, titleKey, preferences) { + ) : BasePreferenceCollection(key, titleKey, icon, layout, preferences) { override fun transform(): PreferenceCategory { return PreferenceCategory( key, titleKey, + icon, + layout, + sorting, preferences = preferences, ) } @@ -82,6 +91,8 @@ abstract class BasePreferenceScreen( abstract class BasePreferenceCollection( val key: String? = null, val titleKey: String = "${key}_title", + val icon: String? = null, + val layout: String? = null, val preferences: MutableSet = mutableSetOf(), ) { abstract fun transform(): BasePreference diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/IntentPreference.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/IntentPreference.kt index dbc109ae8..2aef02dc8 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/IntentPreference.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/IntentPreference.kt @@ -9,6 +9,8 @@ import org.w3c.dom.Document * @param key Optional preference key. * @param titleKey The preference title key. * @param summaryKey The preference summary key. + * @param icon The preference icon resource name. + * @param layout Layout declaration. * @param tag The preference tag. * @param intent The intent to open. */ @@ -16,9 +18,11 @@ class IntentPreference( key: String? = null, titleKey: String = "${key}_title", summaryKey: String? = "${key}_summary", + icon: String? = null, + layout: String? = null, tag: String = "Preference", val intent: Intent, -) : BasePreference(key, titleKey, summaryKey, tag) { +) : BasePreference(key, titleKey, summaryKey, icon, layout, tag) { override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) = super.serialize(ownerDocument, resourceCallback).apply { diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/ListPreference.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/ListPreference.kt index e0338d29c..e6d9b440b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/ListPreference.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/ListPreference.kt @@ -10,6 +10,8 @@ import org.w3c.dom.Document * @param key The preference key. If null, other parameters must be specified. * @param titleKey The preference title key. * @param summaryKey The preference summary key. + * @param icon The preference icon resource name. + * @param layout Layout declaration. * @param tag The preference tag. * @param entriesKey The entries array key. * @param entryValuesKey The entry values array key. @@ -19,10 +21,12 @@ class ListPreference( key: String? = null, titleKey: String = "${key}_title", summaryKey: String? = "${key}_summary", + icon: String? = null, + layout: String? = null, tag: String = "ListPreference", val entriesKey: String? = "${key}_entries", val entryValuesKey: String? = "${key}_entry_values" -) : BasePreference(key, titleKey, summaryKey, tag) { +) : BasePreference(key, titleKey, summaryKey, icon, layout, tag) { var entries: ArrayResource? = null private set var entryValues: ArrayResource? = null diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/NonInteractivePreference.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/NonInteractivePreference.kt index d4ecaae7e..b5ce55489 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/NonInteractivePreference.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/NonInteractivePreference.kt @@ -10,6 +10,8 @@ import org.w3c.dom.Document * * @param key The preference key. * @param summaryKey The preference summary key. + * @param icon The preference icon resource name. + * @param layout Layout declaration. * @param tag The tag or full class name of the preference. * @param selectable If the preference is selectable and responds to tap events. */ @@ -18,9 +20,11 @@ class NonInteractivePreference( key: String, titleKey: String = "${key}_title", summaryKey: String? = "${key}_summary", + icon: String? = null, + layout: String? = null, tag: String = "Preference", val selectable: Boolean = false, -) : BasePreference(key, titleKey, summaryKey, tag) { +) : BasePreference(key, titleKey, summaryKey, icon, layout, tag) { override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) = super.serialize(ownerDocument, resourceCallback).apply { setAttribute("android:selectable", selectable.toString()) diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceCategory.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceCategory.kt index 67e82208f..8b2deb96e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceCategory.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceCategory.kt @@ -1,5 +1,6 @@ package app.revanced.patches.shared.misc.settings.preference +import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference.Sorting import app.revanced.util.resource.BaseResource import org.w3c.dom.Document @@ -8,6 +9,8 @@ import org.w3c.dom.Document * * @param key The key of the preference. If null, other parameters must be specified. * @param titleKey The key of the preference title. + * @param icon The preference icon resource name. + * @param layout Layout declaration. * @param tag The tag or full class name of the preference. * @param preferences The preferences in this category. */ @@ -15,9 +18,12 @@ import org.w3c.dom.Document open class PreferenceCategory( key: String? = null, titleKey: String = "${key}_title", + icon: String? = null, + layout: String? = null, + sorting: Sorting = Sorting.BY_TITLE, tag: String = "PreferenceCategory", val preferences: Set -) : BasePreference(key, titleKey, null, tag) { +) : BasePreference(sorting.appendSortType(key), titleKey, null, icon, layout, tag) { override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) = super.serialize(ownerDocument, resourceCallback).apply { diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference.kt index 2b21a8413..a37e92947 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/PreferenceScreenPreference.kt @@ -9,6 +9,8 @@ import org.w3c.dom.Document * @param key The key of the preference. If null, other parameters must be specified. * @param titleKey The key of the preference title. * @param summaryKey The key of the preference summary. + * @param icon The preference icon resource name. + * @param layout Layout declaration. * @param sorting Sorting to use. If the sorting is not [Sorting.UNSORTED], * then the key parameter will be modified to include the sort type. * @param tag The tag or full class name of the preference. @@ -19,6 +21,8 @@ open class PreferenceScreenPreference( key: String? = null, titleKey: String = "${key}_title", summaryKey: String? = "${key}_summary", + icon: String? = null, + layout: String? = null, sorting: Sorting = Sorting.BY_TITLE, tag: String = "PreferenceScreen", val preferences: Set, @@ -28,7 +32,7 @@ open class PreferenceScreenPreference( // or adding new attributes to the attrs.xml file. // Since the key value is not currently used by the extensions, // for now it's much simpler to modify the key to include the sort parameter. -) : BasePreference(if (sorting == Sorting.UNSORTED) key else (key + sorting.keySuffix), titleKey, summaryKey, tag) { +) : BasePreference(sorting.appendSortType(key), titleKey, summaryKey, icon, layout, tag) { override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) = super.serialize(ownerDocument, resourceCallback).apply { preferences.forEach { @@ -53,6 +57,16 @@ open class PreferenceScreenPreference( /** * Unspecified sorting. */ - UNSORTED("_sort_by_unsorted"), + UNSORTED("_sort_by_unsorted"); + + /** + * @return The key with this sort type appended to to the end, + * or if key is null then null is returned. + */ + fun appendSortType(key: String?): String? { + if (key == null) return null + if (this == UNSORTED) return key + return key + keySuffix + } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/SwitchPreference.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/SwitchPreference.kt index e09d813a3..df9accd4b 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/SwitchPreference.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/SwitchPreference.kt @@ -8,6 +8,8 @@ import org.w3c.dom.Document * * @param key The preference key. If null, other parameters must be specified. * @param titleKey The preference title key. + * @param icon The preference icon resource name. + * @param layout Layout declaration. * @param tag The preference tag. * @param summaryOnKey The preference summary-on key. * @param summaryOffKey The preference summary-off key. @@ -17,9 +19,11 @@ class SwitchPreference( key: String? = null, titleKey: String = "${key}_title", tag: String = "SwitchPreference", + icon: String? = null, + layout: String? = null, val summaryOnKey: String = "${key}_summary_on", val summaryOffKey: String = "${key}_summary_off" -) : BasePreference(key, titleKey, null, tag) { +) : BasePreference(key, titleKey, null, icon, layout, tag) { override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) = super.serialize(ownerDocument, resourceCallback).apply { addSummary(summaryOnKey, SummaryType.ON) diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/TextPreference.kt b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/TextPreference.kt index 81de61569..397d4bd43 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/TextPreference.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/misc/settings/preference/TextPreference.kt @@ -9,6 +9,8 @@ import org.w3c.dom.Document * @param key The preference key. If null, other parameters must be specified. * @param titleKey The preference title key. * @param summaryKey The preference summary key. + * @param icon The preference icon resource name. + * @param layout Layout declaration. * @param tag The preference tag. * @param inputType The preference input type. */ @@ -17,9 +19,11 @@ class TextPreference( key: String? = null, titleKey: String = "${key}_title", summaryKey: String? = "${key}_summary", + icon: String? = null, + layout: String? = null, tag: String = "app.revanced.extension.shared.settings.preference.ResettableEditTextPreference", val inputType: InputType = InputType.TEXT -) : BasePreference(key, titleKey, summaryKey, tag) { +) : BasePreference(key, titleKey, summaryKey, icon, layout, tag) { override fun serialize(ownerDocument: Document, resourceCallback: (BaseResource) -> Unit) = super.serialize(ownerDocument, resourceCallback).apply { diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/NavigationButtonsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/NavigationButtonsPatch.kt index 1a0dc6665..0f7647b4c 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/NavigationButtonsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/NavigationButtonsPatch.kt @@ -65,9 +65,12 @@ val navigationButtonsPatch = bytecodePatch( ) if (is_19_25_or_greater) { - preferences += SwitchPreference("revanced_disable_translucent_status_bar") preferences += SwitchPreference("revanced_disable_translucent_navigation_bar_light") preferences += SwitchPreference("revanced_disable_translucent_navigation_bar_dark") + + PreferenceScreen.GENERAL_LAYOUT.addPreferences( + SwitchPreference("revanced_disable_translucent_status_bar") + ) } PreferenceScreen.GENERAL_LAYOUT.addPreferences( diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/cairo/DisableCairoSettingsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/cairo/DisableCairoSettingsPatch.kt deleted file mode 100644 index cd058ca59..000000000 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/cairo/DisableCairoSettingsPatch.kt +++ /dev/null @@ -1,47 +0,0 @@ -package app.revanced.patches.youtube.misc.fix.cairo - -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patches.youtube.misc.backgroundplayback.backgroundPlaybackPatch -import app.revanced.patches.youtube.misc.playservice.is_19_04_or_greater -import app.revanced.patches.youtube.misc.playservice.versionCheckPatch -import app.revanced.util.indexOfFirstInstructionOrThrow -import app.revanced.util.indexOfFirstLiteralInstructionOrThrow -import com.android.tools.smali.dexlib2.Opcode -import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction - -internal val disableCairoSettingsPatch = bytecodePatch( - description = "Disables Cairo Fragment from being used.", -) { - dependsOn(versionCheckPatch) - - execute { - if (!is_19_04_or_greater) { - return@execute - } - - /** - *

-         * Cairo Fragment was added since YouTube v19.04.38.
-         *
-         * Disable this for the following reasons:
-         * 1. [backgroundPlaybackPatch] does not activate the Minimized playback setting of Cairo Fragment.
-         * 2. Some patches do not yet support Cairo Fragments (ie: custom Seekbar color).
-         * 3. Settings preferences added by ReVanced are missing.
-         *
-         * Screenshots of the Cairo Fragment:
-         * uYouPlus#1468.
-         */
-        cairoFragmentConfigFingerprint.method.apply {
-            val literalIndex = indexOfFirstLiteralInstructionOrThrow(CAIRO_CONFIG_LITERAL_VALUE)
-            val resultIndex = indexOfFirstInstructionOrThrow(literalIndex, Opcode.MOVE_RESULT)
-            val register = getInstruction(resultIndex).registerA
-
-            addInstruction(
-                resultIndex + 1,
-                "const/16 v$register, 0x0",
-            )
-        }
-    }
-}
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/cairo/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/cairo/Fingerprints.kt
deleted file mode 100644
index 5272adc07..000000000
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/fix/cairo/Fingerprints.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package app.revanced.patches.youtube.misc.fix.cairo
-
-import app.revanced.patcher.fingerprint
-import app.revanced.util.literal
-import com.android.tools.smali.dexlib2.AccessFlags
-
-/**
- * Added in YouTube v19.04.38.
- *
- * When this value is true, Cairo Fragment is used.
- * In this case, some of the patches may be broken, so set this value to FALSE.
- */
-internal const val CAIRO_CONFIG_LITERAL_VALUE = 45532100L
-
-internal val cairoFragmentConfigFingerprint = fingerprint {
-    accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
-    returns("Z")
-    literal { CAIRO_CONFIG_LITERAL_VALUE }
-}
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/Fingerprints.kt
index 42298d82f..c86620384 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/Fingerprints.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/Fingerprints.kt
@@ -21,3 +21,14 @@ internal val setThemeFingerprint = fingerprint {
     opcodes(Opcode.RETURN_OBJECT)
     literal { appearanceStringId }
 }
+
+/**
+ * Added in YouTube v19.04.38.
+ */
+internal const val CAIRO_CONFIG_LITERAL_VALUE = 45532100L
+
+internal val cairoFragmentConfigFingerprint = fingerprint {
+    accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
+    returns("Z")
+    literal { CAIRO_CONFIG_LITERAL_VALUE }
+}
diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/SettingsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/SettingsPatch.kt
index 68c058b7b..8bf7cfc06 100644
--- a/patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/SettingsPatch.kt
+++ b/patches/src/main/kotlin/app/revanced/patches/youtube/misc/settings/SettingsPatch.kt
@@ -18,8 +18,10 @@ import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPref
 import app.revanced.patches.shared.misc.settings.settingsPatch
 import app.revanced.patches.youtube.misc.check.checkEnvironmentPatch
 import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
-import app.revanced.patches.youtube.misc.fix.cairo.disableCairoSettingsPatch
 import app.revanced.patches.youtube.misc.fix.playbackspeed.fixPlaybackSpeedWhilePlayingPatch
+import app.revanced.patches.youtube.misc.playservice.is_19_04_or_greater
+import app.revanced.patches.youtube.misc.playservice.is_19_34_or_greater
+import app.revanced.patches.youtube.misc.playservice.versionCheckPatch
 import app.revanced.util.*
 import com.android.tools.smali.dexlib2.AccessFlags
 import com.android.tools.smali.dexlib2.Opcode
@@ -43,13 +45,28 @@ private val settingsResourcePatch = resourcePatch {
     dependsOn(
         resourceMappingPatch,
         settingsPatch(
-            rootPreference = IntentPreference(
-                titleKey = "revanced_settings_title",
-                summaryKey = null,
-                intent = newIntent("revanced_settings_intent"),
-            ) to "settings_fragment",
-            preferences,
-        ),
+            listOf(
+                IntentPreference(
+                    titleKey = "revanced_settings_title",
+                    summaryKey = null,
+                    intent = newIntent("revanced_settings_intent"),
+                ) to "settings_fragment",
+                PreferenceCategory(
+                    titleKey = "revanced_settings_title",
+                    layout = "@layout/preference_group_title",
+                    preferences = setOf(
+                        IntentPreference(
+                            titleKey = "revanced_settings_submenu_title",
+                            summaryKey = null,
+                            icon = "@drawable/revanced_settings_icon",
+                            layout = "@layout/preference_with_icon",
+                            intent = newIntent("revanced_settings_intent"),
+                        )
+                    )
+                ) to "settings_fragment_cairo",
+            ),
+            preferences
+        )
     )
 
     execute {
@@ -57,6 +74,7 @@ private val settingsResourcePatch = resourcePatch {
         appearanceStringId = resourceMappings["string", "app_theme_appearance_dark"]
 
         arrayOf(
+            ResourceGroup("drawable", "revanced_settings_icon.xml"),
             ResourceGroup("layout", "revanced_settings_with_toolbar.xml"),
         ).forEach { resourceGroup ->
             copyResources("settings", resourceGroup)
@@ -79,7 +97,6 @@ private val settingsResourcePatch = resourcePatch {
         // Remove horizontal divider from the settings Preferences
         // To better match the appearance of the stock YouTube settings.
         document("res/values/styles.xml").use { document ->
-
             arrayOf(
                 "Theme.YouTube.Settings",
                 "Theme.YouTube.Settings.Dark",
@@ -99,7 +116,6 @@ private val settingsResourcePatch = resourcePatch {
         // Some devices freak out if undeclared data is passed to an intent,
         // and this change appears to fix the issue.
         document("AndroidManifest.xml").use { document ->
-
             val licenseElement = document.childNodes.findElementByAttributeValueOrThrow(
                 "android:name",
                 "com.google.android.libraries.social.licenses.LicenseActivity",
@@ -123,7 +139,7 @@ val settingsPatch = bytecodePatch(
         sharedExtensionPatch,
         settingsResourcePatch,
         addResourcesPatch,
-        disableCairoSettingsPatch,
+        versionCheckPatch,
         fixPlaybackSpeedWhilePlayingPatch,
         // Currently there is no easy way to make a mandatory patch,
         // so for now this is a dependent of this patch.
@@ -147,6 +163,12 @@ val settingsPatch = bytecodePatch(
             selectable = true,
         )
 
+        if (is_19_34_or_greater) {
+            PreferenceScreen.GENERAL_LAYOUT.addPreferences(
+                SwitchPreference("revanced_restore_old_settings_menus")
+            )
+        }
+
         PreferenceScreen.MISC.addPreferences(
             TextPreference(
                 key = null,
@@ -224,6 +246,14 @@ val settingsPatch = bytecodePatch(
             methods.add(attachBaseContext)
         }
 
+        // Add setting to force cairo settings fragment on/off.
+        if (is_19_04_or_greater) {
+            cairoFragmentConfigFingerprint.method.insertFeatureFlagBooleanOverride(
+                CAIRO_CONFIG_LITERAL_VALUE,
+                "$activityHookClassDescriptor->useCairoSettingsFragment(Z)Z"
+            )
+        }
+
     }
 
     finalize {
@@ -259,17 +289,15 @@ object PreferenceScreen : BasePreferenceScreen() {
         key = "revanced_settings_screen_03_feed",
         summaryKey = null,
     )
-    val PLAYER = Screen(
-        key = "revanced_settings_screen_04_player",
+    val GENERAL_LAYOUT = Screen(
+        key = "revanced_settings_screen_04_general",
         summaryKey = null,
     )
-    val GENERAL_LAYOUT = Screen(
-        key = "revanced_settings_screen_05_general",
+    val PLAYER = Screen(
+        key = "revanced_settings_screen_05_player",
         summaryKey = null,
     )
 
-    // Don't sort, as related preferences are scattered apart.
-    // Can use title sorting after PreferenceCategory support is added.
     val SHORTS = Screen(
         key = "revanced_settings_screen_06_shorts",
         summaryKey = null,
diff --git a/patches/src/main/resources/addresources/values/strings.xml b/patches/src/main/resources/addresources/values/strings.xml
index 9f9b28790..ffceca0ad 100644
--- a/patches/src/main/resources/addresources/values/strings.xml
+++ b/patches/src/main/resources/addresources/values/strings.xml
@@ -32,6 +32,7 @@ Second \"item\" text"
             APK build date is corrupted
         
         
+            Settings
             ReVanced
             Do you wish to proceed?
             Reset
@@ -132,13 +133,16 @@ Tap the continue button and allow optimization changes."
             Ads
             Alternative thumbnails
             Feed
-            Player
-            General layout
+            General
+            Player
             Shorts
             Seekbar
             Swipe controls
-            Misc
+            Miscellaneous
             Video
+            Restore old settings menus
+            Old settings menus are shown
+            Old settings menus are not shown
         
         
             Disable Shorts background play
diff --git a/patches/src/main/resources/settings/drawable/revanced_settings_icon.xml b/patches/src/main/resources/settings/drawable/revanced_settings_icon.xml
new file mode 100644
index 000000000..a7d7185fa
--- /dev/null
+++ b/patches/src/main/resources/settings/drawable/revanced_settings_icon.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+    
+    
+
\ No newline at end of file
diff --git a/patches/src/main/resources/settings/layout/revanced_settings_with_toolbar.xml b/patches/src/main/resources/settings/layout/revanced_settings_with_toolbar.xml
index 1c91a9bc6..238dc5f79 100644
--- a/patches/src/main/resources/settings/layout/revanced_settings_with_toolbar.xml
+++ b/patches/src/main/resources/settings/layout/revanced_settings_with_toolbar.xml
@@ -10,6 +10,7 @@
         android:transitionGroup="true">
 
         
  
 
\ No newline at end of file