Skip to content

fix(Spotify): Apply accent colour in more places #5039

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: dev
Choose a base branch
from

Conversation

LavaDesu
Copy link

@LavaDesu LavaDesu commented May 26, 2025

Apply the accent colour in more places by patching:

  • A method that converts ARGB colours into RGB. This method is used with every hardcoded colour I could find
  • A method that parses Lottie JSONs for solid colours
  • A method that parses Lottie animated colours.

Lottie animations are used in places like the "add to" buttons (you can see a small animation when clicking it) and the animated waves when playing a playlist on your homepage.

@LisoUseInAIKyrios LisoUseInAIKyrios requested a review from Nuckyz May 26, 2025 11:15
@LisoUseInAIKyrios LisoUseInAIKyrios linked an issue May 26, 2025 that may be closed by this pull request
3 tasks
@LisoUseInAIKyrios
Copy link
Contributor

LisoUseInAIKyrios commented May 26, 2025

For future reference:

If the Spotify Lottie animations always use the same theme colors for all animation files (and all usage of that color should be replaced), it's probably better to use what's here and replace the colors in the parser.

But if more fine grained replacement is desired, the colors could be replaced in the Lottie animations JSON before the animation is parsed. Then some animations can be ignored and/or animation colors can be selectively replaced.

ReVanced YouTube does this for the animated launch splash screen. Only the red seekbar color is replaced, but the identical red YouTube play icon in the same animation is not changed.


public static void setSplashAnimationLottie(LottieAnimationView view, int resourceId) {

@ILoveOpenSourceApplications

This comment was marked as resolved.

@LavaDesu
Copy link
Author

@LisoUseInAIKyrios Interesting! I did look into patching the JSON text directly instead of during parsing, but it quickly starting getting long and complex because of the amount of files that needed to be replaced and the code associated with it.

I went with doing it this way because it is far more simple and also probably more futureproof if Spotify decides to convert more of its buttons into animations. The default green accent is a pretty unique brand colour so it shouldn't affect much unintended areas.

Good to know though in case we want to exclude some icons later. The technique to call obfuscated methods from extensions in there is pretty sweet.

@LavaDesu
Copy link
Author

Is #4812 within the scope of this PR or not?

No, this PR only deals with accent colours for now.

@Nuckyz
Copy link
Member

Nuckyz commented May 27, 2025

Do resource colors pass through this function which converts ARGB to RGB too? If yes, then the resource patches can likely be moved to this new approach which would also caught colors we should be patching but aren't

@LisoUseInAIKyrios
Copy link
Contributor

If all the colors can be replaced at runtime, then it opens the possibility changing the theme color in app (and not specify theme colors at patch time). But that also requires adding an in app settings menu which currently does not exist.

@Nuckyz
Copy link
Member

Nuckyz commented May 27, 2025

If all the colors can be replaced at runtime, then it opens the possibility changing the theme color in app (and not specify theme colors at patch time). But that also requires adding an in app settings menu which currently does not exist.

Replacing at runtime also allows us to fix more hardcoded colors which are present all over the app

@LavaDesu
Copy link
Author

Do resource colors pass through this function which converts ARGB to RGB too? If yes, then the resource patches can likely be moved to this new approach which would also caught colors we should be patching but aren't

Unfortunately I don't think the resource colours pass through this function at all.

Replacing at runtime also allows us to fix more hardcoded colors which are present all over the app

The patch does intercept and replace hardcoded colours at runtime, but it's only configurable at build time for now.

@Nuckyz
Copy link
Member

Nuckyz commented May 27, 2025

Do resource colors pass through this function which converts ARGB to RGB too? If yes, then the resource patches can likely be moved to this new approach which would also caught colors we should be patching but aren't

Unfortunately I don't think the resource colours pass through this function at all.

Resource colors are ARGB too, and if the app is converting hardcoded ARGB to RGB then perhaps it also does for resource colors with a different function?

Replacing at runtime also allows us to fix more hardcoded colors which are present all over the app

The patch does intercept and replace hardcoded colours at runtime, but it's only configurable at build time for now.

Colors being configurable only at build time is fine for now

@LavaDesu
Copy link
Author

LavaDesu commented May 27, 2025

Resource colors are ARGB too, and if the app is converting hardcoded ARGB to RGB then perhaps it also does for resource colors with a different function?

I went digging and couldn't find anything worthwhile. context.getColor is called in various different places, and doesn't pass through any one function. But I guess we could make some fingerprint that matches all invocations of it and loop through them? If we wanna manage colours at runtime. It could then be a shared patch too.

I think keeping it a resource patch is fine tho. And also, it supports Material 3 dynamic colours as well since we fetch colours from resources. So I don't think there's going to be a big audience of people who like to change their accent colours super often.

FWIW, Material 3 colours:

Colour Value (A12-13) Value (A14+)
Background @android:color/m3_ref_palette_dynamic_neutral_variant6 @android:color/system_background_dark
Background Secondary @android:color/m3_ref_palette_dynamic_neutral_variant17 @android:color/system_surface_container_high_dark
Accent @android:color/system_accent1_200 @android:color/system_primary_dark
Accent Pressed @android:color/system_accent1_600 @android:color/system_primary_light

@@ -125,6 +126,38 @@ private val customThemeBytecodePatch = bytecodePatch {
SETTINGS_HEADER_COLOR_LITERAL,
backgroundColorSecondary!!
)

// Hijacks a util method that removes alpha to replace hardcoded accent colors
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments end with punctuation

Suggested change
// Hijacks a util method that removes alpha to replace hardcoded accent colors
// Hijacks a util method that removes alpha to replace hardcoded accent colors.

Comment on lines +132 to +135
addInstructions(0, """
invoke-static { p0, p1 }, $EXTENSION_CLASS_DESCRIPTOR->replaceColor(J)J
move-result-wide p0
""")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be formatted like this

Suggested change
addInstructions(0, """
invoke-static { p0, p1 }, $EXTENSION_CLASS_DESCRIPTOR->replaceColor(J)J
move-result-wide p0
""")
addInstructions(
0,
"""
invoke-static { p0, p1 }, $EXTENSION_CLASS_DESCRIPTOR->replaceColor(J)J
move-result-wide p0
"""
)

// Lottie JSON parser method
// It's a gigantic method that parses each value, including the solid color
parseLottieJsonFingerprint.method.apply {
val invokeIdx = indexOfFirstInstructionOrThrow {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variables names should not be shortened

Suggested change
val invokeIdx = indexOfFirstInstructionOrThrow {
val invokeIndex = indexOfFirstInstructionOrThrow {

// It's a gigantic method that parses each value, including the solid color
parseLottieJsonFingerprint.method.apply {
val invokeIdx = indexOfFirstInstructionOrThrow {
val ref = this.getReference<MethodReference>()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explicit receiver not needed

Suggested change
val ref = this.getReference<MethodReference>()
val ref = getReference<MethodReference>()

// Lottie JSON parser method
// It's a gigantic method that parses each value, including the solid color
parseLottieJsonFingerprint.method.apply {
val invokeIdx = indexOfFirstInstructionOrThrow {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont remember but isnt there a helper function for this codeblock?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think for a situation like this. There is a helper for overriding constants passed into methods, but not quite this.

@LisoUseInAIKyrios LisoUseInAIKyrios removed the request for review from Nuckyz May 30, 2025 19:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

bug(Spotify): Inconsistencies with Spotify theme patch
6 participants