From ab808aeb773592cb26c848d8456478a346ec3bad Mon Sep 17 00:00:00 2001 From: MarcaD <152095496+MarcaDian@users.noreply.github.com> Date: Sun, 9 Nov 2025 09:39:51 +0200 Subject: [PATCH] feat(YouTube Music): Add `Change miniplayer color` patch (#6259) Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> --- .../patches/ChangeMiniplayerColorPatch.java | 14 +++ .../extension/music/settings/Settings.java | 1 + patches/api/patches.api | 4 + .../miniplayercolor/ChangeMiniplayerColor.kt | 110 ++++++++++++++++++ .../layout/miniplayercolor/Fingerprints.kt | 27 +++++ .../resources/addresources/values/strings.xml | 5 + 6 files changed, 161 insertions(+) create mode 100644 extensions/music/src/main/java/app/revanced/extension/music/patches/ChangeMiniplayerColorPatch.java create mode 100644 patches/src/main/kotlin/app/revanced/patches/music/layout/miniplayercolor/ChangeMiniplayerColor.kt create mode 100644 patches/src/main/kotlin/app/revanced/patches/music/layout/miniplayercolor/Fingerprints.kt diff --git a/extensions/music/src/main/java/app/revanced/extension/music/patches/ChangeMiniplayerColorPatch.java b/extensions/music/src/main/java/app/revanced/extension/music/patches/ChangeMiniplayerColorPatch.java new file mode 100644 index 000000000..ec941f7f3 --- /dev/null +++ b/extensions/music/src/main/java/app/revanced/extension/music/patches/ChangeMiniplayerColorPatch.java @@ -0,0 +1,14 @@ +package app.revanced.extension.music.patches; + +import app.revanced.extension.music.settings.Settings; + +@SuppressWarnings("unused") +public class ChangeMiniplayerColorPatch { + + /** + * Injection point + */ + public static boolean changeMiniplayerColor() { + return Settings.CHANGE_MINIPLAYER_COLOR.get(); + } +} diff --git a/extensions/music/src/main/java/app/revanced/extension/music/settings/Settings.java b/extensions/music/src/main/java/app/revanced/extension/music/settings/Settings.java index bebb3f86c..bd0c80402 100644 --- a/extensions/music/src/main/java/app/revanced/extension/music/settings/Settings.java +++ b/extensions/music/src/main/java/app/revanced/extension/music/settings/Settings.java @@ -30,6 +30,7 @@ public class Settings extends BaseSettings { public static final BooleanSetting HIDE_NAVIGATION_BAR_LABEL = new BooleanSetting("revanced_music_hide_navigation_bar_labels", FALSE, true); // Player + public static final BooleanSetting CHANGE_MINIPLAYER_COLOR = new BooleanSetting("revanced_music_change_miniplayer_color", FALSE, true); public static final BooleanSetting PERMANENT_REPEAT = new BooleanSetting("revanced_music_play_permanent_repeat", FALSE, true); // Miscellaneous diff --git a/patches/api/patches.api b/patches/api/patches.api index 92a94463e..7615d8ae6 100644 --- a/patches/api/patches.api +++ b/patches/api/patches.api @@ -416,6 +416,10 @@ public final class app/revanced/patches/music/layout/compactheader/HideCategoryB public static final fun getHideCategoryBar ()Lapp/revanced/patcher/patch/BytecodePatch; } +public final class app/revanced/patches/music/layout/miniplayercolor/ChangeMiniplayerColorKt { + public static final fun getChangeMiniplayerColor ()Lapp/revanced/patcher/patch/BytecodePatch; +} + public final class app/revanced/patches/music/layout/navigationbar/NavigationBarPatchKt { public static final fun getNavigationBarPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/miniplayercolor/ChangeMiniplayerColor.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/miniplayercolor/ChangeMiniplayerColor.kt new file mode 100644 index 000000000..ef4465718 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/miniplayercolor/ChangeMiniplayerColor.kt @@ -0,0 +1,110 @@ +@file:Suppress("SpellCheckingInspection") + +package app.revanced.patches.music.layout.miniplayercolor + +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.music.misc.extension.sharedExtensionPatch +import app.revanced.patches.music.misc.settings.PreferenceScreen +import app.revanced.patches.music.misc.settings.settingsPatch +import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.resourceMappingPatch +import app.revanced.patches.shared.misc.mapping.resourceMappings +import app.revanced.patches.shared.misc.settings.preference.SwitchPreference +import app.revanced.util.addInstructionsAtControlFlowLabel +import app.revanced.util.findFreeRegister +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstructionOrThrow +import app.revanced.util.indexOfFirstInstructionReversedOrThrow +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction +import com.android.tools.smali.dexlib2.iface.reference.FieldReference +import com.android.tools.smali.dexlib2.iface.reference.MethodReference + +internal var mpp_player_bottom_sheet = -1L + private set + +private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/music/patches/ChangeMiniplayerColorPatch;" + +@Suppress("unused") +val changeMiniplayerColor = bytecodePatch( + name = "Change miniplayer color", + description = "Adds an option to change the miniplayer background color to match the fullscreen player." +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + addResourcesPatch, + resourceMappingPatch + ) + + compatibleWith( + "com.google.android.apps.youtube.music"( + "7.29.52", + "8.10.52" + ) + ) + + execute { + mpp_player_bottom_sheet = resourceMappings["id", "mpp_player_bottom_sheet"] + + addResources("music", "layout.miniplayercolor.changeMiniplayerColor") + + PreferenceScreen.PLAYER.addPreferences( + SwitchPreference("revanced_music_change_miniplayer_color"), + ) + + switchToggleColorFingerprint.match(miniPlayerConstructorFingerprint.classDef).let { + val relativeIndex = it.patternMatch!!.endIndex + 1 + + val invokeVirtualIndex = it.method.indexOfFirstInstructionOrThrow( + relativeIndex, Opcode.INVOKE_VIRTUAL + ) + val colorMathPlayerInvokeVirtualReference = it.method + .getInstruction(invokeVirtualIndex).reference + + val iGetIndex = it.method.indexOfFirstInstructionOrThrow( + relativeIndex, Opcode.IGET + ) + val colorMathPlayerIGetReference = it.method + .getInstruction(iGetIndex).reference as FieldReference + + val colorGreyIndex = miniPlayerConstructorFingerprint.method.indexOfFirstInstructionReversedOrThrow { + getReference()?.name == "getColor" + } + val iPutIndex = miniPlayerConstructorFingerprint.method.indexOfFirstInstructionOrThrow( + colorGreyIndex, Opcode.IPUT + ) + val colorMathPlayerIPutReference = miniPlayerConstructorFingerprint.method + .getInstruction(iPutIndex).reference + + miniPlayerConstructorFingerprint.classDef.methods.single { method -> + method.accessFlags == AccessFlags.PUBLIC.value or AccessFlags.FINAL.value && + method.returnType == "V" && + method.parameters == it.originalMethod.parameters + }.apply { + val insertIndex = indexOfFirstInstructionReversedOrThrow(Opcode.INVOKE_DIRECT) + val freeRegister = findFreeRegister(insertIndex) + + addInstructionsAtControlFlowLabel( + insertIndex, + """ + invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->changeMiniplayerColor()Z + move-result v$freeRegister + if-eqz v$freeRegister, :off + invoke-virtual { p1 }, $colorMathPlayerInvokeVirtualReference + move-result-object v$freeRegister + check-cast v$freeRegister, ${colorMathPlayerIGetReference.definingClass} + iget v$freeRegister, v$freeRegister, $colorMathPlayerIGetReference + iput v$freeRegister, p0, $colorMathPlayerIPutReference + :off + nop + """ + ) + } + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/miniplayercolor/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/miniplayercolor/Fingerprints.kt new file mode 100644 index 000000000..8cc939f0f --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/miniplayercolor/Fingerprints.kt @@ -0,0 +1,27 @@ +package app.revanced.patches.music.layout.miniplayercolor + +import app.revanced.patcher.fingerprint +import app.revanced.util.literal +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val miniPlayerConstructorFingerprint = fingerprint { + returns("V") + strings("sharedToggleMenuItemMutations") + literal { mpp_player_bottom_sheet } +} + +/** + * Matches to the class found in [miniPlayerConstructorFingerprint]. + */ +internal val switchToggleColorFingerprint = fingerprint { + accessFlags(AccessFlags.PRIVATE, AccessFlags.FINAL) + returns("V") + parameters("L", "J") + opcodes( + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CHECK_CAST, + Opcode.IGET + ) +} diff --git a/patches/src/main/resources/addresources/values/strings.xml b/patches/src/main/resources/addresources/values/strings.xml index eb97fb7d4..b231f87b3 100644 --- a/patches/src/main/resources/addresources/values/strings.xml +++ b/patches/src/main/resources/addresources/values/strings.xml @@ -1718,6 +1718,11 @@ Video playback with AV1 may stutter or drop frames." Category bar is hidden Category bar is shown + + Change miniplayer color + Miniplayer color matches fullscreen player + Miniplayer uses default color + Navigation bar Hide or change navigation bar buttons