mirror of
https://github.com/inotia00/revanced-patches.git
synced 2025-05-28 12:50: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.patch.PatchResultError
|
||||
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.smali.toInstruction
|
||||
import app.revanced.shared.util.integrations.Constants.INTEGRATIONS_PATH
|
||||
@ -65,6 +66,17 @@ fun MutableClass.transformMethods(transform: MutableMethod.() -> MutableMethod)
|
||||
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) {
|
||||
action(this)
|
||||
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_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_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.
|
||||
(requires an app restart)"</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.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_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 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" />
|
||||
|
@ -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.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_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 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" />
|
||||
|
Loading…
x
Reference in New Issue
Block a user