@@ -35,15 +35,14 @@ import app.revanced.patches.music.player.components.fingerprints.PlayerViewPager
35
35
import app.revanced.patches.music.player.components.fingerprints.QuickSeekOverlayFingerprint
36
36
import app.revanced.patches.music.player.components.fingerprints.RemixGenericButtonFingerprint
37
37
import app.revanced.patches.music.player.components.fingerprints.RepeatTrackFingerprint
38
- import app.revanced.patches.music.player.components.fingerprints.ShuffleClassReferenceFingerprint
39
- import app.revanced.patches.music.player.components.fingerprints.ShuffleClassReferenceFingerprint.indexOfImageViewInstruction
40
- import app.revanced.patches.music.player.components.fingerprints.ShuffleClassReferenceFingerprint.indexOfOrdinalInstruction
38
+ import app.revanced.patches.music.player.components.fingerprints.ShuffleOnClickFingerprint
41
39
import app.revanced.patches.music.player.components.fingerprints.SwipeToCloseFingerprint
42
40
import app.revanced.patches.music.player.components.fingerprints.SwitchToggleColorFingerprint
43
41
import app.revanced.patches.music.player.components.fingerprints.ZenModeFingerprint
44
42
import app.revanced.patches.music.utils.compatibility.Constants.COMPATIBLE_PACKAGE
45
43
import app.revanced.patches.music.utils.fingerprints.PendingIntentReceiverFingerprint
46
44
import app.revanced.patches.music.utils.integrations.Constants.COMPONENTS_PATH
45
+ import app.revanced.patches.music.utils.integrations.Constants.INTEGRATIONS_PATH
47
46
import app.revanced.patches.music.utils.integrations.Constants.PLAYER_CLASS_DESCRIPTOR
48
47
import app.revanced.patches.music.utils.mainactivity.MainActivityResolvePatch
49
48
import app.revanced.patches.music.utils.resourceid.SharedResourceIdPatch
@@ -61,6 +60,7 @@ import app.revanced.patches.music.utils.settings.SettingsPatch
61
60
import app.revanced.patches.music.utils.videotype.VideoTypeHookPatch
62
61
import app.revanced.patches.shared.litho.LithoFilterPatch
63
62
import app.revanced.util.REGISTER_TEMPLATE_REPLACEMENT
63
+ import app.revanced.util.addStaticFieldToIntegration
64
64
import app.revanced.util.alsoResolve
65
65
import app.revanced.util.findMethodOrThrow
66
66
import app.revanced.util.getReference
@@ -73,11 +73,10 @@ import app.revanced.util.injectLiteralInstructionBooleanCall
73
73
import app.revanced.util.injectLiteralInstructionViewCall
74
74
import app.revanced.util.patch.BaseBytecodePatch
75
75
import app.revanced.util.resultOrThrow
76
- import app.revanced.util.transformFields
76
+ import app.revanced.util.transformMethods
77
77
import app.revanced.util.traverseClassHierarchy
78
78
import com.android.tools.smali.dexlib2.AccessFlags
79
79
import com.android.tools.smali.dexlib2.Opcode
80
- import com.android.tools.smali.dexlib2.builder.MutableMethodImplementation
81
80
import com.android.tools.smali.dexlib2.iface.MethodParameter
82
81
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
83
82
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@@ -88,7 +87,6 @@ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
88
87
import com.android.tools.smali.dexlib2.iface.reference.Reference
89
88
import com.android.tools.smali.dexlib2.immutable.ImmutableField
90
89
import com.android.tools.smali.dexlib2.immutable.ImmutableMethod
91
- import com.android.tools.smali.dexlib2.util.MethodUtil
92
90
import kotlin.properties.Delegates
93
91
94
92
@Suppress(" unused" , " LocalVariableName" )
@@ -101,7 +99,7 @@ object PlayerComponentsPatch : BaseBytecodePatch(
101
99
PlayerComponentsResourcePatch ::class,
102
100
SettingsPatch ::class,
103
101
SharedResourceIdPatch ::class,
104
- VideoTypeHookPatch ::class
102
+ VideoTypeHookPatch ::class,
105
103
),
106
104
compatiblePackages = COMPATIBLE_PACKAGE ,
107
105
fingerprints = setOf(
@@ -126,13 +124,16 @@ object PlayerComponentsPatch : BaseBytecodePatch(
126
124
QuickSeekOverlayFingerprint ,
127
125
RemixGenericButtonFingerprint ,
128
126
RepeatTrackFingerprint ,
129
- ShuffleClassReferenceFingerprint ,
127
+ ShuffleOnClickFingerprint ,
130
128
SwipeToCloseFingerprint ,
131
129
)
132
130
) {
133
131
private const val FILTER_CLASS_DESCRIPTOR =
134
132
" $COMPONENTS_PATH /PlayerComponentsFilter;"
135
133
134
+ private const val INTEGRATIONS_VIDEO_UTILS_CLASS_DESCRIPTOR =
135
+ " $INTEGRATIONS_PATH /utils/VideoUtils;"
136
+
136
137
override fun execute (context : BytecodeContext ) {
137
138
138
139
// region patch for disable gesture in player
@@ -735,155 +736,85 @@ object PlayerComponentsPatch : BaseBytecodePatch(
735
736
736
737
// region patch for remember shuffle state
737
738
738
- val MUSIC_PLAYBACK_CONTROLS_CLASS_DESCRIPTOR =
739
- " Lcom/google/android/apps/youtube/music/watchpage/MusicPlaybackControls;"
739
+ ShuffleOnClickFingerprint .resultOrThrow().mutableMethod.apply {
740
+ val accessibilityIndex =
741
+ ShuffleOnClickFingerprint .indexOfAccessibilityInstruction(this )
740
742
741
- lateinit var rememberShuffleStateObjectClass: String
742
- lateinit var rememberShuffleStateImageViewReference: Reference
743
- lateinit var rememberShuffleStateShuffleStateLabel: String
743
+ // region set shuffle enum
744
744
745
- ShuffleClassReferenceFingerprint .resultOrThrow().let {
746
- it.mutableMethod.apply {
747
- rememberShuffleStateObjectClass = definingClass
748
-
749
- val imageViewIndex = indexOfImageViewInstruction(this )
750
- val ordinalIndex = indexOfOrdinalInstruction(this )
751
-
752
- val invokeInterfaceIndex =
753
- indexOfFirstInstructionReversedOrThrow(ordinalIndex, Opcode .INVOKE_INTERFACE )
754
- val iGetObjectIndex =
755
- indexOfFirstInstructionReversedOrThrow(invokeInterfaceIndex, Opcode .IGET_OBJECT )
756
- val checkCastIndex =
757
- indexOfFirstInstructionOrThrow(invokeInterfaceIndex, Opcode .CHECK_CAST )
758
-
759
- val iGetObjectReference =
760
- getInstruction<ReferenceInstruction >(iGetObjectIndex).reference
761
- val invokeInterfaceReference =
762
- getInstruction<ReferenceInstruction >(invokeInterfaceIndex).reference
763
- val checkCastReference =
764
- getInstruction<ReferenceInstruction >(checkCastIndex).reference
765
- val getOrdinalClassReference =
766
- getInstruction<ReferenceInstruction >(checkCastIndex + 1 ).reference
767
- val ordinalReference =
768
- getInstruction<ReferenceInstruction >(ordinalIndex).reference
769
-
770
- rememberShuffleStateImageViewReference =
771
- getInstruction<ReferenceInstruction >(imageViewIndex).reference
772
-
773
- rememberShuffleStateShuffleStateLabel = """
774
- iget-object v1, v0, $iGetObjectReference
775
- invoke-interface {v1}, $invokeInterfaceReference
776
- move-result-object v1
777
- check-cast v1, $checkCastReference
778
- """
779
-
780
- rememberShuffleStateShuffleStateLabel + = if (getInstruction(checkCastIndex + 1 ).opcode == Opcode .INVOKE_VIRTUAL ) {
781
- // YouTube Music 7.16.53+
782
- """
783
- invoke-virtual {v1}, $getOrdinalClassReference
784
- move-result-object v1
785
-
786
- """ .trimIndent()
787
- } else {
788
- """
789
- iget-object v1, v1, $getOrdinalClassReference
790
-
791
- """ .trimIndent()
792
- }
793
-
794
- rememberShuffleStateShuffleStateLabel + = """
795
- invoke-virtual {v1}, $ordinalReference
796
- move-result v1
797
-
798
- """ .trimIndent()
745
+ val enumIndex = indexOfFirstInstructionReversedOrThrow(accessibilityIndex) {
746
+ opcode == Opcode .INVOKE_DIRECT &&
747
+ getReference<MethodReference >()?.returnType == " Ljava/lang/String;"
799
748
}
749
+ val enumRegister = getInstruction<FiveRegisterInstruction >(enumIndex).registerD
750
+ val enumClass = (getInstruction<ReferenceInstruction >(enumIndex).reference as MethodReference ).parameterTypes.first()
800
751
801
- val constructorMethod =
802
- it.mutableClass.methods.first { method -> MethodUtil .isConstructor(method) }
803
- val onClickMethod = it.mutableClass.methods.first { method -> method.name == " onClick" }
752
+ addInstruction(
753
+ enumIndex,
754
+ " invoke-static {v$enumRegister }, $PLAYER_CLASS_DESCRIPTOR ->setShuffleState(Ljava/lang/Enum;)V"
755
+ )
804
756
805
- constructorMethod.apply {
806
- addInstruction(
807
- implementation!! .instructions.lastIndex,
808
- " sput-object p0, $MUSIC_PLAYBACK_CONTROLS_CLASS_DESCRIPTOR ->shuffleClass:$rememberShuffleStateObjectClass "
809
- )
810
- }
757
+ // endregion
811
758
812
- onClickMethod.apply {
813
- addInstructions(
814
- 0 , """
815
- move-object v0, p0
816
- """ + rememberShuffleStateShuffleStateLabel + """
817
- invoke-static {v1}, $PLAYER_CLASS_DESCRIPTOR ->setShuffleState(I)V
818
- """
819
- )
820
- }
759
+ // region set static field
821
760
822
- context.traverseClassHierarchy(it.mutableClass) {
823
- accessFlags = AccessFlags .PUBLIC or AccessFlags .FINAL
824
- transformFields {
825
- ImmutableField (
761
+ val shuffleClassIndex = indexOfFirstInstructionReversedOrThrow(accessibilityIndex, Opcode .CHECK_CAST )
762
+ val shuffleClass = getInstruction<ReferenceInstruction >(shuffleClassIndex).reference.toString()
763
+ val shuffleMutableClass = context.findClass { classDef ->
764
+ classDef.type == shuffleClass
765
+ }!! .mutableClass
766
+
767
+ val shuffleMethod = shuffleMutableClass.methods.find { method ->
768
+ method.parameterTypes.firstOrNull() == enumClass &&
769
+ method.parameterTypes.size == 1 &&
770
+ method.returnType == " V"
771
+ } ? : throw PatchException (" target not found" )
772
+
773
+ val smaliInstructions =
774
+ """
775
+ if-eqz v0, :ignore
776
+ sget-object v1, $enumClass ->b:$enumClass
777
+ invoke-virtual {v0, v1}, $shuffleClass ->${shuffleMethod.name} ($enumClass )V
778
+ :ignore
779
+ return-void
780
+ """
781
+
782
+ context.addStaticFieldToIntegration(
783
+ INTEGRATIONS_VIDEO_UTILS_CLASS_DESCRIPTOR ,
784
+ " shuffleTracks" ,
785
+ " shuffleClass" ,
786
+ shuffleClass,
787
+ smaliInstructions
788
+ )
789
+
790
+ // endregion
791
+
792
+ // region make all methods accessible
793
+
794
+ context.traverseClassHierarchy(shuffleMutableClass) {
795
+ transformMethods {
796
+ ImmutableMethod (
826
797
definingClass,
827
798
name,
828
- type ,
829
- AccessFlags . PUBLIC or AccessFlags . PUBLIC ,
830
- null ,
799
+ parameters ,
800
+ returnType ,
801
+ AccessFlags . PUBLIC or AccessFlags . FINAL ,
831
802
annotations,
832
- null
803
+ hiddenApiRestrictions,
804
+ implementation
833
805
).toMutable()
834
806
}
835
807
}
836
- }
837
-
838
- MusicPlaybackControlsFingerprint .resultOrThrow().let {
839
- it.mutableMethod.apply {
840
- addInstruction(
841
- 0 ,
842
- " invoke-virtual {v0}, $MUSIC_PLAYBACK_CONTROLS_CLASS_DESCRIPTOR ->rememberShuffleState()V"
843
- )
844
808
845
- val shuffleField = ImmutableField (
846
- definingClass,
847
- " shuffleClass" ,
848
- rememberShuffleStateObjectClass,
849
- AccessFlags .PUBLIC or AccessFlags .STATIC ,
850
- null ,
851
- annotations,
852
- null
853
- ).toMutable()
854
-
855
- val shuffleMethod = ImmutableMethod (
856
- definingClass,
857
- " rememberShuffleState" ,
858
- emptyList(),
859
- " V" ,
860
- AccessFlags .PUBLIC or AccessFlags .FINAL ,
861
- annotations, null ,
862
- MutableMethodImplementation (5 )
863
- ).toMutable()
864
-
865
- shuffleMethod.addInstructionsWithLabels(
866
- 0 , """
867
- invoke-static {}, $PLAYER_CLASS_DESCRIPTOR ->getShuffleState()I
868
- move-result v2
869
- if-nez v2, :dont_shuffle
870
- sget-object v0, $MUSIC_PLAYBACK_CONTROLS_CLASS_DESCRIPTOR ->shuffleClass:$rememberShuffleStateObjectClass
871
- """ + rememberShuffleStateShuffleStateLabel + """
872
- iget-object v3, v0, $rememberShuffleStateImageViewReference
873
- if-eqz v3, :dont_shuffle
874
- invoke-virtual {v3}, Landroid/view/View;->callOnClick()Z
875
- if-eqz v1, :dont_shuffle
876
- invoke-virtual {v3}, Landroid/view/View;->callOnClick()Z
877
- :dont_shuffle
878
- return-void
879
- """
880
- )
809
+ // endregion
881
810
882
- it.mutableClass.methods.add(shuffleMethod)
883
- it.mutableClass.staticFields.add(shuffleField)
884
- }
885
811
}
886
812
813
+ MusicPlaybackControlsFingerprint .resultOrThrow().mutableMethod.addInstruction(
814
+ 0 ,
815
+ " invoke-static {}, $PLAYER_CLASS_DESCRIPTOR ->shuffleTracks()V"
816
+ )
817
+
887
818
SettingsPatch .addSwitchPreference(
888
819
CategoryType .PLAYER ,
889
820
" revanced_remember_shuffle_state" ,
0 commit comments