mirror of
https://github.com/inotia00/revanced-patches.git
synced 2025-05-29 21:30:19 +02:00
add enable-force-shuffle
patch
This commit is contained in:
parent
f31203bfba
commit
b1e80566b8
@ -0,0 +1,20 @@
|
|||||||
|
package app.revanced.patches.music.layout.shuffle.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
object MusicPlaybackControlsFingerprint : MethodFingerprint(
|
||||||
|
returnType = "V",
|
||||||
|
access = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
parameters = listOf("Z"),
|
||||||
|
opcodes = listOf(
|
||||||
|
Opcode.IPUT_BOOLEAN,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.RETURN_VOID
|
||||||
|
),
|
||||||
|
customFingerprint = { methodDef ->
|
||||||
|
methodDef.definingClass.endsWith("MusicPlaybackControls;")
|
||||||
|
}
|
||||||
|
)
|
@ -0,0 +1,29 @@
|
|||||||
|
package app.revanced.patches.music.layout.shuffle.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import app.revanced.patches.music.misc.resourceid.patch.SharedResourcdIdPatch
|
||||||
|
import org.jf.dexlib2.iface.instruction.WideLiteralInstruction
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
object ShuffleClassFingerprint : MethodFingerprint(
|
||||||
|
returnType = "V",
|
||||||
|
access = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||||
|
opcodes = listOf(
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.MOVE_RESULT,
|
||||||
|
Opcode.IPUT,
|
||||||
|
Opcode.INVOKE_VIRTUAL,
|
||||||
|
Opcode.RETURN_VOID
|
||||||
|
),
|
||||||
|
customFingerprint = { methodDef ->
|
||||||
|
methodDef.name == "<init>" &&
|
||||||
|
methodDef.implementation?.instructions?.any { instruction ->
|
||||||
|
instruction.opcode.ordinal == Opcode.CONST.ordinal &&
|
||||||
|
(instruction as? WideLiteralInstruction)?.wideLiteral == SharedResourcdIdPatch.disabledIconLabelId
|
||||||
|
} == true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,21 @@
|
|||||||
|
package app.revanced.patches.music.layout.shuffle.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
|
||||||
|
object ShuffleClassReferenceFingerprint : MethodFingerprint(
|
||||||
|
returnType = "V",
|
||||||
|
access = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
parameters = listOf(),
|
||||||
|
opcodes = listOf(
|
||||||
|
Opcode.IGET_OBJECT,
|
||||||
|
Opcode.INVOKE_INTERFACE,
|
||||||
|
Opcode.MOVE_RESULT_OBJECT,
|
||||||
|
Opcode.CHECK_CAST,
|
||||||
|
Opcode.IGET_OBJECT
|
||||||
|
),
|
||||||
|
strings = listOf("Unknown shuffle mode")
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,174 @@
|
|||||||
|
package app.revanced.patches.music.layout.shuffle.patch
|
||||||
|
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Version
|
||||||
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
|
import app.revanced.patcher.extensions.*
|
||||||
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.PatchResult
|
||||||
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
|
import app.revanced.patcher.patch.annotations.DependsOn
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.util.TypeUtil.traverseClassHierarchy
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableField.Companion.toMutable
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod.Companion.toMutable
|
||||||
|
import app.revanced.patcher.util.smali.toInstructions
|
||||||
|
import app.revanced.patches.music.layout.shuffle.fingerprints.MusicPlaybackControlsFingerprint
|
||||||
|
import app.revanced.patches.music.layout.shuffle.fingerprints.ShuffleClassFingerprint
|
||||||
|
import app.revanced.patches.music.layout.shuffle.fingerprints.ShuffleClassReferenceFingerprint
|
||||||
|
import app.revanced.patches.music.misc.integrations.patch.MusicIntegrationsPatch
|
||||||
|
import app.revanced.patches.music.misc.resourceid.patch.SharedResourcdIdPatch
|
||||||
|
import app.revanced.patches.music.misc.settings.patch.MusicSettingsPatch
|
||||||
|
import app.revanced.shared.annotation.YouTubeMusicCompatibility
|
||||||
|
import app.revanced.shared.extensions.transformFields
|
||||||
|
import app.revanced.shared.util.integrations.Constants.MUSIC_SETTINGS_PATH
|
||||||
|
import org.jf.dexlib2.AccessFlags
|
||||||
|
import org.jf.dexlib2.dexbacked.reference.DexBackedMethodReference
|
||||||
|
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
|
import org.jf.dexlib2.iface.reference.FieldReference
|
||||||
|
import org.jf.dexlib2.iface.reference.MethodReference
|
||||||
|
import org.jf.dexlib2.immutable.ImmutableField
|
||||||
|
import org.jf.dexlib2.immutable.ImmutableMethod
|
||||||
|
import org.jf.dexlib2.immutable.ImmutableMethodImplementation
|
||||||
|
import org.jf.dexlib2.immutable.ImmutableMethodParameter
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@Name("enable-force-shuffle")
|
||||||
|
@Description("Enable force shuffle even if another track is played.")
|
||||||
|
@DependsOn([MusicIntegrationsPatch::class, MusicSettingsPatch::class, SharedResourcdIdPatch::class])
|
||||||
|
@YouTubeMusicCompatibility
|
||||||
|
@Version("0.0.1")
|
||||||
|
class EnforceShufflePatch : BytecodePatch(
|
||||||
|
listOf(
|
||||||
|
MusicPlaybackControlsFingerprint,
|
||||||
|
ShuffleClassFingerprint,
|
||||||
|
ShuffleClassReferenceFingerprint
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
override fun execute(context: BytecodeContext): PatchResult {
|
||||||
|
|
||||||
|
with(ShuffleClassReferenceFingerprint.result!!) {
|
||||||
|
val startIndex = scanResult.patternScanResult!!.startIndex
|
||||||
|
val endIndex = scanResult.patternScanResult!!.endIndex
|
||||||
|
val referenceInstructions = mutableMethod.implementation!!.instructions
|
||||||
|
|
||||||
|
SHUFFLE_CLASS = classDef.type
|
||||||
|
firstRef = (referenceInstructions.elementAt(startIndex) as ReferenceInstruction).reference as FieldReference
|
||||||
|
secondRef = (referenceInstructions.elementAt(startIndex + 1) as ReferenceInstruction).reference as DexBackedMethodReference
|
||||||
|
thirdRef = (referenceInstructions.elementAt(endIndex) as ReferenceInstruction).reference as FieldReference
|
||||||
|
|
||||||
|
referenceInstructions.filter { instruction ->
|
||||||
|
val fieldReference = (instruction as? ReferenceInstruction)?.reference as? FieldReference
|
||||||
|
fieldReference?.let { it.type == "Landroid/widget/ImageView;" } == true
|
||||||
|
}.forEach { instruction ->
|
||||||
|
fourthRef = (instruction as ReferenceInstruction).reference as FieldReference
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
with(ShuffleClassFingerprint.result!!) {
|
||||||
|
|
||||||
|
val insertIndex = scanResult.patternScanResult!!.endIndex
|
||||||
|
mutableMethod.addInstruction(
|
||||||
|
insertIndex,
|
||||||
|
"sput-object p0, $MUSIC_PLAYBACK_CONTROLS_CLASS_DESCRIPTOR->shuffleclass:$SHUFFLE_CLASS"
|
||||||
|
)
|
||||||
|
|
||||||
|
context.traverseClassHierarchy(mutableClass) {
|
||||||
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL
|
||||||
|
transformFields {
|
||||||
|
ImmutableField(
|
||||||
|
definingClass,
|
||||||
|
name,
|
||||||
|
type,
|
||||||
|
AccessFlags.PUBLIC or AccessFlags.PUBLIC,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
).toMutable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
with(MusicPlaybackControlsFingerprint.result!!) {
|
||||||
|
|
||||||
|
val referenceInstructions = mutableMethod.implementation!!.instructions
|
||||||
|
|
||||||
|
fifthRef = (referenceInstructions.elementAt(0) as ReferenceInstruction).reference as FieldReference
|
||||||
|
sixthRef = (referenceInstructions.elementAt(1) as ReferenceInstruction).reference as DexBackedMethodReference
|
||||||
|
|
||||||
|
mutableClass.staticFields.add(
|
||||||
|
ImmutableField(
|
||||||
|
mutableMethod.definingClass,
|
||||||
|
"shuffleclass",
|
||||||
|
"$SHUFFLE_CLASS",
|
||||||
|
AccessFlags.PUBLIC or AccessFlags.STATIC,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
).toMutable()
|
||||||
|
)
|
||||||
|
|
||||||
|
mutableMethod.addInstructions(
|
||||||
|
0,
|
||||||
|
"""
|
||||||
|
invoke-virtual {v0, v1}, $MUSIC_PLAYBACK_CONTROLS_CLASS_DESCRIPTOR->buttonHook(Z)V
|
||||||
|
return-void
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
mutableClass.methods.add(
|
||||||
|
ImmutableMethod(
|
||||||
|
classDef.type,
|
||||||
|
"buttonHook",
|
||||||
|
listOf(ImmutableMethodParameter("Z", null, null)),
|
||||||
|
"V",
|
||||||
|
AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
ImmutableMethodImplementation(
|
||||||
|
5, """
|
||||||
|
invoke-static {}, $MUSIC_SETTINGS_PATH->enableForceShuffle()Z
|
||||||
|
move-result v0
|
||||||
|
if-eqz v0, :cond_0
|
||||||
|
new-instance v0, $SHUFFLE_CLASS
|
||||||
|
sget-object v0, $MUSIC_PLAYBACK_CONTROLS_CLASS_DESCRIPTOR->shuffleclass:$SHUFFLE_CLASS
|
||||||
|
iget-object v1, v0, $SHUFFLE_CLASS->${firstRef.name}:${firstRef.type}
|
||||||
|
invoke-interface {v1}, $secondRef
|
||||||
|
move-result-object v1
|
||||||
|
check-cast v1, ${thirdRef.definingClass}
|
||||||
|
iget-object v1, v1, ${thirdRef.definingClass}->${thirdRef.name}:${thirdRef.type}
|
||||||
|
invoke-virtual {v1}, ${thirdRef.type}->ordinal()I
|
||||||
|
move-result v1
|
||||||
|
iget-object v2, v0, $SHUFFLE_CLASS->${fourthRef.name}:Landroid/widget/ImageView;
|
||||||
|
invoke-virtual {v2}, Landroid/widget/ImageView;->performClick()Z
|
||||||
|
if-eqz v1, :cond_0
|
||||||
|
invoke-virtual {v2}, Landroid/widget/ImageView;->performClick()Z
|
||||||
|
:cond_0
|
||||||
|
iput-boolean v4, v3, $MUSIC_PLAYBACK_CONTROLS_CLASS_DESCRIPTOR->${fifthRef.name}:${fifthRef.type}
|
||||||
|
invoke-virtual {v3}, $sixthRef
|
||||||
|
return-void
|
||||||
|
""".toInstructions(), null, null
|
||||||
|
)
|
||||||
|
).toMutable()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return PatchResultSuccess()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val MUSIC_PLAYBACK_CONTROLS_CLASS_DESCRIPTOR =
|
||||||
|
"Lcom/google/android/apps/youtube/music/watchpage/MusicPlaybackControls;"
|
||||||
|
|
||||||
|
private lateinit var SHUFFLE_CLASS: String
|
||||||
|
|
||||||
|
private lateinit var firstRef: FieldReference
|
||||||
|
private lateinit var secondRef: DexBackedMethodReference
|
||||||
|
private lateinit var thirdRef: FieldReference
|
||||||
|
private lateinit var fourthRef: FieldReference
|
||||||
|
|
||||||
|
private lateinit var fifthRef: FieldReference
|
||||||
|
private lateinit var sixthRef: DexBackedMethodReference
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@ import app.revanced.patcher.extensions.MethodFingerprintExtensions.name
|
|||||||
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
import app.revanced.patcher.patch.PatchResultError
|
import app.revanced.patcher.patch.PatchResultError
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableField
|
||||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
import app.revanced.patcher.util.smali.toInstruction
|
import app.revanced.patcher.util.smali.toInstruction
|
||||||
import app.revanced.shared.util.integrations.Constants.INTEGRATIONS_PATH
|
import app.revanced.shared.util.integrations.Constants.INTEGRATIONS_PATH
|
||||||
@ -65,6 +66,17 @@ fun MutableClass.transformMethods(transform: MutableMethod.() -> MutableMethod)
|
|||||||
methods.addAll(transformedMethods)
|
methods.addAll(transformedMethods)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* apply a transform to all fields of the class
|
||||||
|
*
|
||||||
|
* @param transform the transformation function. original field goes in, transformed field goes out
|
||||||
|
*/
|
||||||
|
fun MutableClass.transformFields(transform: MutableField.() -> MutableField) {
|
||||||
|
val transformedFields = fields.map { it.transform() }
|
||||||
|
fields.clear()
|
||||||
|
fields.addAll(transformedFields)
|
||||||
|
}
|
||||||
|
|
||||||
internal fun Node.doRecursively(action: (Node) -> Unit) {
|
internal fun Node.doRecursively(action: (Node) -> Unit) {
|
||||||
action(this)
|
action(this)
|
||||||
for (i in 0 until this.childNodes.length) this.childNodes.item(i).doRecursively(action)
|
for (i in 0 until this.childNodes.length) this.childNodes.item(i).doRecursively(action)
|
||||||
|
@ -10,6 +10,9 @@
|
|||||||
<string name="revanced_enable_color_match_player_title">Enable color match Players</string>
|
<string name="revanced_enable_color_match_player_title">Enable color match Players</string>
|
||||||
<string name="revanced_enable_force_minimized_player_summary">Keep player permanently minimized even if another track is played.</string>
|
<string name="revanced_enable_force_minimized_player_summary">Keep player permanently minimized even if another track is played.</string>
|
||||||
<string name="revanced_enable_force_minimized_player_title">Enable force minimized player</string>
|
<string name="revanced_enable_force_minimized_player_title">Enable force minimized player</string>
|
||||||
|
<string name="revanced_enable_force_shuffle_summary">"Enable force shuffle even if another track is played.
|
||||||
|
(Not available in Canada)"</string>
|
||||||
|
<string name="revanced_enable_force_shuffle_title">Enable force shuffle</string>
|
||||||
<string name="revanced_enable_opus_codec_summary">"Enable 250/251 opus codec when playing audio.
|
<string name="revanced_enable_opus_codec_summary">"Enable 250/251 opus codec when playing audio.
|
||||||
(requires an app restart)"</string>
|
(requires an app restart)"</string>
|
||||||
<string name="revanced_enable_opus_codec_title">Enable opus codec</string>
|
<string name="revanced_enable_opus_codec_title">Enable opus codec</string>
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
<com.google.android.apps.youtube.music.ui.preference.PreferenceCategoryCompat android:title="@string/revanced_category_listening" android:key="revanced_settings_listening">
|
<com.google.android.apps.youtube.music.ui.preference.PreferenceCategoryCompat android:title="@string/revanced_category_listening" android:key="revanced_settings_listening">
|
||||||
<com.google.android.apps.youtube.music.ui.preference.SwitchCompatPreference android:title="@string/revanced_enable_opus_codec_title" android:key="revanced_enable_opus_codec" android:summary="@string/revanced_enable_opus_codec_summary" android:defaultValue="true" />
|
<com.google.android.apps.youtube.music.ui.preference.SwitchCompatPreference android:title="@string/revanced_enable_opus_codec_title" android:key="revanced_enable_opus_codec" android:summary="@string/revanced_enable_opus_codec_summary" android:defaultValue="true" />
|
||||||
<com.google.android.apps.youtube.music.ui.preference.SwitchCompatPreference android:title="@string/revanced_enable_force_minimized_player_title" android:key="revanced_enable_force_minimized_player" android:summary="@string/revanced_enable_force_minimized_player_summary" android:defaultValue="true" />
|
<com.google.android.apps.youtube.music.ui.preference.SwitchCompatPreference android:title="@string/revanced_enable_force_minimized_player_title" android:key="revanced_enable_force_minimized_player" android:summary="@string/revanced_enable_force_minimized_player_summary" android:defaultValue="true" />
|
||||||
|
<com.google.android.apps.youtube.music.ui.preference.SwitchCompatPreference android:title="@string/revanced_enable_force_shuffle_title" android:key="revanced_enable_force_shuffle" android:summary="@string/revanced_enable_force_shuffle_summary" android:defaultValue="true" />
|
||||||
</com.google.android.apps.youtube.music.ui.preference.PreferenceCategoryCompat>
|
</com.google.android.apps.youtube.music.ui.preference.PreferenceCategoryCompat>
|
||||||
<com.google.android.apps.youtube.music.ui.preference.PreferenceCategoryCompat android:title="@string/revanced_category_navigation" android:key="revanced_settings_navigation">
|
<com.google.android.apps.youtube.music.ui.preference.PreferenceCategoryCompat android:title="@string/revanced_category_navigation" android:key="revanced_settings_navigation">
|
||||||
<com.google.android.apps.youtube.music.ui.preference.SwitchCompatPreference android:title="@string/revanced_hide_cast_button_title" android:key="revanced_hide_cast_button" android:summary="@string/revanced_hide_cast_button_summary" android:defaultValue="true" />
|
<com.google.android.apps.youtube.music.ui.preference.SwitchCompatPreference android:title="@string/revanced_hide_cast_button_title" android:key="revanced_hide_cast_button" android:summary="@string/revanced_hide_cast_button_summary" android:defaultValue="true" />
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
<com.google.android.apps.youtube.music.ui.preference.PreferenceCategoryCompat android:title="@string/revanced_category_listening" android:key="revanced_settings_listening">
|
<com.google.android.apps.youtube.music.ui.preference.PreferenceCategoryCompat android:title="@string/revanced_category_listening" android:key="revanced_settings_listening">
|
||||||
<com.google.android.apps.youtube.music.ui.preference.SwitchCompatPreference android:title="@string/revanced_enable_opus_codec_title" android:key="revanced_enable_opus_codec" android:summary="@string/revanced_enable_opus_codec_summary" android:defaultValue="true" />
|
<com.google.android.apps.youtube.music.ui.preference.SwitchCompatPreference android:title="@string/revanced_enable_opus_codec_title" android:key="revanced_enable_opus_codec" android:summary="@string/revanced_enable_opus_codec_summary" android:defaultValue="true" />
|
||||||
<com.google.android.apps.youtube.music.ui.preference.SwitchCompatPreference android:title="@string/revanced_enable_force_minimized_player_title" android:key="revanced_enable_force_minimized_player" android:summary="@string/revanced_enable_force_minimized_player_summary" android:defaultValue="true" />
|
<com.google.android.apps.youtube.music.ui.preference.SwitchCompatPreference android:title="@string/revanced_enable_force_minimized_player_title" android:key="revanced_enable_force_minimized_player" android:summary="@string/revanced_enable_force_minimized_player_summary" android:defaultValue="true" />
|
||||||
|
<com.google.android.apps.youtube.music.ui.preference.SwitchCompatPreference android:title="@string/revanced_enable_force_shuffle_title" android:key="revanced_enable_force_shuffle" android:summary="@string/revanced_enable_force_shuffle_summary" android:defaultValue="true" />
|
||||||
</com.google.android.apps.youtube.music.ui.preference.PreferenceCategoryCompat>
|
</com.google.android.apps.youtube.music.ui.preference.PreferenceCategoryCompat>
|
||||||
<com.google.android.apps.youtube.music.ui.preference.PreferenceCategoryCompat android:title="@string/revanced_category_navigation" android:key="revanced_settings_navigation">
|
<com.google.android.apps.youtube.music.ui.preference.PreferenceCategoryCompat android:title="@string/revanced_category_navigation" android:key="revanced_settings_navigation">
|
||||||
<com.google.android.apps.youtube.music.ui.preference.SwitchCompatPreference android:title="@string/revanced_hide_cast_button_title" android:key="revanced_hide_cast_button" android:summary="@string/revanced_hide_cast_button_summary" android:defaultValue="true" />
|
<com.google.android.apps.youtube.music.ui.preference.SwitchCompatPreference android:title="@string/revanced_hide_cast_button_title" android:key="revanced_hide_cast_button" android:summary="@string/revanced_hide_cast_button_summary" android:defaultValue="true" />
|
||||||
|
Loading…
x
Reference in New Issue
Block a user