@@ -2,65 +2,19 @@ package app.revanced.patches.spotify.layout.theme
2
2
3
3
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
4
4
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
5
- import app.revanced.patcher.fingerprint
6
5
import app.revanced.patcher.patch.booleanOption
7
6
import app.revanced.patcher.patch.bytecodePatch
8
7
import app.revanced.patcher.patch.resourcePatch
9
8
import app.revanced.patcher.patch.stringOption
10
- import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
11
9
import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch
12
10
import app.revanced.patches.spotify.shared.IS_SPOTIFY_LEGACY_APP_TARGET
13
11
import app.revanced.util.*
14
- import com.android.tools.smali.dexlib2.AccessFlags
15
- import com.android.tools.smali.dexlib2.Opcode
16
12
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
17
- import com.android.tools.smali.dexlib2.iface.reference.FieldReference
13
+ import com.android.tools.smali.dexlib2.iface.reference.MethodReference
18
14
import org.w3c.dom.Element
19
15
20
16
private const val EXTENSION_CLASS_DESCRIPTOR = " Lapp/revanced/extension/spotify/layout/theme/CustomThemePatch;"
21
17
22
- internal val spotifyBackgroundColor = stringOption(
23
- key = " backgroundColor" ,
24
- default = " @android:color/black" ,
25
- title = " Primary background color" ,
26
- description = " The background color. Can be a hex color or a resource reference." ,
27
- required = true ,
28
- )
29
-
30
- internal val overridePlayerGradientColor = booleanOption(
31
- key = " overridePlayerGradientColor" ,
32
- default = false ,
33
- title = " Override player gradient color" ,
34
- description = " Apply primary background color to the player gradient color, which changes dynamically with the song." ,
35
- required = false
36
- )
37
-
38
- internal val spotifyBackgroundColorSecondary = stringOption(
39
- key = " backgroundColorSecondary" ,
40
- default = " #FF121212" ,
41
- title = " Secondary background color" ,
42
- description =
43
- " The secondary background color. (e.g. playlist list in home, player artist, song credits). Can be a hex color or a resource reference." ,
44
- required = true ,
45
- )
46
-
47
- internal val spotifyAccentColor = stringOption(
48
- key = " accentColor" ,
49
- default = " #FF1ED760" ,
50
- title = " Accent color" ,
51
- description = " The accent color ('Spotify green' by default). Can be a hex color or a resource reference." ,
52
- required = true ,
53
- )
54
-
55
- internal val spotifyAccentColorPressed = stringOption(
56
- key = " accentColorPressed" ,
57
- default = " #FF169C46" ,
58
- title = " Pressed dark theme accent color" ,
59
- description =
60
- " The color when accented buttons are pressed, by default slightly darker than accent. Can be a hex color or a resource reference." ,
61
- required = true ,
62
- )
63
-
64
18
private val customThemeBytecodePatch = bytecodePatch {
65
19
dependsOn(sharedExtensionPatch)
66
20
@@ -71,60 +25,60 @@ private val customThemeBytecodePatch = bytecodePatch {
71
25
return @execute
72
26
}
73
27
74
- fun MutableMethod.addColorChangeInstructions (literal : Long , colorString : String ) {
75
- val index = indexOfFirstLiteralInstructionOrThrow(literal)
76
- val register = getInstruction<OneRegisterInstruction >(index).registerA
28
+ val colorSpaceUtilsClassDef = colorSpaceUtilsClassFingerprint.originalClassDef
77
29
30
+ // Hook a util method that converts ARGB to RGBA in the sRGB color space to replace hardcoded accent colors.
31
+ convertArgbToRgbaFingerprint.match(colorSpaceUtilsClassDef).method.apply {
78
32
addInstructions(
79
- index + 1 ,
33
+ 0 ,
80
34
"""
81
- const-string v$register , "$colorString "
82
- invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR ->getThemeColor(Ljava/lang/String;)J
83
- move-result-wide v$register
35
+ long-to-int p0, p0
36
+ invoke-static { p0 }, $EXTENSION_CLASS_DESCRIPTOR ->replaceColor(I)I
37
+ move-result p0
38
+ int-to-long p0, p0
84
39
"""
85
40
)
86
41
}
87
42
88
- val encoreColorsClassName = with (encoreThemeFingerprint.originalMethod) {
89
- // "Encore" colors are referenced right before the value of POSITIVE_INFINITY is returned.
90
- // Begin the instruction find using the index of where POSITIVE_INFINITY is set into the register.
91
- val positiveInfinityIndex = indexOfFirstLiteralInstructionOrThrow(
92
- Float .POSITIVE_INFINITY
93
- )
94
- val encoreColorsFieldReferenceIndex = indexOfFirstInstructionReversedOrThrow(
95
- positiveInfinityIndex,
96
- Opcode .SGET_OBJECT
97
- )
98
-
99
- getInstruction(encoreColorsFieldReferenceIndex)
100
- .getReference<FieldReference >()!! .definingClass
101
- }
102
-
103
- val encoreColorsConstructorFingerprint = fingerprint {
104
- accessFlags(AccessFlags .STATIC , AccessFlags .CONSTRUCTOR )
105
- custom { method, classDef ->
106
- classDef.type == encoreColorsClassName &&
107
- method.containsLiteralInstruction(PLAYLIST_BACKGROUND_COLOR_LITERAL )
43
+ // Lottie JSON parser method. It parses the JSON Lottie animation into its own class,
44
+ // including the solid color of it.
45
+ parseLottieJsonFingerprint.method.apply {
46
+ val invokeParseColorIndex = indexOfFirstInstructionOrThrow {
47
+ val reference = getReference<MethodReference >()
48
+ reference?.definingClass == " Landroid/graphics/Color;"
49
+ && reference.name == " parseColor"
108
50
}
109
- }
51
+ val parsedColorRegister = getInstruction< OneRegisterInstruction >(invokeParseColorIndex + 1 ).registerA
110
52
111
- val backgroundColor by spotifyBackgroundColor
112
- val backgroundColorSecondary by spotifyBackgroundColorSecondary
53
+ val replaceColorDescriptor = " $EXTENSION_CLASS_DESCRIPTOR ->replaceColor(I)I"
113
54
114
- encoreColorsConstructorFingerprint.method.apply {
115
- addColorChangeInstructions(PLAYLIST_BACKGROUND_COLOR_LITERAL , backgroundColor!! )
116
- addColorChangeInstructions(SHARE_MENU_BACKGROUND_COLOR_LITERAL , backgroundColorSecondary!! )
55
+ addInstructions(
56
+ invokeParseColorIndex + 2 ,
57
+ """
58
+ # Use invoke-static/range because the register number is too large.
59
+ invoke-static/range { v$parsedColorRegister .. v$parsedColorRegister }, $replaceColorDescriptor
60
+ move-result v$parsedColorRegister
61
+ """
62
+ )
117
63
}
118
64
119
- homeCategoryPillColorsFingerprint.method.addColorChangeInstructions(
120
- HOME_CATEGORY_PILL_COLOR_LITERAL ,
121
- backgroundColorSecondary!!
122
- )
65
+ // Lottie animated color parser.
66
+ parseAnimatedColorFingerprint.method.apply {
67
+ val invokeArgbIndex = indexOfFirstInstructionOrThrow {
68
+ val reference = getReference<MethodReference >()
69
+ reference?.definingClass == " Landroid/graphics/Color;"
70
+ && reference.name == " argb"
71
+ }
72
+ val argbColorRegister = getInstruction<OneRegisterInstruction >(invokeArgbIndex + 1 ).registerA
123
73
124
- settingsHeaderColorFingerprint.method.addColorChangeInstructions(
125
- SETTINGS_HEADER_COLOR_LITERAL ,
126
- backgroundColorSecondary!!
127
- )
74
+ addInstructions(
75
+ invokeArgbIndex + 2 ,
76
+ """
77
+ invoke-static { v$argbColorRegister }, $EXTENSION_CLASS_DESCRIPTOR ->replaceColor(I)I
78
+ move-result v$argbColorRegister
79
+ """
80
+ )
81
+ }
128
82
}
129
83
}
130
84
@@ -138,11 +92,48 @@ val customThemePatch = resourcePatch(
138
92
139
93
dependsOn(customThemeBytecodePatch)
140
94
141
- val backgroundColor by spotifyBackgroundColor()
142
- val overridePlayerGradientColor by overridePlayerGradientColor()
143
- val backgroundColorSecondary by spotifyBackgroundColorSecondary()
144
- val accentColor by spotifyAccentColor()
145
- val accentColorPressed by spotifyAccentColorPressed()
95
+ val backgroundColor by stringOption(
96
+ key = " backgroundColor" ,
97
+ default = " @android:color/black" ,
98
+ title = " Primary background color" ,
99
+ description = " The background color. Can be a hex color or a resource reference." ,
100
+ required = true ,
101
+ )
102
+
103
+ val overridePlayerGradientColor by booleanOption(
104
+ key = " overridePlayerGradientColor" ,
105
+ default = false ,
106
+ title = " Override player gradient color" ,
107
+ description =
108
+ " Apply primary background color to the player gradient color, which changes dynamically with the song." ,
109
+ required = false ,
110
+ )
111
+
112
+ val backgroundColorSecondary by stringOption(
113
+ key = " backgroundColorSecondary" ,
114
+ default = " #FF121212" ,
115
+ title = " Secondary background color" ,
116
+ description = " The secondary background color. (e.g. playlist list in home, player artist, song credits). " +
117
+ " Can be a hex color or a resource reference.\" ," ,
118
+ required = true ,
119
+ )
120
+
121
+ val accentColor by stringOption(
122
+ key = " accentColor" ,
123
+ default = " #FF1ED760" ,
124
+ title = " Accent color" ,
125
+ description = " The accent color ('Spotify green' by default). Can be a hex color or a resource reference." ,
126
+ required = true ,
127
+ )
128
+
129
+ val accentColorPressed by stringOption(
130
+ key = " accentColorPressed" ,
131
+ default = " #FF1ABC54" ,
132
+ title = " Pressed dark theme accent color" ,
133
+ description = " The color when accented buttons are pressed, by default slightly darker than accent. " +
134
+ " Can be a hex color or a resource reference." ,
135
+ required = true ,
136
+ )
146
137
147
138
execute {
148
139
document(" res/values/colors.xml" ).use { document ->
@@ -161,34 +152,41 @@ val customThemePatch = resourcePatch(
161
152
}
162
153
163
154
node.textContent = when (name) {
164
- // Gradient next to user photo and "All" in home page.
165
- " dark_base_background_base" ,
166
- // Main background.
155
+ // Main background color.
167
156
" gray_7" ,
168
- // Left sidebar background in tablet mode.
157
+ // Left sidebar background color in tablet mode.
169
158
" gray_10" ,
170
- // "Add account", "Settings and privacy", "View Profile" left sidebar background.
159
+ // Gradient next to user photo and "All" in home page.
160
+ " dark_base_background_base" ,
161
+ // "Add account", "Settings and privacy", "View Profile" left sidebar background color.
171
162
" dark_base_background_elevated_base" ,
172
163
// Song/player gradient start/end color.
173
164
" bg_gradient_start_color" , " bg_gradient_end_color" ,
174
- // Login screen background and gradient start.
165
+ // Login screen background color and gradient start.
175
166
" sthlm_blk" , " sthlm_blk_grad_start" ,
176
167
// Misc.
177
168
" image_placeholder_color" ,
178
169
-> backgroundColor
179
170
180
- // Track credits, merch background in song player.
171
+ // "About the artist" background color in song player.
172
+ " gray_15" ,
173
+ // Track credits, merch background color in song player.
181
174
" track_credits_card_bg" , " benefit_list_default_color" , " merch_card_background" ,
182
175
// Playlist list background in home page.
183
176
" opacity_white_10" ,
184
- // "About the artist" background in song player.
185
- " gray_15" ,
186
177
// "What's New" pills background.
187
178
" dark_base_background_tinted_highlight"
188
179
-> backgroundColorSecondary
189
180
190
- " dark_brightaccent_background_base" , " dark_base_text_brightaccent" , " green_light" -> accentColor
191
- " dark_brightaccent_background_press" -> accentColorPressed
181
+ " dark_brightaccent_background_base" ,
182
+ " dark_base_text_brightaccent" ,
183
+ " green_light" ,
184
+ " spotify_green_157"
185
+ -> accentColor
186
+
187
+ " dark_brightaccent_background_press"
188
+ -> accentColorPressed
189
+
192
190
else -> continue
193
191
}
194
192
}
@@ -198,8 +196,8 @@ val customThemePatch = resourcePatch(
198
196
document(" res/drawable/start_screen_gradient.xml" ).use { document ->
199
197
val gradientNode = document.getElementsByTagName(" gradient" ).item(0 ) as Element
200
198
201
- gradientNode.setAttribute(" android:startColor" , backgroundColor )
202
- gradientNode.setAttribute(" android:endColor" , backgroundColor )
199
+ gradientNode.setAttribute(" android:startColor" , " @color/gray_7 " )
200
+ gradientNode.setAttribute(" android:endColor" , " @color/gray_7 " )
203
201
}
204
202
}
205
203
}
0 commit comments