diff --git a/extensions/spotify/build.gradle.kts b/extensions/spotify/build.gradle.kts index 07cce9e23..4a5ce935d 100644 --- a/extensions/spotify/build.gradle.kts +++ b/extensions/spotify/build.gradle.kts @@ -1,3 +1,16 @@ dependencies { + compileOnly(project(":extensions:shared:library")) compileOnly(project(":extensions:spotify:stub")) + compileOnly(libs.annotation) +} + +android { + defaultConfig { + minSdk = 24 + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } } diff --git a/extensions/spotify/src/main/java/app/revanced/extension/spotify/misc/UnlockPremiumPatch.java b/extensions/spotify/src/main/java/app/revanced/extension/spotify/misc/UnlockPremiumPatch.java index 3cbb55d5d..b295b39e1 100644 --- a/extensions/spotify/src/main/java/app/revanced/extension/spotify/misc/UnlockPremiumPatch.java +++ b/extensions/spotify/src/main/java/app/revanced/extension/spotify/misc/UnlockPremiumPatch.java @@ -2,38 +2,57 @@ package app.revanced.extension.spotify.misc; import com.spotify.remoteconfig.internal.AccountAttribute; +import java.util.HashMap; import java.util.Map; -import java.util.Objects; + +import app.revanced.extension.shared.Logger; /** * @noinspection unused */ public final class UnlockPremiumPatch { - private static final Map OVERRIDES = Map.of( - // Disables player and app ads. - "ads", false, - // Works along on-demand, allows playing any song without restriction. - "player-license", "premium", - // Disables shuffle being initially enabled when first playing a playlist. - "shuffle", false, - // Allows playing any song on-demand, without a shuffled order. - "on-demand", true, - // Make sure playing songs is not disabled remotely and playlists show up. - "streaming", true, - // Allows adding songs to queue and removes the smart shuffle mode restriction, - // allowing to pick any of the other modes. - "pick-and-shuffle", false, - // Disables shuffle-mode streaming-rule, which forces songs to be played shuffled - // and breaks the player when other patches are applied. - "streaming-rules", "", - // Enables premium UI in settings and removes the premium button in the nav-bar. - "nft-disabled", "1" - ); + private static final Map OVERRIDES = new HashMap<>() {{ + // Disables player and app ads. + put("ads", false); + // Works along on-demand, allows playing any song without restriction. + put("player-license", "premium"); + // Disables shuffle being initially enabled when first playing a playlist. + put("shuffle", false); + // Allows playing any song on-demand, without a shuffled order. + put("on-demand", true); + // Make sure playing songs is not disabled remotely and playlists show up. + put("streaming", true); + // Allows adding songs to queue and removes the smart shuffle mode restriction, + // allowing to pick any of the other modes. + put("pick-and-shuffle", false); + // Disables shuffle-mode streaming-rule, which forces songs to be played shuffled + // and breaks the player when other patches are applied. + put("streaming-rules", ""); + // Enables premium UI in settings and removes the premium button in the nav-bar. + put("nft-disabled", "1"); + // Enable Cross-Platform Spotify Car Thing. + put("can_use_superbird", true); + // Removes the premium button in the nav-bar for tablet users. + put("tablet-free", false); + }}; + + /** + * Injection point. + */ public static void overrideAttribute(Map attributes) { - for (var entry : OVERRIDES.entrySet()) { - var attribute = Objects.requireNonNull(attributes.get(entry.getKey())); - attribute.value_ = entry.getValue(); + try { + for (var entry : OVERRIDES.entrySet()) { + var key = entry.getKey(); + var attribute = attributes.get(key); + if (attribute == null) { + Logger.printException(() -> "Account attribute not found: " + key); + } else { + attribute.value_ = entry.getValue(); + } + } + } catch (Exception ex) { + Logger.printException(() -> "overrideAttribute failure", ex); } } } diff --git a/patches/api/patches.api b/patches/api/patches.api index f84f9113e..f933fdf66 100644 --- a/patches/api/patches.api +++ b/patches/api/patches.api @@ -820,6 +820,10 @@ public final class app/revanced/patches/spotify/misc/UnlockPremiumPatchKt { public static final fun getUnlockPremiumPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } +public final class app/revanced/patches/spotify/misc/extension/ExtensionPatchKt { + public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + public final class app/revanced/patches/spotify/misc/fix/SpoofSignaturePatchKt { public static final fun getSpoofSignaturePatch ()Lapp/revanced/patcher/patch/BytecodePatch; } diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/layout/theme/CustomThemePatch.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/layout/theme/CustomThemePatch.kt index 28d48e010..8af9d65fb 100644 --- a/patches/src/main/kotlin/app/revanced/patches/spotify/layout/theme/CustomThemePatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/spotify/layout/theme/CustomThemePatch.kt @@ -10,6 +10,7 @@ import org.w3c.dom.Element val customThemePatch = resourcePatch( name = "Custom theme", description = "Applies a custom theme.", + use = false, ) { compatibleWith("com.spotify.music") diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/UnlockPremiumPatch.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/UnlockPremiumPatch.kt index 5146fcdaa..b81699fdb 100644 --- a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/UnlockPremiumPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/UnlockPremiumPatch.kt @@ -4,9 +4,12 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.spotify.misc.extension.sharedExtensionPatch import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/spotify/misc/UnlockPremiumPatch;" + @Suppress("unused") val unlockPremiumPatch = bytecodePatch( name = "Unlock Spotify Premium", @@ -14,11 +17,12 @@ val unlockPremiumPatch = bytecodePatch( ) { compatibleWith("com.spotify.music") - extendWith("extensions/spotify.rve") + dependsOn(sharedExtensionPatch) execute { // Make _value accessible so that it can be overridden in the extension. accountAttributeFingerprint.classDef.fields.first { it.name == "value_" }.apply { + // Add public flag and remove private. accessFlags = accessFlags.or(AccessFlags.PUBLIC.value).and(AccessFlags.PRIVATE.value.inv()) } @@ -28,7 +32,7 @@ val unlockPremiumPatch = bytecodePatch( productStateProtoFingerprint.method.addInstruction( instantiateUnmodifiableMapIndex, "invoke-static { v$attributesMapRegister }," + - "Lapp/revanced/extension/spotify/misc/UnlockPremiumPatch;->overrideAttribute(Ljava/util/Map;)V", + "$EXTENSION_CLASS_DESCRIPTOR->overrideAttribute(Ljava/util/Map;)V", ) // Add the query parameter trackRows to show popular tracks in the artist page. diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/extension/ExtensionPatch.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/extension/ExtensionPatch.kt new file mode 100644 index 000000000..6b95f437a --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/extension/ExtensionPatch.kt @@ -0,0 +1,5 @@ +package app.revanced.patches.spotify.misc.extension + +import app.revanced.patches.shared.misc.extension.sharedExtensionPatch + +val sharedExtensionPatch = sharedExtensionPatch("spotify", spotifyMainActivityOnCreate) diff --git a/patches/src/main/kotlin/app/revanced/patches/spotify/misc/extension/Hooks.kt b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/extension/Hooks.kt new file mode 100644 index 000000000..baed926ea --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/spotify/misc/extension/Hooks.kt @@ -0,0 +1,10 @@ +package app.revanced.patches.spotify.misc.extension + +import app.revanced.patches.shared.misc.extension.extensionHook + +internal val spotifyMainActivityOnCreate = extensionHook { + custom { method, classDef -> + classDef.type == "Lcom/spotify/music/SpotifyMainActivity;" && + method.name == "onCreate" + } +}