mirror of
https://github.com/revanced/revanced-patches.git
synced 2025-06-12 13:17:38 +02:00
feat(Spotify): Add limited support for version 8.6.98.900
(last version that supports Kenwood and Pioneer car stereos) (#4750)
This commit is contained in:

committed by
GitHub

parent
b91285ec20
commit
a3fde874af
@ -3,7 +3,6 @@ package app.revanced.extension.spotify.misc;
|
||||
import static java.lang.Boolean.FALSE;
|
||||
import static java.lang.Boolean.TRUE;
|
||||
|
||||
import com.spotify.remoteconfig.internal.AccountAttribute;
|
||||
import com.spotify.home.evopage.homeapi.proto.Section;
|
||||
|
||||
import java.util.List;
|
||||
@ -15,6 +14,25 @@ import app.revanced.extension.shared.Logger;
|
||||
@SuppressWarnings("unused")
|
||||
public final class UnlockPremiumPatch {
|
||||
|
||||
private static final String SPOTIFY_MAIN_ACTIVITY_LEGACY = "com.spotify.music.MainActivity";
|
||||
|
||||
/**
|
||||
* If the app target is 8.6.98.900.
|
||||
*/
|
||||
private static final boolean IS_SPOTIFY_LEGACY_APP_TARGET;
|
||||
|
||||
static {
|
||||
boolean legacy;
|
||||
try {
|
||||
Class.forName(SPOTIFY_MAIN_ACTIVITY_LEGACY);
|
||||
legacy = true;
|
||||
} catch (ClassNotFoundException ex) {
|
||||
legacy = false;
|
||||
}
|
||||
|
||||
IS_SPOTIFY_LEGACY_APP_TARGET = legacy;
|
||||
}
|
||||
|
||||
private static class OverrideAttribute {
|
||||
/**
|
||||
* Account attribute key.
|
||||
@ -55,8 +73,8 @@ public final class UnlockPremiumPatch {
|
||||
// Make sure playing songs is not disabled remotely and playlists show up.
|
||||
new OverrideAttribute("streaming", TRUE),
|
||||
// Allows adding songs to queue and removes the smart shuffle mode restriction,
|
||||
// allowing to pick any of the other modes.
|
||||
new OverrideAttribute("pick-and-shuffle", FALSE),
|
||||
// allowing to pick any of the other modes. Flag is not present in legacy app target.
|
||||
new OverrideAttribute("pick-and-shuffle", FALSE, !IS_SPOTIFY_LEGACY_APP_TARGET),
|
||||
// Disables shuffle-mode streaming-rule, which forces songs to be played shuffled
|
||||
// and breaks the player when other patches are applied.
|
||||
new OverrideAttribute("streaming-rules", ""),
|
||||
@ -78,7 +96,7 @@ public final class UnlockPremiumPatch {
|
||||
/**
|
||||
* Override attributes injection point.
|
||||
*/
|
||||
public static void overrideAttribute(Map<String, AccountAttribute> attributes) {
|
||||
public static void overrideAttribute(Map<String, /*AccountAttribute*/ Object> attributes) {
|
||||
try {
|
||||
for (var override : OVERRIDES) {
|
||||
var attribute = attributes.get(override.key);
|
||||
@ -87,7 +105,12 @@ public final class UnlockPremiumPatch {
|
||||
Logger.printException(() -> "'" + override.key + "' expected but not found");
|
||||
}
|
||||
} else {
|
||||
attribute.value_ = override.overrideValue;
|
||||
Object overrideValue = override.overrideValue;
|
||||
if (IS_SPOTIFY_LEGACY_APP_TARGET) {
|
||||
((com.spotify.useraccount.v1.AccountAttribute) attribute).value_ = overrideValue;
|
||||
} else {
|
||||
((com.spotify.remoteconfig.internal.AccountAttribute) attribute).value_ = overrideValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
|
@ -0,0 +1,8 @@
|
||||
package com.spotify.useraccount.v1;
|
||||
|
||||
/**
|
||||
* Used for target 8.6.98.900. Class is still present in newer app targets.
|
||||
*/
|
||||
public class AccountAttribute {
|
||||
public Object value_;
|
||||
}
|
@ -92,7 +92,7 @@ fun sharedExtensionPatch(
|
||||
}
|
||||
|
||||
class ExtensionHook internal constructor(
|
||||
private val fingerprint: Fingerprint,
|
||||
internal val fingerprint: Fingerprint,
|
||||
private val insertIndexResolver: ((Method) -> Int),
|
||||
private val contextRegisterResolver: (Method) -> String,
|
||||
) {
|
||||
|
@ -5,6 +5,7 @@ import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||
import app.revanced.patches.spotify.misc.extension.IS_SPOTIFY_LEGACY_APP_TARGET
|
||||
import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.util.*
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
@ -21,6 +22,12 @@ internal val customThemeByteCodePatch = bytecodePatch {
|
||||
val backgroundColorSecondary by spotifyBackgroundColorSecondary
|
||||
|
||||
execute {
|
||||
if (IS_SPOTIFY_LEGACY_APP_TARGET) {
|
||||
// Bytecode changes are not needed for legacy app target.
|
||||
// Player background color is changed with existing resource patch.
|
||||
return@execute
|
||||
}
|
||||
|
||||
fun MutableMethod.addColorChangeInstructions(literal: Long, colorString: String) {
|
||||
val index = indexOfFirstLiteralInstructionOrThrow(literal)
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
|
@ -1,16 +1,29 @@
|
||||
package app.revanced.patches.spotify.misc
|
||||
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.patches.spotify.misc.extension.IS_SPOTIFY_LEGACY_APP_TARGET
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal val accountAttributeFingerprint = fingerprint {
|
||||
custom { _, classDef -> classDef.endsWith("internal/AccountAttribute;") }
|
||||
custom { _, classDef ->
|
||||
classDef.type == if (IS_SPOTIFY_LEGACY_APP_TARGET) {
|
||||
"Lcom/spotify/useraccount/v1/AccountAttribute;"
|
||||
} else {
|
||||
"Lcom/spotify/remoteconfig/internal/AccountAttribute;"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal val productStateProtoFingerprint = fingerprint {
|
||||
returns("Ljava/util/Map;")
|
||||
custom { _, classDef -> classDef.endsWith("ProductStateProto;") }
|
||||
custom { _, classDef ->
|
||||
classDef.type == if (IS_SPOTIFY_LEGACY_APP_TARGET) {
|
||||
"Lcom/spotify/ucs/proto/v0/UcsResponseWrapper${'$'}AccountAttributesResponse;"
|
||||
} else {
|
||||
"Lcom/spotify/remoteconfig/internal/ProductStateProto;"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal val buildQueryParametersFingerprint = fingerprint {
|
||||
|
@ -6,6 +6,7 @@ import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||
import app.revanced.patcher.fingerprint
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.spotify.misc.extension.IS_SPOTIFY_LEGACY_APP_TARGET
|
||||
import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch
|
||||
import app.revanced.util.*
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
@ -14,6 +15,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||
import java.util.logging.Logger
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/spotify/misc/UnlockPremiumPatch;"
|
||||
|
||||
@ -53,6 +55,12 @@ val unlockPremiumPatch = bytecodePatch(
|
||||
method.replaceInstruction(addQueryParameterConditionIndex, "nop")
|
||||
}
|
||||
|
||||
if (IS_SPOTIFY_LEGACY_APP_TARGET) {
|
||||
return@execute Logger.getLogger(this::class.java.name).info(
|
||||
"Patching a legacy Spotify version. Patch functionality may be limited."
|
||||
)
|
||||
}
|
||||
|
||||
// Disable the "Spotify Premium" upsell experiment in context menus.
|
||||
with(contextMenuExperimentsFingerprint) {
|
||||
val moveIsEnabledIndex = method.indexOfFirstInstructionOrThrow(
|
||||
|
@ -1,5 +1,20 @@
|
||||
package app.revanced.patches.spotify.misc.extension
|
||||
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
|
||||
|
||||
val sharedExtensionPatch = sharedExtensionPatch("spotify", spotifyMainActivityOnCreate)
|
||||
/**
|
||||
* If patching a legacy 8.x target. This may also be set if patching slightly older/newer app targets,
|
||||
* but the only legacy target of interest is 8.6.98.900 as it's the last version that
|
||||
* supports Spotify integration on Kenwood/Pioneer car stereos.
|
||||
*/
|
||||
internal var IS_SPOTIFY_LEGACY_APP_TARGET = false
|
||||
|
||||
val sharedExtensionPatch = bytecodePatch {
|
||||
dependsOn(sharedExtensionPatch("spotify", spotifyMainActivityOnCreate))
|
||||
|
||||
execute {
|
||||
IS_SPOTIFY_LEGACY_APP_TARGET = spotifyMainActivityOnCreate.fingerprint
|
||||
.originalClassDef.type == SPOTIFY_MAIN_ACTIVITY_LEGACY
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,16 @@ package app.revanced.patches.spotify.misc.extension
|
||||
|
||||
import app.revanced.patches.shared.misc.extension.extensionHook
|
||||
|
||||
private const val SPOTIFY_MAIN_ACTIVITY = "Lcom/spotify/music/SpotifyMainActivity;"
|
||||
|
||||
/**
|
||||
* Main activity of target 8.6.98.900.
|
||||
*/
|
||||
internal const val SPOTIFY_MAIN_ACTIVITY_LEGACY = "Lcom/spotify/music/MainActivity;"
|
||||
|
||||
internal val spotifyMainActivityOnCreate = extensionHook {
|
||||
custom { method, classDef ->
|
||||
classDef.type == "Lcom/spotify/music/SpotifyMainActivity;" &&
|
||||
method.name == "onCreate"
|
||||
method.name == "onCreate" && (classDef.type == SPOTIFY_MAIN_ACTIVITY
|
||||
|| classDef.type == SPOTIFY_MAIN_ACTIVITY_LEGACY)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user