diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/shortsbutton/annotations/ShortsButtonCompatibility.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/annotations/PivotBarCompatibility.kt similarity index 63% rename from src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/shortsbutton/annotations/ShortsButtonCompatibility.kt rename to src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/annotations/PivotBarCompatibility.kt index edf4d36e..25083136 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/shortsbutton/annotations/ShortsButtonCompatibility.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/annotations/PivotBarCompatibility.kt @@ -1,13 +1,13 @@ -package app.revanced.patches.youtube.layout.pivotbar.shortsbutton.annotations +package app.revanced.patches.youtube.layout.pivotbar.annotations import app.revanced.patcher.annotation.Compatibility import app.revanced.patcher.annotation.Package @Compatibility( [Package( - "com.google.android.youtube", arrayOf("17.36.37", "17.41.37", "17.42.35", "17.43.36") + "com.google.android.youtube", arrayOf("17.36.37", "17.41.37", "17.42.35", "17.43.36", "17.45.36") )] ) @Target(AnnotationTarget.CLASS) @Retention(AnnotationRetention.RUNTIME) -internal annotation class ShortsButtonCompatibility \ No newline at end of file +internal annotation class PivotBarCompatibility \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/fingerprints/InitializeButtonsFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/fingerprints/InitializeButtonsFingerprint.kt new file mode 100644 index 00000000..32a77f7b --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/fingerprints/InitializeButtonsFingerprint.kt @@ -0,0 +1,15 @@ +package app.revanced.patches.youtube.layout.pivotbar.fingerprints + +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import app.revanced.patches.youtube.layout.pivotbar.resource.patch.ResolvePivotBarFingerprintsPatch +import org.jf.dexlib2.Opcode +import org.jf.dexlib2.iface.instruction.WideLiteralInstruction + +object InitializeButtonsFingerprint : MethodFingerprint( + customFingerprint = { methodDef -> + methodDef.implementation?.instructions?.any { + it.opcode == Opcode.CONST && (it as WideLiteralInstruction).wideLiteral == + ResolvePivotBarFingerprintsPatch.imageOnlyTabResourceId + } == true + } +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/fingerprints/PivotBarConstructorFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/fingerprints/PivotBarConstructorFingerprint.kt new file mode 100644 index 00000000..0609dd1f --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/fingerprints/PivotBarConstructorFingerprint.kt @@ -0,0 +1,10 @@ +package app.revanced.patches.youtube.layout.pivotbar.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import org.jf.dexlib2.AccessFlags + +object PivotBarConstructorFingerprint : MethodFingerprint( + access = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, + strings = listOf("com.google.android.apps.youtube.app.endpoint.flags") +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/fingerprints/PivotBarFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/fingerprints/PivotBarFingerprint.kt deleted file mode 100644 index 29c81c37..00000000 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/fingerprints/PivotBarFingerprint.kt +++ /dev/null @@ -1,44 +0,0 @@ -package app.revanced.patches.youtube.layout.pivotbar.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) -object PivotBarFingerprint : MethodFingerprint( - "V", - AccessFlags.PUBLIC or AccessFlags.FINAL, - listOf("Z"), - listOf( - Opcode.IGET, - Opcode.INVOKE_STATIC, - Opcode.MOVE_RESULT_OBJECT, - Opcode.IF_NEZ, - Opcode.SGET_OBJECT, - Opcode.INVOKE_INTERFACE, - Opcode.MOVE_RESULT, - Opcode.IGET_OBJECT, - Opcode.IF_NEZ, - Opcode.SGET_OBJECT, - Opcode.IGET_OBJECT, - Opcode.IF_NEZ, - Opcode.SGET_OBJECT, - Opcode.IGET_OBJECT, - Opcode.IGET_OBJECT, - Opcode.INVOKE_VIRTUAL, - Opcode.MOVE_RESULT_OBJECT, - Opcode.INVOKE_STATIC, - Opcode.MOVE_RESULT_OBJECT, - Opcode.NEW_INSTANCE, - Opcode.NEW_INSTANCE, - Opcode.INVOKE_DIRECT, - Opcode.CONST, - Opcode.INVOKE_STATIC, - Opcode.MOVE_RESULT_OBJECT, - Opcode.MOVE_OBJECT, - Opcode.MOVE_OBJECT, - Opcode.INVOKE_DIRECT_RANGE, - ) -) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/resource/patch/ResolvePivotBarFingerprintsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/resource/patch/ResolvePivotBarFingerprintsPatch.kt new file mode 100644 index 00000000..bf1cc3f6 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/resource/patch/ResolvePivotBarFingerprintsPatch.kt @@ -0,0 +1,45 @@ +package app.revanced.patches.youtube.layout.pivotbar.resource.patch + +import app.revanced.patcher.annotation.Description +import app.revanced.patcher.annotation.Version +import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve +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.DependsOn +import app.revanced.patches.shared.mapping.misc.patch.ResourceMappingPatch +import app.revanced.patches.youtube.layout.pivotbar.annotations.PivotBarCompatibility +import app.revanced.patches.youtube.layout.pivotbar.fingerprints.InitializeButtonsFingerprint +import app.revanced.patches.youtube.layout.pivotbar.fingerprints.PivotBarConstructorFingerprint +import app.revanced.patches.youtube.layout.pivotbar.utils.InjectionUtils.toErrorResult + +@DependsOn([ResourceMappingPatch::class]) +@PivotBarCompatibility +@Description("Resolves necessary fingerprints.") +@Version("0.0.1") +class ResolvePivotBarFingerprintsPatch : BytecodePatch( + listOf(PivotBarConstructorFingerprint) +) { + internal companion object { + var imageOnlyTabResourceId: Long = -1 + } + + override fun execute(context: BytecodeContext): PatchResult { + // imageOnlyTabResourceId is used in InitializeButtonsFingerprint fingerprint + ResourceMappingPatch.resourceMappings.find { it.type == "layout" && it.name == "image_only_tab" } + ?.let { imageOnlyTabResourceId = it.id } ?: return PatchResultError("Failed to find resource") + + PivotBarConstructorFingerprint.result?.let { + // Resolve InitializeButtonsFingerprint on the class of the method + // which PivotBarConstructorFingerprint resolved to + if (!InitializeButtonsFingerprint.resolve( + context, + it.classDef + ) + ) return InitializeButtonsFingerprint.toErrorResult() + } ?: return PivotBarConstructorFingerprint.toErrorResult() + return PatchResultSuccess() + } +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/shortsbutton/patch/ShortsButtonRemoverPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/shortsbutton/patch/ShortsButtonRemoverPatch.kt index 86b71e4c..66ec687c 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/shortsbutton/patch/ShortsButtonRemoverPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/shortsbutton/patch/ShortsButtonRemoverPatch.kt @@ -4,34 +4,36 @@ 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.fingerprint.method.impl.MethodFingerprint.Companion.resolve 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.DependsOn import app.revanced.patcher.patch.annotations.Patch -import app.revanced.patches.youtube.layout.pivotbar.fingerprints.PivotBarFingerprint -import app.revanced.patches.youtube.layout.pivotbar.shortsbutton.annotations.ShortsButtonCompatibility +import app.revanced.patches.shared.settings.preference.impl.StringResource +import app.revanced.patches.shared.settings.preference.impl.SwitchPreference +import app.revanced.patches.youtube.layout.pivotbar.annotations.PivotBarCompatibility +import app.revanced.patches.youtube.layout.pivotbar.fingerprints.InitializeButtonsFingerprint +import app.revanced.patches.youtube.layout.pivotbar.resource.patch.ResolvePivotBarFingerprintsPatch import app.revanced.patches.youtube.layout.pivotbar.shortsbutton.fingerprints.PivotBarEnumFingerprint import app.revanced.patches.youtube.layout.pivotbar.shortsbutton.fingerprints.PivotBarShortsButtonViewFingerprint import app.revanced.patches.youtube.layout.pivotbar.utils.InjectionUtils.REGISTER_TEMPLATE_REPLACEMENT import app.revanced.patches.youtube.layout.pivotbar.utils.InjectionUtils.injectHook +import app.revanced.patches.youtube.layout.pivotbar.utils.InjectionUtils.toErrorResult import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch import app.revanced.patches.youtube.misc.settings.bytecode.patch.SettingsPatch -import app.revanced.patches.shared.settings.preference.impl.StringResource -import app.revanced.patches.shared.settings.preference.impl.SwitchPreference @Patch -@DependsOn([IntegrationsPatch::class, SettingsPatch::class]) +@DependsOn([IntegrationsPatch::class, SettingsPatch::class, ResolvePivotBarFingerprintsPatch::class]) @Name("hide-shorts-button") @Description("Hides the shorts button on the navigation bar.") -@ShortsButtonCompatibility +@PivotBarCompatibility @Version("0.0.1") -class ShortsButtonRemoverPatch : BytecodePatch( - listOf(PivotBarFingerprint) -) { +class ShortsButtonRemoverPatch : BytecodePatch() { + private companion object { + const val INTEGRATIONS_CLASS_DESCRIPTOR = "Lapp/revanced/integrations/patches/HideShortsButtonPatch;" + } + override fun execute(context: BytecodeContext): PatchResult { SettingsPatch.PreferenceScreen.LAYOUT.addPreferences( SwitchPreference( @@ -47,18 +49,21 @@ class ShortsButtonRemoverPatch : BytecodePatch( * Resolve fingerprints */ - val pivotBarResult = PivotBarFingerprint.result ?: return PatchResultError("PivotBarFingerprint failed") - val fingerprintResults = arrayOf(PivotBarEnumFingerprint, PivotBarShortsButtonViewFingerprint) - .onEach { - val resolutionSucceeded = it.resolve( - context, - pivotBarResult.mutableMethod, - pivotBarResult.mutableClass - ) + val initializeButtonsResult = InitializeButtonsFingerprint.result!! + + val fingerprintResults = + arrayOf(PivotBarEnumFingerprint, PivotBarShortsButtonViewFingerprint) + .onEach { + if (!it.resolve( + context, + initializeButtonsResult.mutableMethod, + initializeButtonsResult.mutableClass + ) + ) + return it.toErrorResult() + } + .map { it.result!!.scanResult.patternScanResult!! } - if (!resolutionSucceeded) return PatchResultError("${it.name} failed") - } - .map { it.result!!.scanResult.patternScanResult!! } val enumScanResult = fingerprintResults[0] val buttonViewResult = fingerprintResults[1] @@ -70,19 +75,17 @@ class ShortsButtonRemoverPatch : BytecodePatch( * Inject hooks */ - val integrationsClass = "Lapp/revanced/integrations/patches/HideShortsButtonPatch;" - - val enumHook = - "sput-object v$REGISTER_TEMPLATE_REPLACEMENT, $integrationsClass->lastPivotTab:Ljava/lang/Enum;" - val buttonHook = - "invoke-static { v$REGISTER_TEMPLATE_REPLACEMENT }, $integrationsClass->hideShortsButton(Landroid/view/View;)V" + val enumHook = "sput-object v$REGISTER_TEMPLATE_REPLACEMENT, " + + "$INTEGRATIONS_CLASS_DESCRIPTOR->lastPivotTab:Ljava/lang/Enum;" + val buttonHook = "invoke-static { v$REGISTER_TEMPLATE_REPLACEMENT }, " + + "$INTEGRATIONS_CLASS_DESCRIPTOR->hideShortsButton(Landroid/view/View;)V" // Inject bottom to top to not mess up the indices mapOf( buttonHook to buttonHookInsertIndex, enumHook to enumHookInsertIndex ).forEach { (hook, insertIndex) -> - pivotBarResult.mutableMethod.injectHook(hook, insertIndex) + initializeButtonsResult.mutableMethod.injectHook(hook, insertIndex) } return PatchResultSuccess() diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/utils/InjectionUtils.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/utils/InjectionUtils.kt index 8667b10d..e8ba2fc3 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/utils/InjectionUtils.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/pivotbar/utils/InjectionUtils.kt @@ -1,7 +1,10 @@ package app.revanced.patches.youtube.layout.pivotbar.utils +import app.revanced.patcher.extensions.MethodFingerprintExtensions.name import app.revanced.patcher.extensions.addInstruction import app.revanced.patcher.extensions.instruction +import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint +import app.revanced.patcher.patch.PatchResultError import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import org.jf.dexlib2.Opcode.MOVE_RESULT_OBJECT import org.jf.dexlib2.iface.instruction.OneRegisterInstruction @@ -27,4 +30,7 @@ internal object InjectionUtils { hook.replace("REGISTER_INDEX", register.toString()), ) } + + // TODO: move this to a global extension class and use it in all patches + fun MethodFingerprint.toErrorResult() = PatchResultError("Failed to resolve $name") } \ No newline at end of file