bump v2.147.0

This commit is contained in:
inotia00
2022-12-23 17:49:31 +09:00
parent 74aecf4720
commit 187d905bdb
1034 changed files with 21004 additions and 11645 deletions

View File

@ -1,37 +0,0 @@
package app.revanced.patches.all.interaction.gestures.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotations.Patch
@Patch(false)
@Name("predictive-back-gesture")
@Description("Enables the predictive back gesture introduced on Android 13.")
@Version("0.0.1")
class PredictiveBackGesturePatch : ResourcePatch {
override fun execute(context: ResourceContext): PatchResult {
context.xmlEditor["AndroidManifest.xml"].use { editor ->
val document = editor.file
with(document.getElementsByTagName("application").item(0)) {
if (attributes.getNamedItem(FLAG) != null) return@with
document.createAttribute(FLAG)
.apply { value = "true" }
.let(attributes::setNamedItem)
}
}
return PatchResultSuccess()
}
private companion object {
const val FLAG = "android:enableOnBackInvokedCallback"
}
}

View File

@ -1,7 +0,0 @@
package app.revanced.patches.backdrops.misc.pro.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility([Package("com.backdrops.wallpapers")])
internal annotation class ProUnlockCompatibility

View File

@ -1,15 +0,0 @@
package app.revanced.patches.backdrops.misc.pro.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.Opcode
object ProUnlockFingerprint : MethodFingerprint(
opcodes = listOf(
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.INVOKE_INTERFACE,
Opcode.MOVE_RESULT,
Opcode.IF_EQZ
),
customFingerprint = { it.definingClass == "Lcom/backdrops/wallpapers/data/local/DatabaseHandlerIAB;" && it.name == "lambda\$existPurchase\$0" }
)

View File

@ -1,42 +0,0 @@
package app.revanced.patches.backdrops.misc.pro.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.MethodFingerprintExtensions.name
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.instruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultError
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.backdrops.misc.pro.annotations.ProUnlockCompatibility
import app.revanced.patches.backdrops.misc.pro.fingerprints.ProUnlockFingerprint
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
@Patch
@Name("pro-unlock")
@Description("Unlocks pro-only functions.")
@ProUnlockCompatibility
@Version("0.0.1")
class ProUnlockPatch : BytecodePatch(
listOf(ProUnlockFingerprint)
) {
override fun execute(context: BytecodeContext): PatchResult {
val result = ProUnlockFingerprint.result ?: return PatchResultError("${ProUnlockFingerprint.name} not found")
val moveRegisterInstruction = result.mutableMethod.instruction(result.scanResult.patternScanResult!!.endIndex - 1)
val register = (moveRegisterInstruction as OneRegisterInstruction).registerA
result.mutableMethod.addInstructions(
result.scanResult.patternScanResult!!.endIndex,
"""
const/4 v$register, 0x1
"""
)
return PatchResultSuccess()
}
}

View File

@ -1,7 +0,0 @@
package app.revanced.patches.citra.misc.premium.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility([Package("org.citra.citra_emu"), Package("org.citra.citra_emu.canary")])
internal annotation class PremiumUnlockCompatbility

View File

@ -1,7 +0,0 @@
package app.revanced.patches.citra.misc.premium.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
object PremiumUnlockFingerprint : MethodFingerprint(
customFingerprint = { it.definingClass == "Lorg/citra/citra_emu/ui/main/MainActivity;" && it.name == "isPremiumActive" }
)

View File

@ -1,37 +0,0 @@
package app.revanced.patches.citra.misc.premium.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.MethodFingerprintExtensions.name
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultError
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patches.citra.misc.premium.annotations.PremiumUnlockCompatbility
import app.revanced.patches.citra.misc.premium.fingerprints.PremiumUnlockFingerprint
@Patch
@Name("premium-unlock")
@Description("Unlocks premium functions.")
@PremiumUnlockCompatbility
@Version("0.0.1")
class PremiumUnlockPatch : BytecodePatch(
listOf(PremiumUnlockFingerprint)
) {
override fun execute(context: BytecodeContext): PatchResult {
val result = PremiumUnlockFingerprint.result ?: return PatchResultError("${PremiumUnlockFingerprint.name} not found")
result.mutableMethod.addInstructions(
0,
"""
const v0, 0x1
return v0
"""
)
return PatchResultSuccess()
}
}

View File

@ -1,13 +0,0 @@
package app.revanced.patches.hexeditor.ad.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility(
[
Package("com.myprog.hexedit")
]
)
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class HexEditorAdsCompatibility

View File

@ -1,9 +0,0 @@
package app.revanced.patches.hexeditor.ad.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
object PrimaryAdsFingerprint : MethodFingerprint(
customFingerprint = { methodDef ->
methodDef.definingClass.endsWith("PreferencesHelper;") && methodDef.name == "isAdsDisabled"
}
)

View File

@ -1,39 +0,0 @@
package app.revanced.patches.hexeditor.ad.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.removeInstruction
import app.revanced.patcher.extensions.replaceInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.hexeditor.ad.annotations.HexEditorAdsCompatibility
import app.revanced.patches.hexeditor.ad.fingerprints.PrimaryAdsFingerprint
@Patch
@Name("disable-ads")
@Description("Disables ads in HexEditor.")
@HexEditorAdsCompatibility
@Version("0.0.1")
class HexEditorAdsPatch : BytecodePatch(
listOf(
PrimaryAdsFingerprint
)
) {
override fun execute(context: BytecodeContext): PatchResult {
val method = PrimaryAdsFingerprint.result!!.mutableMethod
method.replaceInstructions(
0,
"""
const/4 v0, 0x1
return v0
"""
)
return PatchResultSuccess()
}
}

View File

@ -1,9 +0,0 @@
package app.revanced.patches.iconpackstudio.misc.pro.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility([Package("ginlemon.iconpackstudio")])
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class UnlockProCompatibility

View File

@ -1,8 +0,0 @@
package app.revanced.patches.iconpackstudio.misc.pro.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
object CheckProFingerprint : MethodFingerprint(
"Z",
customFingerprint = { it.definingClass.endsWith("IPSPurchaseRepository;")}
)

View File

@ -1,38 +0,0 @@
package app.revanced.patches.iconpackstudio.misc.pro.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.removeInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.iconpackstudio.misc.pro.annotations.UnlockProCompatibility
import app.revanced.patches.iconpackstudio.misc.pro.fingerprints.CheckProFingerprint
@Patch
@Name("unlock-pro")
@Description("Unlocks all pro features.")
@UnlockProCompatibility
@Version("0.0.1")
class UnlockProPatch : BytecodePatch(
listOf(
CheckProFingerprint
)
) {
override fun execute(context: BytecodeContext): PatchResult {
val method = CheckProFingerprint.result!!.mutableMethod
method.addInstructions(
0,
"""
const/4 v0, 0x1
return v0
"""
)
return PatchResultSuccess()
}
}

View File

@ -1,9 +0,0 @@
package app.revanced.patches.moneymanager.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility([Package("com.ithebk.expensemanager")])
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class UnlockProCompatibility

View File

@ -1,19 +0,0 @@
package app.revanced.patches.moneymanager.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
object UnlockProFingerprint : MethodFingerprint(
"Z",
AccessFlags.STATIC or AccessFlags.SYNTHETIC,
parameters = listOf("L"),
opcodes = listOf(
Opcode.IGET_BOOLEAN,
Opcode.RETURN
),
customFingerprint = { methodDef ->
methodDef.definingClass.endsWith("MainActivity;")
}
)

View File

@ -1,34 +0,0 @@
package app.revanced.patches.moneymanager.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.moneymanager.annotations.UnlockProCompatibility
import app.revanced.patches.moneymanager.fingerprints.UnlockProFingerprint
@Patch
@Name("unlock-pro")
@Description("Unlocks pro features.")
@UnlockProCompatibility
@Version("0.0.1")
class UnlockProPatch : BytecodePatch(
listOf(UnlockProFingerprint)
) {
override fun execute(context: BytecodeContext): PatchResult {
UnlockProFingerprint.result!!.mutableMethod.addInstructions(
0,
"""
const/4 v0, 0x1
return v0
"""
)
return PatchResultSuccess()
}
}

View File

@ -1,30 +0,0 @@
package app.revanced.patches.music.ad.video.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility(
[Package(
"com.google.android.apps.youtube.music",
arrayOf(
"5.14.53",
"5.16.51",
"5.17.51",
"5.21.52",
"5.22.54",
"5.23.50",
"5.25.51",
"5.25.52",
"5.26.52",
"5.27.51",
"5.28.52",
"5.29.52",
"5.31.50",
"5.34.51",
"5.36.51"
)
)]
)
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class MusicVideoAdsCompatibility

View File

@ -1,27 +0,0 @@
package app.revanced.patches.music.ad.video.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
object ShowMusicVideoAdsConstructorFingerprint : MethodFingerprint(
"V", AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, listOf("L", "L", "L"), listOf(
Opcode.INVOKE_DIRECT,
Opcode.NEW_INSTANCE,
Opcode.INVOKE_DIRECT,
Opcode.IPUT_OBJECT,
Opcode.NEW_INSTANCE,
Opcode.INVOKE_DIRECT,
Opcode.IPUT_OBJECT,
Opcode.NEW_INSTANCE,
Opcode.INVOKE_DIRECT,
Opcode.IPUT_OBJECT,
Opcode.IPUT_OBJECT,
Opcode.IPUT_OBJECT,
Opcode.CONST_4,
Opcode.IPUT_BOOLEAN,
Opcode.RETURN_VOID
)
)

View File

@ -1,14 +0,0 @@
package app.revanced.patches.music.ad.video.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
object ShowMusicVideoAdsFingerprint : MethodFingerprint(
"V", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf("Z"), listOf(
Opcode.IPUT_BOOLEAN,
Opcode.INVOKE_VIRTUAL,
Opcode.RETURN_VOID
)
)

View File

@ -5,35 +5,38 @@ import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.instruction
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.music.ad.video.annotations.MusicVideoAdsCompatibility
import app.revanced.patches.music.ad.video.fingerprints.ShowMusicVideoAdsConstructorFingerprint
import app.revanced.patches.music.ad.video.fingerprints.ShowMusicVideoAdsFingerprint
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.music.misc.integrations.patch.MusicIntegrationsPatch
import app.revanced.patches.music.misc.settings.patch.MusicSettingsPatch
import app.revanced.shared.annotation.YouTubeMusicCompatibility
import app.revanced.shared.patches.videoads.GeneralVideoAdsPatch
@Patch
@DependsOn(
[
GeneralVideoAdsPatch::class,
MusicIntegrationsPatch::class,
MusicSettingsPatch::class
]
)
@Name("music-video-ads")
@Description("Removes ads in the music player.")
@MusicVideoAdsCompatibility
@YouTubeMusicCompatibility
@Version("0.0.1")
class MusicVideoAdsPatch : BytecodePatch(
listOf(
ShowMusicVideoAdsConstructorFingerprint
)
) {
class MusicVideoAdsPatch : BytecodePatch() {
override fun execute(context: BytecodeContext): PatchResult {
ShowMusicVideoAdsFingerprint.resolve(context, ShowMusicVideoAdsConstructorFingerprint.result!!.classDef)
val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/settings/MusicSettings;->getShowAds()Z"
val result = ShowMusicVideoAdsFingerprint.result!!
GeneralVideoAdsPatch.injectLegacyAds(INTEGRATIONS_CLASS_DESCRIPTOR)
result.mutableMethod.addInstructions(
result.scanResult.patternScanResult!!.startIndex, """
const/4 p1, 0x0
"""
)
GeneralVideoAdsPatch.injectMainstreamAds(INTEGRATIONS_CLASS_DESCRIPTOR)
return PatchResultSuccess()
}

View File

@ -1,30 +0,0 @@
package app.revanced.patches.music.audio.codecs.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility(
[Package(
"com.google.android.apps.youtube.music",
arrayOf(
"5.14.53",
"5.16.51",
"5.17.51",
"5.21.52",
"5.22.54",
"5.23.50",
"5.25.51",
"5.25.52",
"5.26.52",
"5.27.51",
"5.28.52",
"5.29.52",
"5.31.50",
"5.34.51",
"5.36.51"
)
)]
)
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class CodecsUnlockCompatibility

View File

@ -6,7 +6,6 @@ import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
object AllCodecsReferenceFingerprint : MethodFingerprint(
"J", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf("L"), listOf(

View File

@ -1,13 +1,18 @@
package app.revanced.patches.music.audio.codecs.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.shared.annotation.YouTubeMusicCompatibility
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@Name("codec-lock-fingerprint")
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
@YouTubeMusicCompatibility
@Version("0.0.1")
object CodecsLockFingerprint : MethodFingerprint(
"L", AccessFlags.PUBLIC or AccessFlags.STATIC, opcodes = listOf(
Opcode.INVOKE_DIRECT,

View File

@ -5,20 +5,26 @@ import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.data.toMethodWalker
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.instruction
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.util.smali.toInstruction
import app.revanced.patches.music.audio.codecs.annotations.CodecsUnlockCompatibility
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.music.audio.codecs.fingerprints.AllCodecsReferenceFingerprint
import app.revanced.patches.music.audio.codecs.fingerprints.CodecsLockFingerprint
import app.revanced.patches.music.misc.integrations.patch.MusicIntegrationsPatch
import app.revanced.patches.music.misc.settings.patch.MusicSettingsPatch
import app.revanced.shared.annotation.YouTubeMusicCompatibility
import org.jf.dexlib2.Opcode
@Patch
@DependsOn([MusicIntegrationsPatch::class, MusicSettingsPatch::class])
@Name("codecs-unlock")
@Description("Adds more audio codec options. The new audio codecs usually result in better audio quality.")
@CodecsUnlockCompatibility
@YouTubeMusicCompatibility
@Version("0.0.1")
class CodecsUnlockPatch : BytecodePatch(
listOf(
@ -27,8 +33,9 @@ class CodecsUnlockPatch : BytecodePatch(
) {
override fun execute(context: BytecodeContext): PatchResult {
val codecsLockResult = CodecsLockFingerprint.result!!
val codecsLockMethod = codecsLockResult.mutableMethod
val implementation = codecsLockResult.mutableMethod.implementation!!
val implementation = codecsLockMethod.implementation!!
val scanResultStartIndex = codecsLockResult.scanResult.patternScanResult!!.startIndex
val instructionIndex = scanResultStartIndex +
@ -46,9 +53,14 @@ class CodecsUnlockPatch : BytecodePatch(
.nextMethod(allCodecsResult.scanResult.patternScanResult!!.startIndex)
.getMethod()
implementation.replaceInstruction(
instructionIndex,
"invoke-static {}, ${allCodecsMethod.definingClass}->${allCodecsMethod.name}()Ljava/util/Set;".toInstruction()
codecsLockMethod.addInstructions(
instructionIndex + 2, """
invoke-static {}, Lapp/revanced/integrations/settings/MusicSettings;->getCodecsUnlock()Z
move-result v7
if-eqz v7, :mp4a
invoke-static {}, ${allCodecsMethod.definingClass}->${allCodecsMethod.name}()Ljava/util/Set;
move-result-object p4
""", listOf(ExternalLabel("mp4a", codecsLockMethod.instruction(instructionIndex + 2)))
)
return PatchResultSuccess()

View File

@ -1,30 +0,0 @@
package app.revanced.patches.music.audio.exclusiveaudio.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility(
[Package(
"com.google.android.apps.youtube.music",
arrayOf(
"5.14.53",
"5.16.51",
"5.17.51",
"5.21.52",
"5.22.54",
"5.23.50",
"5.25.51",
"5.25.52",
"5.26.52",
"5.27.51",
"5.28.52",
"5.29.52",
"5.31.50",
"5.34.51",
"5.36.51"
)
)]
)
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class ExclusiveAudioCompatibility

View File

@ -6,7 +6,6 @@ import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
object ExclusiveAudioFingerprint : MethodFingerprint(
"V",

View File

@ -6,17 +6,17 @@ import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstruction
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.music.audio.exclusiveaudio.annotations.ExclusiveAudioCompatibility
import app.revanced.patches.music.audio.exclusiveaudio.fingerprints.AudioOnlyEnablerFingerprint
import app.revanced.shared.annotation.YouTubeMusicCompatibility
@Patch
@Name("exclusive-audio-playback")
@Description("Enables the option to play music without video.")
@ExclusiveAudioCompatibility
@YouTubeMusicCompatibility
@Version("0.0.1")
class ExclusiveAudioPatch : BytecodePatch(
listOf(

View File

@ -0,0 +1,84 @@
package app.revanced.patches.music.layout.blacknavbar.bytecode.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.instruction
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.music.misc.integrations.patch.MusicIntegrationsPatch
import app.revanced.patches.music.misc.settings.patch.MusicSettingsPatch
import app.revanced.shared.annotation.YouTubeMusicCompatibility
import app.revanced.shared.extensions.findMutableMethodOf
import app.revanced.shared.patches.mapping.ResourceMappingPatch
import org.jf.dexlib2.iface.instruction.formats.Instruction11x
import org.jf.dexlib2.iface.instruction.formats.Instruction31i
import org.jf.dexlib2.Opcode
@Patch
@DependsOn(
[
MusicIntegrationsPatch::class,
MusicSettingsPatch::class,
ResourceMappingPatch::class
]
)
@Name("black-navbar")
@Description("Sets the navigation bar color to black.")
@YouTubeMusicCompatibility
@Version("0.0.1")
class BlackNavbarPatch : BytecodePatch() {
// list of resource names to get the id of
private val resourceIds = arrayOf(
"ytm_color_grey_12"
).map { name ->
ResourceMappingPatch.resourceMappings.single { it.name == name }.id
}
override fun execute(context: BytecodeContext): PatchResult {
context.classes.forEach { classDef ->
classDef.methods.forEach { method ->
with(method.implementation) {
this?.instructions?.forEachIndexed { index, instruction ->
when (instruction.opcode) {
Opcode.CONST -> {
when ((instruction as Instruction31i).wideLiteral) {
resourceIds[0] -> { // blacknavbar
val insertIndex = index - 1
val insertIndex2 = index + 2
val invokeInstruction = instructions.elementAt(insertIndex)
val invokeInstruction2 = instructions.elementAt(insertIndex2)
if (invokeInstruction.opcode != Opcode.MOVE_RESULT_OBJECT) return@forEachIndexed
val register1 = (instructions.elementAt(index) as Instruction31i).registerA
val register2 = (invokeInstruction2 as Instruction11x).registerA
val mutableMethod = context.proxy(classDef).mutableClass.findMutableMethodOf(method)
mutableMethod.addInstructions(
index + 3, """
invoke-static {}, Lapp/revanced/integrations/settings/MusicSettings;->getBlackNavbar()Z
move-result v$register1
if-eqz v$register1, :default
const/high16 v$register2, -0x1000000
""", listOf(ExternalLabel("default", mutableMethod.instruction(index + 3)))
)
}
}
}
else -> return@forEachIndexed
}
}
}
}
}
return PatchResultSuccess()
}
}

View File

@ -0,0 +1,111 @@
package app.revanced.patches.music.layout.branding.icon.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.*
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.shared.annotation.YouTubeMusicCompatibility
import java.nio.file.Files
import java.nio.file.StandardCopyOption
@Patch
@Name("custom-branding-music-red")
@Description("Changes the YouTube Music launcher icon to your choice (defaults to ReVanced Red).")
@YouTubeMusicCompatibility
@Version("0.0.1")
class CustomBrandingMusicPatch_Red : ResourcePatch {
override fun execute(context: ResourceContext): PatchResult {
val classLoader = this.javaClass.classLoader
val resDirectory = context["res"]
if (!resDirectory.isDirectory) return PatchResultError("The res folder can not be found.")
// App Icon
val AppiconNames = arrayOf(
"adaptiveproduct_youtube_music_background_color_108",
"adaptiveproduct_youtube_music_foreground_color_108",
"ic_launcher_release"
)
mapOf(
"xxxhdpi" to 192,
"xxhdpi" to 144,
"xhdpi" to 96,
"hdpi" to 72,
"mdpi" to 48
).forEach { (iconDirectory, size) ->
AppiconNames.forEach iconLoop@{ iconName ->
Files.copy(
classLoader.getResourceAsStream("music/branding/red/launchericon/$size/$iconName.png")!!,
resDirectory.resolve("mipmap-$iconDirectory").resolve("$iconName.png").toPath(),
StandardCopyOption.REPLACE_EXISTING
)
}
}
// Other Resource
val drawables1 = "drawable-hdpi" to arrayOf(
"action_bar_logo",
"action_bar_logo_release",
"record"
)
val drawables2 = "drawable-large-hdpi" to arrayOf(
"record"
)
val drawables3 = "drawable-large-mdpi" to arrayOf(
"record"
)
val drawables4 = "drawable-large-xhdpi" to arrayOf(
"record"
)
val drawables5 = "drawable-mdpi" to arrayOf(
"action_bar_logo",
"record"
)
val drawables6 = "drawable-xhdpi" to arrayOf(
"action_bar_logo",
"record"
)
val drawables7 = "drawable-xlarge-hdpi" to arrayOf(
"record"
)
val drawables8 = "drawable-xlarge-mdpi" to arrayOf(
"record"
)
val drawables9 = "drawable-xxhdpi" to arrayOf(
"action_bar_logo",
"record"
)
val drawables10 = "drawable-xxxhdpi" to arrayOf(
"action_bar_logo"
)
val pngResources = arrayOf(drawables1, drawables2, drawables3, drawables4, drawables5, drawables6, drawables7, drawables8, drawables9, drawables10)
pngResources.forEach { (path, resourceNames) ->
resourceNames.forEach { name ->
val relativePath = "$path/$name.png"
Files.copy(
classLoader.getResourceAsStream("music/branding/red/resource/$relativePath")!!,
context["res"].resolve(relativePath).toPath(),
StandardCopyOption.REPLACE_EXISTING
)
}
}
return PatchResultSuccess()
}
}

View File

@ -0,0 +1,64 @@
package app.revanced.patches.music.layout.branding.icon.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.*
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.shared.annotation.YouTubeMusicCompatibility
import java.nio.file.Files
import java.nio.file.StandardCopyOption
@Patch(false)
@Name("custom-branding-music-revancify")
@Description("Changes the YouTube Music launcher icon to your choice (Revancify).")
@YouTubeMusicCompatibility
@Version("0.0.1")
class CustomBrandingMusicPatch_Revancify : ResourcePatch {
override fun execute(context: ResourceContext): PatchResult {
val classLoader = this.javaClass.classLoader
val resDirectory = context["res"]
if (!resDirectory.isDirectory) return PatchResultError("The res folder can not be found.")
// App Icon
val AppiconNames = arrayOf(
"adaptiveproduct_youtube_music_background_color_108",
"adaptiveproduct_youtube_music_foreground_color_108",
"ic_launcher_release"
)
mapOf(
"xxxhdpi" to 192,
"xxhdpi" to 144,
"xhdpi" to 96,
"hdpi" to 72,
"mdpi" to 48
).forEach { (iconDirectory, size) ->
AppiconNames.forEach iconLoop@{ iconName ->
Files.copy(
classLoader.getResourceAsStream("music/branding/revancify/launchericon/$size/$iconName.png")!!,
resDirectory.resolve("mipmap-$iconDirectory").resolve("$iconName.png").toPath(),
StandardCopyOption.REPLACE_EXISTING
)
}
}
// MonoChrome Icon
arrayOf("drawable" to arrayOf("ic_app_icons_themed_youtube_music")).forEach { (path, resourceNames) ->
resourceNames.forEach { name ->
val relativePath = "$path/$name.xml"
Files.copy(
classLoader.getResourceAsStream("music/branding/revancify/monochromeicon/$relativePath")!!,
context["res"].resolve(relativePath).toPath(),
StandardCopyOption.REPLACE_EXISTING
)
}
}
return PatchResultSuccess()
}
}

View File

@ -0,0 +1,15 @@
package app.revanced.patches.music.layout.castbutton.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.shared.annotation.YouTubeMusicCompatibility
import org.jf.dexlib2.AccessFlags
@Name("hide-castbutton-signature")
@YouTubeMusicCompatibility
@Version("0.0.1")
object HideCastButtonFingerprint : MethodFingerprint (
"V", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf("I"), null ,null, null
)

View File

@ -0,0 +1,16 @@
package app.revanced.patches.music.layout.castbutton.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.shared.annotation.YouTubeMusicCompatibility
import org.jf.dexlib2.AccessFlags
@Name("hide-castbutton-parent-signature")
@YouTubeMusicCompatibility
@Version("0.0.1")
object HideCastButtonParentFingerprint : MethodFingerprint (
"Z", AccessFlags.PRIVATE or AccessFlags.FINAL,
strings = listOf("MediaRouteButton")
)

View File

@ -0,0 +1,46 @@
package app.revanced.patches.music.layout.castbutton.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultError
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patches.music.layout.castbutton.fingerprints.HideCastButtonFingerprint
import app.revanced.patches.music.layout.castbutton.fingerprints.HideCastButtonParentFingerprint
import app.revanced.patches.music.misc.integrations.patch.MusicIntegrationsPatch
import app.revanced.patches.music.misc.settings.patch.MusicSettingsPatch
import app.revanced.shared.annotation.YouTubeMusicCompatibility
@Patch
@DependsOn([MusicIntegrationsPatch::class, MusicSettingsPatch::class])
@Name("hide-music-cast-button")
@Description("Hides the cast button in the video player and header")
@YouTubeMusicCompatibility
@Version("0.0.1")
class HideWatermarkPatch : BytecodePatch(
listOf(
HideCastButtonParentFingerprint
)
) {
override fun execute(context: BytecodeContext): PatchResult {
HideCastButtonFingerprint.resolve(context, HideCastButtonParentFingerprint.result!!.classDef)
val result = HideCastButtonFingerprint.result
?: return PatchResultError("Required parent method could not be found.")
result.mutableMethod.addInstructions(
0, """
invoke-static {p1}, Lapp/revanced/integrations/settings/MusicSettings;->getCastButtonOverrideV2(I)I
move-result p1
"""
)
return PatchResultSuccess()
}
}

View File

@ -1,29 +0,0 @@
package app.revanced.patches.music.layout.compactheader.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility(
[Package(
"com.google.android.apps.youtube.music",
arrayOf(
"5.14.53",
"5.16.51",
"5.21.52",
"5.22.54",
"5.23.50",
"5.25.51",
"5.25.52",
"5.26.52",
"5.27.51",
"5.28.52",
"5.29.52",
"5.31.50",
"5.34.51",
"5.36.51"
)
)]
)
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class CompactHeaderCompatibility

View File

@ -1,10 +1,16 @@
package app.revanced.patches.music.layout.compactheader.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.shared.annotation.YouTubeMusicCompatibility
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@Name("compact-header-constructor-fingerprint")
@YouTubeMusicCompatibility
@Version("0.0.1")
object CompactHeaderConstructorFingerprint : MethodFingerprint(
"V", AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, listOf("L", "L", "L", "L", "L"), listOf(
Opcode.INVOKE_DIRECT,

View File

@ -5,18 +5,22 @@ import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.music.layout.compactheader.annotations.CompactHeaderCompatibility
import app.revanced.patches.music.layout.compactheader.fingerprints.CompactHeaderConstructorFingerprint
import app.revanced.patches.music.misc.integrations.patch.MusicIntegrationsPatch
import app.revanced.patches.music.misc.settings.patch.MusicSettingsPatch
import app.revanced.shared.annotation.YouTubeMusicCompatibility
import org.jf.dexlib2.builder.instruction.BuilderInstruction11x
@Patch(false)
@Patch
@DependsOn([MusicIntegrationsPatch::class, MusicSettingsPatch::class])
@Name("compact-header")
@Description("Hides the music category bar at the top of the homepage.")
@CompactHeaderCompatibility
@YouTubeMusicCompatibility
@Version("0.0.1")
class CompactHeaderPatch : BytecodePatch(
listOf(
@ -31,7 +35,8 @@ class CompactHeaderPatch : BytecodePatch(
val register = (method.implementation!!.instructions[insertIndex - 1] as BuilderInstruction11x).registerA
method.addInstructions(
insertIndex, """
const/16 v2, 0x8
invoke-static {}, Lapp/revanced/integrations/settings/MusicSettings;->getCompactHeader()I
move-result v2
invoke-virtual {v${register}, v2}, Landroid/view/View;->setVisibility(I)V
"""
)

View File

@ -1,30 +0,0 @@
package app.revanced.patches.music.layout.minimizedplayback.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility(
[Package(
"com.google.android.apps.youtube.music",
arrayOf(
"5.14.53",
"5.16.51",
"5.17.51",
"5.21.52",
"5.22.54",
"5.23.50",
"5.25.51",
"5.25.52",
"5.26.52",
"5.27.51",
"5.28.52",
"5.29.52",
"5.31.50",
"5.34.51",
"5.36.51"
)
)]
)
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class MinimizedPlaybackCompatibility

View File

@ -1,10 +1,16 @@
package app.revanced.patches.music.layout.minimizedplayback.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.shared.annotation.YouTubeMusicCompatibility
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@Name("minimized-playback-manager-fingerprint")
@YouTubeMusicCompatibility
@Version("0.0.1")
object MinimizedPlaybackManagerFingerprint : MethodFingerprint(
"V",
AccessFlags.PUBLIC or AccessFlags.FINAL,

View File

@ -4,18 +4,22 @@ import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.data.toMethodWalker
import app.revanced.patcher.extensions.addInstruction
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.music.layout.minimizedplayback.annotations.MinimizedPlaybackCompatibility
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.music.layout.minimizedplayback.fingerprints.MinimizedPlaybackManagerFingerprint
import app.revanced.shared.annotation.YouTubeMusicCompatibility
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
import org.jf.dexlib2.iface.reference.MethodReference
@Patch
@Name("minimized-playback-music")
@Description("Enables minimized playback on Kids music.")
@MinimizedPlaybackCompatibility
@YouTubeMusicCompatibility
@Version("0.0.1")
class MinimizedPlaybackPatch : BytecodePatch(
listOf(
@ -23,10 +27,8 @@ class MinimizedPlaybackPatch : BytecodePatch(
)
) {
override fun execute(context: BytecodeContext): PatchResult {
MinimizedPlaybackManagerFingerprint.result!!.mutableMethod.addInstructions(
0, """
return-void
"""
MinimizedPlaybackManagerFingerprint.result!!.mutableMethod.addInstruction(
0, "return-void"
)
return PatchResultSuccess()

View File

@ -0,0 +1,28 @@
package app.revanced.patches.music.layout.minimizedplayer.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.shared.annotation.YouTubeCompatibility
import app.revanced.shared.annotation.YouTubeMusicCompatibility
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@Name("minimized-player-fingerprint")
@YouTubeMusicCompatibility
@Version("0.0.1")
object MinimizedPlayerFingerprint : MethodFingerprint(
"V", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf("L", "L"), listOf(
Opcode.IGET_OBJECT,
Opcode.IGET_OBJECT,
Opcode.SGET_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.CONST_4,
Opcode.IF_NEZ,
Opcode.SGET_OBJECT,
Opcode.INVOKE_VIRTUAL
),
strings = listOf("w_st")
)

View File

@ -0,0 +1,51 @@
package app.revanced.patches.music.layout.minimizedplayer.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultError
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.music.layout.minimizedplayer.fingerprints.MinimizedPlayerFingerprint
import app.revanced.patches.music.misc.integrations.patch.MusicIntegrationsPatch
import app.revanced.patches.music.misc.settings.patch.MusicSettingsPatch
import app.revanced.shared.annotation.YouTubeMusicCompatibility
import org.jf.dexlib2.iface.instruction.Instruction
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
@Patch
@DependsOn([MusicIntegrationsPatch::class, MusicSettingsPatch::class])
@Name("minimized-player")
@Description("Permanently keep player minimized even if another track is played.")
@YouTubeMusicCompatibility
@Version("0.0.1")
class MinimizedPlayerPatch : BytecodePatch(
listOf(
MinimizedPlayerFingerprint
)
) {
override fun execute(context: BytecodeContext): PatchResult {
val result = MinimizedPlayerFingerprint.result!!
val method = result.mutableMethod
val index = result.scanResult.patternScanResult!!.endIndex
val register = (method.implementation!!.instructions[index - 1] as OneRegisterInstruction).registerA - 1
val jumpInstruction = method.implementation!!.instructions[index + 1] as Instruction
method.addInstructions(
index, """
invoke-static {}, Lapp/revanced/integrations/settings/MusicSettings;->getEnforceMinimizedPlayer()Z
move-result v$register
if-nez v$register, :enforce
""", listOf(ExternalLabel("enforce", jumpInstruction))
)
return PatchResultSuccess()
}
}

View File

@ -0,0 +1,20 @@
package app.revanced.patches.music.layout.miniplayercolor.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.shared.annotation.YouTubeMusicCompatibility
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@Name("miniplayer-color-fingerprint")
@YouTubeMusicCompatibility
@Version("0.0.1")
object MiniplayerColorFingerprint : MethodFingerprint(
"V", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf("L", "J"), listOf(
Opcode.INVOKE_DIRECT,
Opcode.IPUT_OBJECT,
Opcode.RETURN_VOID
)
)

View File

@ -0,0 +1,33 @@
package app.revanced.patches.music.layout.miniplayercolor.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.shared.annotation.YouTubeMusicCompatibility
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@Name("miniplayer-color-hook-fingerprint")
@YouTubeMusicCompatibility
@Version("0.0.1")
object MiniplayerColorParentFingerprint : MethodFingerprint(
"V",
AccessFlags.PRIVATE or AccessFlags.FINAL,
null,
listOf(
Opcode.IGET,
Opcode.IGET,
Opcode.CONST_WIDE_16,
Opcode.IF_EQ,
Opcode.IPUT,
Opcode.IGET_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.IGET,
Opcode.IGET,
Opcode.IF_EQ,
Opcode.IPUT,
Opcode.IGET_OBJECT,
Opcode.INVOKE_VIRTUAL
)
)

View File

@ -0,0 +1,99 @@
package app.revanced.patches.music.layout.miniplayercolor.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.removeInstruction
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.music.layout.miniplayercolor.fingerprints.MiniplayerColorFingerprint
import app.revanced.patches.music.layout.miniplayercolor.fingerprints.MiniplayerColorParentFingerprint
import app.revanced.patches.music.misc.integrations.patch.MusicIntegrationsPatch
import app.revanced.patches.music.misc.settings.patch.MusicSettingsPatch
import app.revanced.shared.annotation.YouTubeMusicCompatibility
import org.jf.dexlib2.iface.instruction.Instruction
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
import org.jf.dexlib2.iface.reference.FieldReference
import org.jf.dexlib2.iface.reference.MethodReference
@Patch
@DependsOn([MusicIntegrationsPatch::class, MusicSettingsPatch::class])
@Name("miniplayer-color")
@Description("Matches the fullscreen player color with the minimized one.")
@YouTubeMusicCompatibility
@Version("0.0.1")
class MiniplayerColorPatch : BytecodePatch(
listOf(
MiniplayerColorFingerprint, MiniplayerColorParentFingerprint
)
) {
override fun execute(context: BytecodeContext): PatchResult {
val miniplayerColorParentResult = MiniplayerColorParentFingerprint.result!!
val miniplayerColorParentMethod = miniplayerColorParentResult.mutableMethod
MiniplayerColorFingerprint.resolve(context, miniplayerColorParentResult.classDef)
val miniplayerColorResult = MiniplayerColorFingerprint.result!!
val miniplayerColorMethod = miniplayerColorResult.mutableMethod
val insertIndex = miniplayerColorResult.scanResult.patternScanResult!!.startIndex + 1
val jumpInstruction = miniplayerColorMethod.implementation!!.instructions[insertIndex] as Instruction
val Reference_A_1 =
miniplayerColorParentMethod.let { method ->
(method.implementation!!.instructions.elementAt(4) as ReferenceInstruction).reference as FieldReference
}
val Reference_A_2 =
miniplayerColorParentMethod.let { method ->
(method.implementation!!.instructions.elementAt(5) as ReferenceInstruction).reference as FieldReference
}
val Reference_A_3 =
miniplayerColorParentMethod.let { method ->
(method.implementation!!.instructions.elementAt(6) as ReferenceInstruction).reference as MethodReference
}
val Reference_B_1 =
miniplayerColorParentMethod.let { method ->
(method.implementation!!.instructions.elementAt(10) as ReferenceInstruction).reference as FieldReference
}
val Reference_B_2 =
miniplayerColorParentMethod.let { method ->
(method.implementation!!.instructions.elementAt(11) as ReferenceInstruction).reference as FieldReference
}
val Reference_B_3 =
miniplayerColorParentMethod.let { method ->
(method.implementation!!.instructions.elementAt(12) as ReferenceInstruction).reference as MethodReference
}
miniplayerColorMethod.addInstructions(
insertIndex, """
invoke-static {}, Lapp/revanced/integrations/settings/MusicSettings;->getMiniPlayerColor()Z
move-result v2
if-eqz v2, :off
iget v0, p0, ${miniplayerColorResult.classDef.type}->${Reference_A_1.name}:${Reference_A_1.type}
if-eq v0, v2, :abswitch
iput v2, p0, ${miniplayerColorResult.classDef.type}->${Reference_A_1.name}:${Reference_A_1.type}
iget-object v0, p0, ${miniplayerColorResult.classDef.type}->${Reference_A_2.name}:${Reference_A_2.type}
invoke-virtual {v0, v2, p2, p3}, $Reference_A_3
:abswitch
iget v0, p0, ${miniplayerColorResult.classDef.type}->${Reference_B_1.name}:${Reference_B_1.type}
if-eq v0, v1, :exit
iput v1, p0, ${miniplayerColorResult.classDef.type}->${Reference_B_1.name}:${Reference_B_1.type}
iget-object v0, p0, ${miniplayerColorResult.classDef.type}->${Reference_B_2.name}:${Reference_B_2.type}
invoke-virtual {v0, v1, p2, p3}, $Reference_B_3
goto :exit
:off
invoke-direct {p0}, ${miniplayerColorResult.classDef.type}->${miniplayerColorParentResult.mutableMethod.name}()V
""", listOf(ExternalLabel("exit", jumpInstruction))
)
miniplayerColorMethod.removeInstruction(insertIndex - 1)
return PatchResultSuccess()
}
}

View File

@ -1,30 +0,0 @@
package app.revanced.patches.music.layout.premium.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility(
[Package(
"com.google.android.apps.youtube.music",
arrayOf(
"5.14.53",
"5.16.51",
"5.17.51",
"5.21.52",
"5.22.54",
"5.23.50",
"5.25.51",
"5.25.52",
"5.26.52",
"5.27.51",
"5.28.52",
"5.29.52",
"5.31.50",
"5.34.51",
"5.36.51"
)
)]
)
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class HideGetPremiumCompatibility

View File

@ -1,10 +1,16 @@
package app.revanced.patches.music.layout.premium.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.shared.annotation.YouTubeMusicCompatibility
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@Name("hide-get-premium-fingerprint")
@YouTubeMusicCompatibility
@Version("0.0.1")
object HideGetPremiumFingerprint : MethodFingerprint(
"V", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf(), listOf(
Opcode.IF_NEZ,

View File

@ -1,10 +1,16 @@
package app.revanced.patches.music.layout.premium.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.shared.annotation.YouTubeMusicCompatibility
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@Name("hide-get-premium-parent-fingerprint")
@YouTubeMusicCompatibility
@Version("0.0.1")
object HideGetPremiumParentFingerprint : MethodFingerprint(
"V", AccessFlags.PUBLIC or AccessFlags.FINAL, listOf(), listOf(
Opcode.IGET_BOOLEAN,

View File

@ -7,18 +7,18 @@ import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.replaceInstruction
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.music.layout.premium.annotations.HideGetPremiumCompatibility
import app.revanced.patches.music.layout.premium.fingerprints.HideGetPremiumFingerprint
import app.revanced.patches.music.layout.premium.fingerprints.HideGetPremiumParentFingerprint
import app.revanced.shared.annotation.YouTubeMusicCompatibility
@Patch
@Name("hide-get-premium")
@Description("Removes all \"Get Premium\" evidences from the avatar menu.")
@HideGetPremiumCompatibility
@YouTubeMusicCompatibility
@Version("0.0.1")
class HideGetPremiumPatch : BytecodePatch(
listOf(

View File

@ -0,0 +1,75 @@
package app.revanced.patches.music.layout.tabletmode.bytecode.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.instruction
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patches.music.misc.integrations.patch.MusicIntegrationsPatch
import app.revanced.patches.music.misc.settings.patch.MusicSettingsPatch
import app.revanced.shared.annotation.YouTubeMusicCompatibility
import app.revanced.shared.extensions.findMutableMethodOf
import app.revanced.shared.patches.mapping.ResourceMappingPatch
import org.jf.dexlib2.iface.instruction.formats.Instruction31i
import org.jf.dexlib2.Opcode
@Patch
@DependsOn(
[
MusicIntegrationsPatch::class,
MusicSettingsPatch::class,
ResourceMappingPatch::class
]
)
@Name("tablet-mode")
@Description("Unlocks landscape mode.")
@YouTubeMusicCompatibility
@Version("0.0.1")
class TabletModePatch : BytecodePatch() {
// list of resource names to get the id of
private val resourceIds = arrayOf(
"is_tablet"
).map { name ->
ResourceMappingPatch.resourceMappings.single { it.name == name }.id
}
override fun execute(context: BytecodeContext): PatchResult {
context.classes.forEach { classDef ->
classDef.methods.forEach { method ->
with(method.implementation) {
this?.instructions?.forEachIndexed { index, instruction ->
when (instruction.opcode) {
Opcode.CONST -> {
when ((instruction as Instruction31i).wideLiteral) {
resourceIds[0] -> { // tablet
val insertIndex = index - 2
val invokeInstruction = instructions.elementAt(insertIndex)
if (invokeInstruction.opcode != Opcode.INVOKE_VIRTUAL) return@forEachIndexed
val mutableMethod = context.proxy(classDef).mutableClass.findMutableMethodOf(method)
mutableMethod.addInstructions(
index + 3, """
invoke-static {p0}, Lapp/revanced/integrations/settings/MusicSettings;->getTabletMode(Z)Z
move-result p0
"""
)
}
}
}
else -> return@forEachIndexed
}
}
}
}
}
return PatchResultSuccess()
}
}

View File

@ -1,32 +0,0 @@
package app.revanced.patches.music.layout.tastebuilder.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
/**
* -- Note 2022-08-05 --
* Since 5.17.xx the tastebuilder component is dismissible, so this patch is less useful
* also it is partly litho now
*/
@Compatibility(
[Package(
"com.google.android.apps.youtube.music",
arrayOf(
"5.21.52",
"5.22.54",
"5.23.50",
"5.25.51",
"5.25.52",
"5.26.52",
"5.27.51",
"5.28.52",
"5.29.52",
"5.31.50",
"5.34.51",
"5.36.51"
)
)]
)
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class RemoveTasteBuilderCompatibility

View File

@ -1,13 +1,18 @@
package app.revanced.patches.music.layout.tastebuilder.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.shared.annotation.YouTubeMusicCompatibility
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@Name("taste-builder-constructor-fingerprint")
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
@YouTubeMusicCompatibility
@Version("0.0.1")
object TasteBuilderConstructorFingerprint : MethodFingerprint(
"V", AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, listOf("L", "L", "L"), listOf(
Opcode.INVOKE_DIRECT,

View File

@ -5,18 +5,18 @@ import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.music.layout.tastebuilder.annotations.RemoveTasteBuilderCompatibility
import app.revanced.patches.music.layout.tastebuilder.fingerprints.TasteBuilderConstructorFingerprint
import app.revanced.shared.annotation.YouTubeMusicCompatibility
import org.jf.dexlib2.iface.instruction.formats.Instruction22c
@Patch
@Name("tasteBuilder-remover")
@Description("Removes the \"Tell us which artists you like\" card from the home screen.")
@RemoveTasteBuilderCompatibility
@YouTubeMusicCompatibility
@Version("0.0.1")
class RemoveTasteBuilderPatch : BytecodePatch(
listOf(

View File

@ -1,30 +0,0 @@
package app.revanced.patches.music.layout.upgradebutton.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility(
[Package(
"com.google.android.apps.youtube.music",
arrayOf(
"5.14.53",
"5.16.51",
"5.17.51",
"5.21.52",
"5.22.54",
"5.23.50",
"5.25.51",
"5.25.52",
"5.26.52",
"5.27.51",
"5.28.52",
"5.29.52",
"5.31.50",
"5.34.51",
"5.36.51"
)
)]
)
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class RemoveUpgradeButtonCompatibility

View File

@ -1,13 +1,18 @@
package app.revanced.patches.music.layout.upgradebutton.fingerprints
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.shared.annotation.YouTubeMusicCompatibility
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@Name("pivot-bar-constructor-fingerprint")
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
@YouTubeMusicCompatibility
@Version("0.0.1")
object PivotBarConstructorFingerprint : MethodFingerprint(
"V",
AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,

View File

@ -5,23 +5,23 @@ import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.util.smali.toInstructions
import app.revanced.patches.music.layout.upgradebutton.annotations.RemoveUpgradeButtonCompatibility
import app.revanced.patches.music.layout.upgradebutton.fingerprints.PivotBarConstructorFingerprint
import org.jf.dexlib2.Opcode
import app.revanced.shared.annotation.YouTubeMusicCompatibility
import org.jf.dexlib2.builder.instruction.BuilderInstruction22t
import org.jf.dexlib2.iface.instruction.formats.Instruction22c
import org.jf.dexlib2.iface.instruction.formats.Instruction35c
import org.jf.dexlib2.Opcode
@Patch
@Name("upgrade-button-remover")
@Description("Removes the upgrade tab from the pivot bar.")
@RemoveUpgradeButtonCompatibility
@YouTubeMusicCompatibility
@Version("0.0.1")
class RemoveUpgradeButtonPatch : BytecodePatch(
listOf(

View File

@ -0,0 +1,10 @@
package app.revanced.patches.music.misc.clientspoof.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.Opcode
object UserAgentHeaderBuilderFingerprint : MethodFingerprint(
parameters = listOf("L"),
opcodes = listOf(Opcode.MOVE_RESULT_OBJECT, Opcode.INVOKE_VIRTUAL, Opcode.CONST_16),
strings = listOf("(Linux; U; Android ")
)

View File

@ -1,4 +1,4 @@
package app.revanced.patches.youtube.misc.fix.spoof.patch
package app.revanced.patches.music.misc.clientspoof.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
@ -6,30 +6,31 @@ import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstruction
import app.revanced.patcher.extensions.instruction
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.youtube.misc.fix.spoof.annotations.ClientSpoofCompatibility
import app.revanced.patches.youtube.misc.fix.spoof.fingerprints.UserAgentHeaderBuilderFingerprint
import app.revanced.patches.music.misc.clientspoof.fingerprints.UserAgentHeaderBuilderFingerprint
import app.revanced.shared.annotation.YouTubeMusicCompatibility
import org.jf.dexlib2.iface.instruction.FiveRegisterInstruction
@Patch
@Name("client-spoof")
@Description("Spoofs the YouTube or Vanced client to prevent playback issues.")
@ClientSpoofCompatibility
@Name("client-spoof-music")
@Description("Spoofs the YouTube Music client.")
@YouTubeMusicCompatibility
@Version("0.0.1")
class ClientSpoofPatch : BytecodePatch(
class ClientSpoofMusicPatch : BytecodePatch(
listOf(UserAgentHeaderBuilderFingerprint)
) {
override fun execute(context: BytecodeContext): PatchResult {
val result = UserAgentHeaderBuilderFingerprint.result!!
val method = result.mutableMethod
val insertIndex = result.scanResult.patternScanResult!!.endIndex
val insertIndex = result.scanResult.patternScanResult!!.endIndex - 1
val packageNameRegister = (method.instruction(insertIndex) as FiveRegisterInstruction).registerD
val originalPackageName = "com.google.android.youtube"
val originalPackageName = "com.google.android.apps.youtube.music"
method.addInstruction(insertIndex, "const-string v$packageNameRegister, \"$originalPackageName\"")
return PatchResultSuccess()

View File

@ -0,0 +1,7 @@
package app.revanced.patches.music.misc.integrations.fingerprints
import app.revanced.shared.patches.integrations.AbstractIntegrationsPatch.IntegrationsFingerprint
object InitFingerprint : IntegrationsFingerprint(
strings = listOf("YouTubeMusic", "activity")
)

View File

@ -0,0 +1,13 @@
package app.revanced.patches.music.misc.integrations.patch
import app.revanced.patcher.annotation.Name
import app.revanced.patches.music.misc.integrations.fingerprints.InitFingerprint
import app.revanced.shared.annotation.YouTubeMusicCompatibility
import app.revanced.shared.patches.integrations.AbstractIntegrationsPatch
@Name("music-integrations")
@YouTubeMusicCompatibility
class MusicIntegrationsPatch : AbstractIntegrationsPatch(
"Lapp/revanced/integrations/settings/MusicSettings;",
listOf(InitFingerprint),
)

View File

@ -1,30 +0,0 @@
package app.revanced.patches.music.misc.microg.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility(
[Package(
"com.google.android.apps.youtube.music",
arrayOf(
"5.14.53",
"5.16.51",
"5.17.51",
"5.21.52",
"5.22.54",
"5.23.50",
"5.25.51",
"5.25.52",
"5.26.52",
"5.27.51",
"5.28.52",
"5.29.52",
"5.31.50",
"5.34.51",
"5.36.51"
)
)]
)
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class MusicMicroGPatchCompatibility

View File

@ -1,5 +1,4 @@
package app.revanced.patches.music.misc.microg.fingerprints
package app.revanced.patches.music.misc.microg.bytecode.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint

View File

@ -1,5 +1,4 @@
package app.revanced.patches.music.misc.microg.fingerprints
package app.revanced.patches.music.misc.microg.bytecode.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint

View File

@ -1,5 +1,4 @@
package app.revanced.patches.youtube.misc.microg.fingerprints
package app.revanced.patches.music.misc.microg.bytecode.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint

View File

@ -1,18 +1,18 @@
package app.revanced.patches.music.misc.microg.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.AccessFlags
object GooglePlayUtilityFingerprint : MethodFingerprint(
"I",
AccessFlags.PUBLIC or AccessFlags.STATIC,
listOf("L", "I"),
strings = listOf(
"This should never happen.",
"MetadataValueReader",
"GooglePlayServicesUtil",
"com.android.vending",
"android.hardware.type.embedded"
)
package app.revanced.patches.music.misc.microg.bytecode.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.AccessFlags
object GooglePlayUtilityFingerprint : MethodFingerprint(
"I",
AccessFlags.PUBLIC or AccessFlags.STATIC,
listOf("L", "I"),
strings = listOf(
"This should never happen.",
"MetadataValueReader",
"GooglePlayServicesUtil",
"com.android.vending",
"android.hardware.type.embedded"
)
)

View File

@ -1,5 +1,4 @@
package app.revanced.patches.youtube.misc.microg.fingerprints
package app.revanced.patches.music.misc.microg.bytecode.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint

View File

@ -1,15 +1,14 @@
package app.revanced.patches.music.misc.microg.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.AccessFlags
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
object ServiceCheckFingerprint : MethodFingerprint(
"V",
AccessFlags.PUBLIC or AccessFlags.STATIC,
listOf("L", "I"),
strings = listOf("Google Play Services not available")
)
package app.revanced.patches.music.misc.microg.bytecode.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.method.annotation.FuzzyPatternScanMethod
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.AccessFlags
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
object ServiceCheckFingerprint : MethodFingerprint(
"V",
AccessFlags.PUBLIC or AccessFlags.STATIC,
listOf("L", "I"),
strings = listOf("Google Play Services not available")
)

View File

@ -1,65 +1,79 @@
package app.revanced.patches.music.misc.microg.patch.bytecode
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.music.misc.microg.annotations.MusicMicroGPatchCompatibility
import app.revanced.patches.music.misc.microg.fingerprints.*
import app.revanced.patches.music.misc.microg.patch.resource.MusicMicroGResourcePatch
import app.revanced.patches.music.misc.microg.shared.Constants.MUSIC_PACKAGE_NAME
import app.revanced.patches.music.misc.microg.shared.Constants.REVANCED_MUSIC_PACKAGE_NAME
import app.revanced.patches.youtube.misc.microg.shared.Constants
import app.revanced.util.microg.MicroGBytecodeHelper
@Patch
@DependsOn([MusicMicroGResourcePatch::class])
@Name("music-microg-support")
@Description("Allows YouTube Music ReVanced to run without root and under a different package name.")
@MusicMicroGPatchCompatibility
@Version("0.0.2")
class MusicMicroGBytecodePatch : BytecodePatch(
listOf(
ServiceCheckFingerprint,
GooglePlayUtilityFingerprint,
CastDynamiteModuleFingerprint,
CastDynamiteModuleV2Fingerprint,
CastContextFetchFingerprint,
PrimeFingerprint,
)
) {
// NOTE: the previous patch also replaced the following strings, but it seems like they are not needed:
// - "com.google.android.gms.chimera.GmsIntentOperationService",
// - "com.google.android.gms.phenotype.internal.IPhenotypeCallbacks",
// - "com.google.android.gms.phenotype.internal.IPhenotypeService",
// - "com.google.android.gms.phenotype.PACKAGE_NAME",
// - "com.google.android.gms.phenotype.UPDATE",
// - "com.google.android.gms.phenotype",
override fun execute(context: BytecodeContext) =
// apply common microG patch
MicroGBytecodeHelper.patchBytecode(
context,
arrayOf(
MicroGBytecodeHelper.packageNameTransform(
Constants.PACKAGE_NAME,
Constants.REVANCED_PACKAGE_NAME
)
),
MicroGBytecodeHelper.PrimeMethodTransformationData(
PrimeFingerprint,
MUSIC_PACKAGE_NAME,
REVANCED_MUSIC_PACKAGE_NAME
),
listOf(
ServiceCheckFingerprint,
GooglePlayUtilityFingerprint,
CastDynamiteModuleFingerprint,
CastDynamiteModuleV2Fingerprint,
CastContextFetchFingerprint
)
).let { PatchResultSuccess() }
}
package app.revanced.patches.music.misc.microg.bytecode.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patches.music.misc.clientspoof.patch.ClientSpoofMusicPatch
import app.revanced.patches.music.misc.microg.bytecode.fingerprints.*
import app.revanced.patches.music.misc.microg.resource.patch.MusicMicroGResourcePatch
import app.revanced.patches.music.misc.microg.shared.Constants.YOUTUBE_PACKAGE_NAME
import app.revanced.patches.music.misc.microg.shared.Constants.MUSIC_PACKAGE_NAME
import app.revanced.shared.annotation.YouTubeMusicCompatibility
import app.revanced.shared.patches.options.PatchOptions
import app.revanced.shared.util.microg.MicroGBytecodeHelper
@Patch
@DependsOn(
[
ClientSpoofMusicPatch::class,
MusicMicroGResourcePatch::class,
PatchOptions::class
]
)
@Name("music-microg-support")
@Description("Allows YouTube Music ReVanced to run without root and under a different package name.")
@YouTubeMusicCompatibility
@Version("0.0.2")
class MusicMicroGBytecodePatch : BytecodePatch(
listOf(
ServiceCheckFingerprint,
GooglePlayUtilityFingerprint,
CastDynamiteModuleFingerprint,
CastDynamiteModuleV2Fingerprint,
CastContextFetchFingerprint,
PrimeFingerprint,
)
) {
// NOTE: the previous patch also replaced the following strings, but it seems like they are not needed:
// - "com.google.android.gms.chimera.GmsIntentOperationService",
// - "com.google.android.gms.phenotype.internal.IPhenotypeCallbacks",
// - "com.google.android.gms.phenotype.internal.IPhenotypeService",
// - "com.google.android.gms.phenotype.PACKAGE_NAME",
// - "com.google.android.gms.phenotype.UPDATE",
// - "com.google.android.gms.phenotype",
override fun execute(context: BytecodeContext): PatchResult {
var YouTubePackageName = PatchOptions.YouTube_PackageName
var MusicPackageName = PatchOptions.Music_PackageName
// apply common microG patch
MicroGBytecodeHelper.patchBytecode(
context,
arrayOf(
MicroGBytecodeHelper.packageNameTransform(
YOUTUBE_PACKAGE_NAME,
"$YouTubePackageName"
)
),
MicroGBytecodeHelper.PrimeMethodTransformationData(
PrimeFingerprint,
MUSIC_PACKAGE_NAME,
"$MusicPackageName"
),
listOf(
ServiceCheckFingerprint,
GooglePlayUtilityFingerprint,
CastDynamiteModuleFingerprint,
CastDynamiteModuleV2Fingerprint,
CastContextFetchFingerprint
)
)
return PatchResultSuccess()
}
}

View File

@ -1,33 +1,39 @@
package app.revanced.patches.music.misc.microg.patch.resource
package app.revanced.patches.music.misc.microg.resource.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patches.music.misc.microg.annotations.MusicMicroGPatchCompatibility
import app.revanced.patches.music.misc.microg.shared.Constants.MUSIC_PACKAGE_NAME
import app.revanced.patches.music.misc.microg.shared.Constants.REVANCED_MUSIC_APP_NAME
import app.revanced.patches.music.misc.microg.shared.Constants.REVANCED_MUSIC_PACKAGE_NAME
import app.revanced.patches.music.misc.microg.shared.Constants.SPOOFED_PACKAGE_NAME
import app.revanced.patches.music.misc.microg.shared.Constants.SPOOFED_PACKAGE_SIGNATURE
import app.revanced.util.microg.MicroGManifestHelper
import app.revanced.util.microg.MicroGResourceHelper
import app.revanced.shared.annotation.YouTubeMusicCompatibility
import app.revanced.shared.patches.options.PatchOptions
import app.revanced.shared.util.microg.MicroGManifestHelper
import app.revanced.shared.util.microg.MicroGResourceHelper
@Name("music-microg-resource-patch")
@Description("Resource patch to allow YouTube Music ReVanced to run without root and under a different package name.")
@MusicMicroGPatchCompatibility
@DependsOn(
[
PatchOptions::class
]
)
@YouTubeMusicCompatibility
@Version("0.0.2")
class MusicMicroGResourcePatch : ResourcePatch {
override fun execute(context: ResourceContext): PatchResult {
var MusicPackageName = PatchOptions.Music_PackageName
// update manifest
MicroGResourceHelper.patchManifest(
context,
MUSIC_PACKAGE_NAME,
REVANCED_MUSIC_PACKAGE_NAME,
REVANCED_MUSIC_APP_NAME
"$MusicPackageName"
)
// add metadata to the manifest

View File

@ -1,9 +1,8 @@
package app.revanced.patches.music.misc.microg.shared
object Constants {
internal const val REVANCED_MUSIC_APP_NAME = "YT Music ReVanced"
internal const val REVANCED_MUSIC_PACKAGE_NAME = "app.revanced.android.apps.youtube.music"
internal const val YOUTUBE_PACKAGE_NAME = "com.google.android.youtube"
internal const val MUSIC_PACKAGE_NAME = "com.google.android.apps.youtube.music"
internal const val SPOOFED_PACKAGE_NAME = MUSIC_PACKAGE_NAME
internal const val SPOOFED_PACKAGE_SIGNATURE = "afb0fed5eeaebdd86f56a97742f4b6b33ef59875"
}
}

View File

@ -0,0 +1,33 @@
package app.revanced.patches.music.misc.optimizeresource.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.shared.annotation.YouTubeMusicCompatibility
import java.nio.file.Files
import java.nio.file.StandardCopyOption
@Patch
@Name("optimize-resource-music")
@Description("Remove unnecessary resources.")
@YouTubeMusicCompatibility
@Version("0.0.1")
class OptimizeResourcePatch : ResourcePatch {
override fun execute(context: ResourceContext): PatchResult {
val relativePath = "raw/third_party_licenses"
Files.copy(
this.javaClass.classLoader.getResourceAsStream("youtube/resource/$relativePath")!!,
context["res"].resolve(relativePath).toPath(),
StandardCopyOption.REPLACE_EXISTING
)
return PatchResultSuccess()
}
}

View File

@ -0,0 +1,60 @@
package app.revanced.patches.music.misc.settings.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patches.youtube.misc.manifest.patch.FixLocaleConfigErrorPatch
import app.revanced.shared.annotation.YouTubeMusicCompatibility
import app.revanced.shared.util.resources.ResourceUtils.copyXmlNode
import org.w3c.dom.Element
@Patch
@Name("music-settings")
@Description("Adds settings for ReVanced to YouTube Music.")
@YouTubeMusicCompatibility
@DependsOn([FixLocaleConfigErrorPatch::class])
@Version("0.0.1")
class MusicSettingsPatch : ResourcePatch {
override fun execute(context: ResourceContext): PatchResult {
/*
* Copy strings
*/
context.copyXmlNode("music/settings/host", "values/strings.xml", "resources")
/*
* Copy colors
*/
context.xmlEditor["res/values/colors.xml"].use { editor ->
val resourcesNode = editor.file.getElementsByTagName("resources").item(0) as Element
for (i in 0 until resourcesNode.childNodes.length) {
val node = resourcesNode.childNodes.item(i) as? Element ?: continue
node.textContent = when (node.getAttribute("name")) {
"material_deep_teal_500" -> "@android:color/white"
else -> continue
}
}
}
/*
* Copy preference fragments
*/
context.copyXmlNode("music/settings/host", "xml/settings_headers.xml", "PreferenceScreen")
context.copyXmlNode("music/settings/host", "xml/settings_prefs_compat.xml", "com.google.android.apps.youtube.music.ui.preference.PreferenceCategoryCompat")
return PatchResultSuccess()
}
}

View File

@ -0,0 +1,86 @@
package app.revanced.patches.youtube.misc.translations.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.annotations.DependsOn
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patches.music.misc.integrations.patch.MusicIntegrationsPatch
import app.revanced.patches.music.misc.settings.patch.MusicSettingsPatch
import app.revanced.shared.annotation.YouTubeMusicCompatibility
import java.nio.file.Files
import java.nio.file.StandardCopyOption
@Patch
@DependsOn([MusicIntegrationsPatch::class, MusicSettingsPatch::class])
@Name("translations-music")
@Description("Add Crowdin Translations for YouTube Music")
@YouTubeMusicCompatibility
@Version("0.0.1")
class MusicTranslationsPatch : ResourcePatch {
override fun execute(context: ResourceContext): PatchResult {
val revanced_translations = "translate" to arrayOf(
"ar-v21",
"az-rAZ-v21",
"bg-rBG-v21",
"bn-rIN-v21",
"bn-v21",
"cs-rCZ-v21",
"de-rDE-v21",
"el-rGR-v21",
"es-rES-v21",
"fi-rFI-v21",
"fr-rFR-v21",
"hi-rIN-v21",
"hu-rHU-v21",
"id-rID-v21",
"in-v21",
"it-rIT-v21",
"ja-rJP-v21",
"kn-rIN-v21",
"ko-rKR-v21",
"ml-rIN-v21",
"nl-rNL-v21",
"pa-rIN-v21",
"pl-rPL-v21",
"pt-rBR-v21",
"pt-rPT-v21",
"ro-rRO-v21",
"ru-rRU-v21",
"sk-rSK-v21",
"sv-rFI-v21",
"sv-rSE-v21",
"ta-rIN-v21",
"th-v21",
"tr-rTR-v21",
"uk-rUA-v21",
"vi-rVN-v21",
"zh-rCN-v21",
"zh-rTW-v21"
)
val TranslationsResources = arrayOf(revanced_translations)
val classLoader = this.javaClass.classLoader
TranslationsResources.forEach { (path, languageNames) ->
languageNames.forEach { name ->
val resDirectory = context["res"].resolve("values-$name")
val relativePath = "values-$name/strings.xml"
Files.createDirectory(resDirectory.toPath())
Files.copy(
classLoader.getResourceAsStream("music/$path/$relativePath")!!,
context["res"].resolve(relativePath).toPath(),
StandardCopyOption.REPLACE_EXISTING
)
}
}
return PatchResultSuccess()
}
}

View File

@ -1,30 +0,0 @@
package app.revanced.patches.music.premium.backgroundplay.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility(
[Package(
"com.google.android.apps.youtube.music",
arrayOf(
"5.14.53",
"5.16.51",
"5.17.51",
"5.21.52",
"5.22.54",
"5.23.50",
"5.25.51",
"5.25.52",
"5.26.52",
"5.27.51",
"5.28.52",
"5.29.52",
"5.31.50",
"5.34.51",
"5.36.51"
)
)]
)
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class BackgroundPlayCompatibility

View File

@ -6,7 +6,6 @@ import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.AccessFlags
import org.jf.dexlib2.Opcode
@FuzzyPatternScanMethod(2) // FIXME: Test this threshold and find the best value.
object BackgroundPlaybackDisableFingerprint : MethodFingerprint(
"Z", AccessFlags.PUBLIC or AccessFlags.STATIC, listOf("L"), listOf(

View File

@ -5,17 +5,17 @@ import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.music.premium.backgroundplay.annotations.BackgroundPlayCompatibility
import app.revanced.patches.music.premium.backgroundplay.fingerprints.BackgroundPlaybackDisableFingerprint
import app.revanced.shared.annotation.YouTubeMusicCompatibility
@Patch
@Name("background-play")
@Description("Enables playing music in the background.")
@BackgroundPlayCompatibility
@YouTubeMusicCompatibility
@Version("0.0.1")
class BackgroundPlayPatch : BytecodePatch(
listOf(

View File

@ -1,9 +0,0 @@
package app.revanced.patches.myexpenses.misc.pro.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility([Package("org.totschnig.myexpenses")])
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class UnlockProCompatibility

View File

@ -1,8 +0,0 @@
package app.revanced.patches.myexpenses.misc.pro.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
object IsEnabledFingerprint : MethodFingerprint(
"Z",
strings = listOf("feature", "feature.licenceStatus")
)

View File

@ -1,38 +0,0 @@
package app.revanced.patches.myexpenses.misc.pro.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.removeInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.myexpenses.misc.pro.annotations.UnlockProCompatibility
import app.revanced.patches.myexpenses.misc.pro.fingerprints.IsEnabledFingerprint
@Patch
@Name("unlock-pro")
@Description("Unlocks all professional features.")
@UnlockProCompatibility
@Version("0.0.1")
class UnlockProPatch : BytecodePatch(
listOf(
IsEnabledFingerprint
)
) {
override fun execute(context: BytecodeContext): PatchResult {
val method = IsEnabledFingerprint.result!!.mutableMethod
method.addInstructions(
0,
"""
const/4 v0, 0x1
return v0
"""
)
return PatchResultSuccess()
}
}

View File

@ -1,9 +0,0 @@
package app.revanced.patches.nyx.misc.pro.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility([Package("com.awedea.nyx")])
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class UnlockProCompatibility

View File

@ -1,9 +0,0 @@
package app.revanced.patches.nyx.misc.pro.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
object CheckProFingerprint : MethodFingerprint(
customFingerprint = { methodDef ->
methodDef.definingClass.endsWith("BillingManager;") && methodDef.name == "isProVersion"
}
)

View File

@ -1,38 +0,0 @@
package app.revanced.patches.nyx.misc.pro.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.extensions.removeInstruction
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.nyx.misc.pro.annotations.UnlockProCompatibility
import app.revanced.patches.nyx.misc.pro.fingerprints.CheckProFingerprint
@Patch
@Name("unlock-pro")
@Description("Unlocks all pro features.")
@UnlockProCompatibility
@Version("0.0.1")
class UnlockProPatch : BytecodePatch(
listOf(
CheckProFingerprint
)
) {
override fun execute(context: BytecodeContext): PatchResult {
val method = CheckProFingerprint.result!!.mutableMethod
method.addInstructions(
0,
"""
const/4 v0, 0x1
return v0
"""
)
return PatchResultSuccess()
}
}

View File

@ -1,9 +0,0 @@
package app.revanced.patches.reddit.ad.general.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility([Package("com.reddit.frontpage")])
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class GeneralAdsCompatibility

View File

@ -1,54 +0,0 @@
package app.revanced.patches.reddit.ad.general.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.reddit.ad.general.annotations.GeneralAdsCompatibility
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.builder.instruction.BuilderInstruction21c
import org.jf.dexlib2.iface.instruction.ReferenceInstruction
import org.jf.dexlib2.iface.reference.StringReference
import org.jf.dexlib2.immutable.reference.ImmutableStringReference
@Patch
@Name("general-reddit-ads")
@Description("Removes general ads from the Reddit frontpage and subreddits.")
@GeneralAdsCompatibility
@Version("0.0.1")
class GeneralAdsPatch : BytecodePatch() {
override fun execute(context: BytecodeContext): PatchResult {
context.classes.forEach { classDef ->
classDef.methods.forEach methodLoop@{ method ->
val implementation = method.implementation ?: return@methodLoop
implementation.instructions.forEachIndexed { i, instruction ->
if (instruction.opcode != Opcode.CONST_STRING) return@forEachIndexed
if (((instruction as ReferenceInstruction).reference as StringReference).string != "AdPost") return@forEachIndexed
val proxiedClass = context.proxy(classDef).mutableClass
val proxiedImplementation = proxiedClass.methods.first {
it.name == method.name && it.parameterTypes.containsAll(method.parameterTypes)
}.implementation!!
var newString = "AdPost1"
if (proxiedImplementation.instructions[i - 1].opcode == Opcode.CONST_STRING) {
newString = "SubredditPost"
}
proxiedImplementation.replaceInstruction(
i, BuilderInstruction21c(
Opcode.CONST_STRING, (proxiedImplementation.instructions[i] as BuilderInstruction21c).registerA, ImmutableStringReference(newString)
)
)
}
}
}
return PatchResultSuccess()
}
}

View File

@ -1,9 +0,0 @@
package app.revanced.patches.reddit.layout.premiumicon.annotations
import app.revanced.patcher.annotation.Compatibility
import app.revanced.patcher.annotation.Package
@Compatibility([Package("com.reddit.frontpage")])
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
internal annotation class PremiumIconCompatibility

View File

@ -1,10 +0,0 @@
package app.revanced.patches.reddit.layout.premiumicon.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
object PremiumIconFingerprint : MethodFingerprint(
"Z",
customFingerprint = { methodDef ->
methodDef.definingClass.endsWith("MyAccount;") && methodDef.name == "isPremiumSubscriber"
}
)

View File

@ -1,36 +0,0 @@
package app.revanced.patches.reddit.layout.premiumicon.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstructions
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patches.reddit.layout.premiumicon.annotations.PremiumIconCompatibility
import app.revanced.patches.reddit.layout.premiumicon.fingerprints.PremiumIconFingerprint
@Patch
@Name("premium-icon-reddit")
@Description("Unlocks premium Reddit app icons.")
@PremiumIconCompatibility
@Version("0.0.1")
class PremiumIconPatch : BytecodePatch(
listOf(
PremiumIconFingerprint
)
) {
override fun execute(context: BytecodeContext): PatchResult {
val method = PremiumIconFingerprint.result!!.mutableMethod
method.addInstructions(
0,
"""
const/4 v0, 0x1
return v0
"""
)
return PatchResultSuccess()
}
}

View File

@ -1,9 +0,0 @@
package app.revanced.patches.shared.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
object SeekbarFingerprint : MethodFingerprint(
"V",
strings = listOf("timed_markers_width")
)

View File

@ -1,8 +0,0 @@
package app.revanced.patches.shared.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
object SeekbarOnDrawFingerprint : MethodFingerprint(
customFingerprint = { it.name == "onDraw" }
)

View File

@ -1,66 +0,0 @@
package app.revanced.patches.shared.integrations.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstruction
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultError
import app.revanced.patcher.patch.PatchResultSuccess
import org.jf.dexlib2.iface.Method
@Description("Applies mandatory patches to implement the ReVanced integrations into the application.")
@Version("0.0.1")
abstract class AbstractIntegrationsPatch(
private val integrationsDescriptor: String,
private val hooks: Iterable<IntegrationsFingerprint>
) : BytecodePatch(hooks) {
/**
* [MethodFingerprint] for integrations.
*
* @param contextRegisterResolver A [RegisterResolver] to get the register.
* @see MethodFingerprint
*/
abstract class IntegrationsFingerprint(
strings: Iterable<String>? = null,
customFingerprint: ((methodDef: Method) -> Boolean)? = null,
private val contextRegisterResolver: (Method) -> Int = object : RegisterResolver {}
) : MethodFingerprint(strings = strings, customFingerprint = customFingerprint) {
fun invoke(integrationsDescriptor: String): PatchResult {
result?.mutableMethod?.let { method ->
val contextRegister = contextRegisterResolver(method)
method.addInstruction(
0,
"sput-object v$contextRegister, " +
"$integrationsDescriptor->context:Landroid/content/Context;"
)
} ?: return PatchResultError("Could not find hook target fingerprint.")
return PatchResultSuccess()
}
interface RegisterResolver : (Method) -> Int {
override operator fun invoke(method: Method) = method.implementation!!.registerCount - 1
}
}
override fun execute(context: BytecodeContext): PatchResult {
if (context.findClass(integrationsDescriptor) == null) return MISSING_INTEGRATIONS
for (hook in hooks) hook.invoke(integrationsDescriptor).let {
if (it is PatchResultError) return it
}
return PatchResultSuccess()
}
private companion object {
val MISSING_INTEGRATIONS = PatchResultError(
"Integrations have not been merged yet. " +
"This patch can not succeed without merging the integrations."
)
}
}

View File

@ -1,73 +0,0 @@
package app.revanced.patches.shared.mapping.misc.patch
import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess
import app.revanced.patcher.patch.ResourcePatch
import org.w3c.dom.Element
import java.util.*
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
@Name("resource-mapping")
@Description("Creates a map of public resources.")
@Version("0.0.1")
class ResourceMappingPatch : ResourcePatch {
companion object {
internal lateinit var resourceMappings: List<ResourceElement>
private set
private val THREAD_COUNT = Runtime.getRuntime().availableProcessors()
private val threadPoolExecutor = Executors.newFixedThreadPool(THREAD_COUNT)
}
override fun execute(context: ResourceContext): PatchResult {
// save the file in memory to concurrently read from
val resourceXmlFile = context["res/values/public.xml"].readBytes()
// create a synchronized list to store the resource mappings
val mappings = Collections.synchronizedList(mutableListOf<ResourceElement>())
for (threadIndex in 0 until THREAD_COUNT) {
threadPoolExecutor.execute thread@{
context.xmlEditor[resourceXmlFile.inputStream()].use { editor ->
val resources = editor.file.documentElement.childNodes
val resourcesLength = resources.length
val jobSize = resourcesLength / THREAD_COUNT
val batchStart = jobSize * threadIndex
val batchEnd = jobSize * (threadIndex + 1)
element@ for (i in batchStart until batchEnd) {
// make sure to not to go out of bounds when rounding errors occur at calculating the jobSize
if (i >= resourcesLength) return@thread
val node = resources.item(i)
if (node !is Element) continue
val nameAttribute = node.getAttribute("name")
val typeAttribute = node.getAttribute("type")
if (node.nodeName != "public" || nameAttribute.startsWith("APKTOOL")) continue
val id = node.getAttribute("id").substring(2).toLong(16)
mappings.add(ResourceElement(typeAttribute, nameAttribute, id))
}
}
}
}
threadPoolExecutor
.also { it.shutdown() }
.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS)
resourceMappings = mappings
return PatchResultSuccess()
}
}
data class ResourceElement(val type: String, val name: String, val id: Long)

View File

@ -1,31 +0,0 @@
package app.revanced.patches.shared.settings.preference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import org.w3c.dom.Document
import org.w3c.dom.Element
/**
* Base preference class for all preferences.
*
* @param key The key of the preference.
* @param title The title of the preference.
*/
internal abstract class BasePreference(
override val key: String,
override val title: StringResource,
) : IPreference {
/**
* Serialize preference element to XML.
* Overriding methods should invoke super and operate on its return value.
* @param ownerDocument Target document to create elements from.
* @param resourceCallback Called when a resource has been processed.
*/
open fun serialize(ownerDocument: Document, resourceCallback: ((IResource) -> Unit)? = null): Element {
return ownerDocument.createElement(tag).apply {
if(key.isNotEmpty())
setAttribute("android:key", key)
setAttribute("android:title", "@string/${title.also { resourceCallback?.invoke(it) }.name}")
}
}
}

View File

@ -1,26 +0,0 @@
package app.revanced.patches.shared.settings.preference
import org.w3c.dom.Document
import org.w3c.dom.Element
/**
* Base resource class for all resources.
*
* @param name The name of the resource.
*/
internal abstract class BaseResource(
override val name: String
) : IResource {
/**
* Serialize resource element to XML.
* Overriding methods should invoke super and operate on its return value.
* @param ownerDocument Target document to create elements from.
* @param resourceCallback Called when a resource has been processed.
*/
open fun serialize(ownerDocument: Document, resourceCallback: ((IResource) -> Unit)? = null): Element {
return ownerDocument.createElement(tag).apply {
setAttribute("name", name)
}
}
}

View File

@ -1,42 +0,0 @@
package app.revanced.patches.shared.settings.preference
import app.revanced.patches.shared.settings.preference.impl.StringResource
import org.w3c.dom.Element
import org.w3c.dom.Node
/**
* Add a resource node child
*
* @param resource The resource to add.
* @param resourceCallback Called when a resource has been processed.
*/
internal fun Node.addResource(resource: BaseResource, resourceCallback: ((IResource) -> Unit)? = null) {
appendChild(resource.serialize(ownerDocument, resourceCallback))
}
/**
* Add a preference node child to the settings.
*
* @param preference The preference to add.
* @param resourceCallback Called when a resource has been processed.
*/
internal fun Node.addPreference(preference: BasePreference, resourceCallback: ((IResource) -> Unit)? = null) {
appendChild(preference.serialize(ownerDocument, resourceCallback))
}
internal fun Element.addSummary(summaryResource: StringResource?, summaryType: SummaryType = SummaryType.DEFAULT) =
summaryResource?.let { summary ->
setAttribute("android:${summaryType.type}", "@string/${summary.name}")
}
internal fun <T> Element.addDefault(default: T) {
default?.let {
setAttribute(
"android:defaultValue", when (it) {
is Boolean -> if (it) "true" else "false"
is String -> it
else -> throw IllegalArgumentException("Unsupported default value type: ${it::class.java.name}")
}
)
}
}

View File

@ -1,23 +0,0 @@
package app.revanced.patches.shared.settings.preference
import app.revanced.patches.shared.settings.preference.impl.StringResource
/**
* Preference
*/
internal interface IPreference {
/**
* Key of the preference.
*/
val key: String
/**
* Title of the preference.
*/
val title: StringResource
/**
* Tag name of the preference.
*/
val tag: String
}

View File

@ -1,16 +0,0 @@
package app.revanced.patches.shared.settings.preference
/**
* Resource
*/
internal interface IResource {
/**
* Name of the resource.
*/
val name: String
/**
* Tag name of the resource.
*/
val tag: String
}

View File

@ -1,5 +0,0 @@
package app.revanced.patches.shared.settings.preference
enum class SummaryType(val type: String) {
DEFAULT("summary"), ON("summaryOn"), OFF("summaryOff")
}

View File

@ -1,33 +0,0 @@
package app.revanced.patches.shared.settings.preference.impl
import app.revanced.patches.shared.settings.preference.BaseResource
import app.revanced.patches.shared.settings.preference.IResource
import org.w3c.dom.Document
import org.w3c.dom.Element
/**
* Represents an array resource.
*
* @param name The name of the array resource.
* @param items The items of the array resource.
*/
internal data class ArrayResource(
override val name: String,
val items: List<StringResource>
) : BaseResource(name) {
override val tag = "string-array"
override fun serialize(ownerDocument: Document, resourceCallback: ((IResource) -> Unit)?): Element {
return super.serialize(ownerDocument, resourceCallback).apply {
setAttribute("name", name)
items.forEach { item ->
resourceCallback?.invoke(item)
this.appendChild(ownerDocument.createElement("item").also { itemNode ->
itemNode.textContent = "@string/${item.name}"
})
}
}
}
}

View File

@ -1,6 +0,0 @@
package app.revanced.patches.shared.settings.preference.impl
enum class InputType(val type: String) {
STRING("text"),
NUMBER("number"),
}

View File

@ -1,38 +0,0 @@
package app.revanced.patches.shared.settings.preference.impl
import app.revanced.patches.shared.settings.preference.BasePreference
import app.revanced.patches.shared.settings.preference.IResource
import app.revanced.patches.shared.settings.preference.addDefault
import app.revanced.patches.shared.settings.preference.addSummary
import org.w3c.dom.Document
import org.w3c.dom.Element
/**
* List preference.
*
* @param key The key of the list preference.
* @param title The title of the list preference.
* @param entries The human-readable entries of the list preference.
* @param entryValues The entry values of the list preference.
* @param default The default entry value of the list preference.
* @param summary The summary of the list preference.
*/
internal class ListPreference(
key: String,
title: StringResource,
val entries: ArrayResource,
val entryValues: ArrayResource,
val default: String? = null,
val summary: StringResource? = null
) : BasePreference(key, title) {
override val tag: String = "ListPreference"
override fun serialize(ownerDocument: Document, resourceCallback: ((IResource) -> Unit)?): Element {
return super.serialize(ownerDocument, resourceCallback).apply {
setAttribute("android:entries", "@array/${entries.also { resourceCallback?.invoke(it) }.name}")
setAttribute("android:entryValues", "@array/${entryValues.also { resourceCallback?.invoke(it) }.name}")
addDefault(default)
addSummary(summary)
}
}
}

View File

@ -1,44 +0,0 @@
package app.revanced.patches.shared.settings.preference.impl
import app.revanced.patches.shared.settings.preference.BasePreference
import app.revanced.patches.shared.settings.preference.IResource
import app.revanced.patches.shared.settings.preference.addSummary
import org.w3c.dom.Document
import org.w3c.dom.Element
/**
* A Preference object.
*
* @param title The title of the preference.
* @param intent The intent of the preference.
* @param summary The summary of the text preference.
*/
internal class Preference(
key: String,
title: StringResource,
val intent: Intent,
val summary: StringResource? = null
) : BasePreference(key, title) {
override val tag: String = "Preference"
/* Key-less constructor */
constructor(
title: StringResource,
intent: Intent,
summary: StringResource? = null
) : this("", title, intent, summary)
override fun serialize(ownerDocument: Document, resourceCallback: ((IResource) -> Unit)?): Element {
return super.serialize(ownerDocument, resourceCallback).apply {
addSummary(summary?.also { resourceCallback?.invoke(it) })
this.appendChild(ownerDocument.createElement("intent").also { intentNode ->
intentNode.setAttribute("android:targetPackage", intent.targetPackage)
intentNode.setAttribute("android:data", intent.data)
intentNode.setAttribute("android:targetClass", intent.targetClass)
})
}
}
data class Intent(val targetPackage: String, val data: String, val targetClass: String)
}

Some files were not shown because too many files have changed in this diff Show More