From 59162042b0a68edf7f94a3c21f838dada3c3f9c3 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Wed, 27 Mar 2024 11:26:40 +0400 Subject: [PATCH] feat(YouTube - Hide layout components): Filter home/search results by keywords (#2853) Co-authored-by: oSumAtrIX --- api/revanced-patches.api | 7 + .../navigation/NavigationButtonsPatch.kt | 84 ++--------- .../ResolvePivotBarFingerprintsPatch.kt | 37 ----- .../PivotBarButtonsViewFingerprint.kt | 12 -- .../PivotBarCreateButtonViewFingerprint.kt | 13 -- .../fingerprints/PivotBarEnumFingerprint.kt | 15 -- .../navigation/utils/InjectionUtils.kt | 30 ---- .../hide/general/HideLayoutComponentsPatch.kt | 39 ++++-- .../misc/navigation/NavigationBarHookPatch.kt | 130 ++++++++++++++++++ .../NavigationBarHookResourcePatch.kt | 24 ++++ .../ActionBarSearchResultsFingerprint.kt | 13 ++ .../InitializeButtonsFingerprint.kt | 9 +- .../NavigationBarHookCallbackFingerprint.kt | 21 +++ .../fingerprints/NavigationEnumFingerprint.kt | 21 +++ ...BarButtonsCreateDrawableViewFingerprint.kt | 17 +++ ...BarButtonsCreateResourceViewFingerprint.kt | 14 ++ .../PivotBarConstructorFingerprint.kt | 2 +- .../resources/addresources/values/strings.xml | 19 ++- 18 files changed, 311 insertions(+), 196 deletions(-) delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/ResolvePivotBarFingerprintsPatch.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/fingerprints/PivotBarButtonsViewFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/fingerprints/PivotBarCreateButtonViewFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/fingerprints/PivotBarEnumFingerprint.kt delete mode 100644 src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/utils/InjectionUtils.kt create mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/navigation/NavigationBarHookPatch.kt create mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/navigation/NavigationBarHookResourcePatch.kt create mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/ActionBarSearchResultsFingerprint.kt rename src/main/kotlin/app/revanced/patches/youtube/{layout/buttons => misc}/navigation/fingerprints/InitializeButtonsFingerprint.kt (52%) create mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/NavigationBarHookCallbackFingerprint.kt create mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/NavigationEnumFingerprint.kt create mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/PivotBarButtonsCreateDrawableViewFingerprint.kt create mode 100644 src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/PivotBarButtonsCreateResourceViewFingerprint.kt rename src/main/kotlin/app/revanced/patches/youtube/{layout/buttons => misc}/navigation/fingerprints/PivotBarConstructorFingerprint.kt (82%) diff --git a/api/revanced-patches.api b/api/revanced-patches.api index 80dc74465..521de8099 100644 --- a/api/revanced-patches.api +++ b/api/revanced-patches.api @@ -1609,6 +1609,13 @@ public final class app/revanced/patches/youtube/misc/minimizedplayback/Minimized public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V } +public final class app/revanced/patches/youtube/misc/navigation/NavigationBarHookPatch : app/revanced/patcher/patch/BytecodePatch { + public static final field INSTANCE Lapp/revanced/patches/youtube/misc/navigation/NavigationBarHookPatch; + public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V + public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V + public final fun getHookNavigationButtonCreated ()Lkotlin/jvm/functions/Function1; +} + public final class app/revanced/patches/youtube/misc/playercontrols/BottomControlsResourcePatch : app/revanced/patcher/patch/ResourcePatch, java/io/Closeable { public static final field INSTANCE Lapp/revanced/patches/youtube/misc/playercontrols/BottomControlsResourcePatch; public final fun addControls (Ljava/lang/String;)V diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/NavigationButtonsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/NavigationButtonsPatch.kt index 94e67f73b..c660344de 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/NavigationButtonsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/NavigationButtonsPatch.kt @@ -10,10 +10,10 @@ import app.revanced.patches.all.misc.resources.AddResourcesPatch import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting import app.revanced.patches.shared.misc.settings.preference.SwitchPreference -import app.revanced.patches.youtube.layout.buttons.navigation.fingerprints.* -import app.revanced.patches.youtube.layout.buttons.navigation.utils.InjectionUtils.REGISTER_TEMPLATE_REPLACEMENT -import app.revanced.patches.youtube.layout.buttons.navigation.utils.InjectionUtils.injectHook +import app.revanced.patches.youtube.layout.buttons.navigation.fingerprints.ANDROID_AUTOMOTIVE_STRING +import app.revanced.patches.youtube.layout.buttons.navigation.fingerprints.AddCreateButtonViewFingerprint import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch +import app.revanced.patches.youtube.misc.navigation.NavigationBarHookPatch import app.revanced.patches.youtube.misc.settings.SettingsPatch import app.revanced.util.exception import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction @@ -24,8 +24,8 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction dependencies = [ IntegrationsPatch::class, SettingsPatch::class, - ResolvePivotBarFingerprintsPatch::class, AddResourcesPatch::class, + NavigationBarHookPatch::class, ], compatiblePackages = [ CompatiblePackage( @@ -47,7 +47,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction "19.06.39", "19.07.40", "19.08.36", - "19.09.37" + "19.09.37", ], ), ], @@ -76,53 +76,7 @@ object NavigationButtonsPatch : BytecodePatch( ), ) - /* - * Resolve fingerprints - */ - - val initializeButtonsResult = InitializeButtonsFingerprint.result!! - - val fingerprintResults = - arrayOf(PivotBarEnumFingerprint, PivotBarButtonsViewFingerprint) - .onEach { - if (!it.resolve( - context, - initializeButtonsResult.mutableMethod, - initializeButtonsResult.mutableClass, - ) - ) { - throw it.exception - } - } - .map { it.result!!.scanResult.patternScanResult!! } - - val enumScanResult = fingerprintResults[0] - val buttonViewResult = fingerprintResults[1] - - val enumHookInsertIndex = enumScanResult.startIndex + 2 - val buttonHookInsertIndex = buttonViewResult.endIndex - - /* - * Inject hooks - */ - - val enumHook = "sput-object v$REGISTER_TEMPLATE_REPLACEMENT, " + - "$INTEGRATIONS_CLASS_DESCRIPTOR->lastNavigationButton:Ljava/lang/Enum;" - val buttonHook = "invoke-static { v$REGISTER_TEMPLATE_REPLACEMENT }, " + - "$INTEGRATIONS_CLASS_DESCRIPTOR->hideButton(Landroid/view/View;)V" - - // Inject bottom to top to not mess up the indices - mapOf( - buttonHook to buttonHookInsertIndex, - enumHook to enumHookInsertIndex, - ).forEach { (hook, insertIndex) -> - initializeButtonsResult.mutableMethod.injectHook(hook, insertIndex) - } - - /* - * Hide create or switch it with notifications buttons. - */ - + // Switch create with notifications button. AddCreateButtonViewFingerprint.result?.let { it.mutableMethod.apply { val stringIndex = it.scanResult.stringsScanResult!!.matches.find { match -> @@ -130,7 +84,8 @@ object NavigationButtonsPatch : BytecodePatch( }!!.index val conditionalCheckIndex = stringIndex - 1 - val conditionRegister = getInstruction(conditionalCheckIndex).registerA + val conditionRegister = + getInstruction(conditionalCheckIndex).registerA addInstructions( conditionalCheckIndex, @@ -142,26 +97,7 @@ object NavigationButtonsPatch : BytecodePatch( } } ?: throw AddCreateButtonViewFingerprint.exception - /* - * Resolve fingerprints - */ - - InitializeButtonsFingerprint.result!!.let { - if (!PivotBarCreateButtonViewFingerprint.resolve(context, it.mutableMethod, it.mutableClass)) { - throw PivotBarCreateButtonViewFingerprint.exception - } - } - - PivotBarCreateButtonViewFingerprint.result!!.apply { - val insertIndex = scanResult.patternScanResult!!.endIndex - - /* - * Inject hooks - */ - val hook = "invoke-static { v$REGISTER_TEMPLATE_REPLACEMENT }, " + - "$INTEGRATIONS_CLASS_DESCRIPTOR->hideCreateButton(Landroid/view/View;)V" - - mutableMethod.injectHook(hook, insertIndex) - } + // Hook navigation button created, in order to hide them. + NavigationBarHookPatch.hookNavigationButtonCreated(INTEGRATIONS_CLASS_DESCRIPTOR) } } diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/ResolvePivotBarFingerprintsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/ResolvePivotBarFingerprintsPatch.kt deleted file mode 100644 index eacab72b4..000000000 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/ResolvePivotBarFingerprintsPatch.kt +++ /dev/null @@ -1,37 +0,0 @@ -package app.revanced.patches.youtube.layout.buttons.navigation - -import app.revanced.util.exception -import app.revanced.patcher.data.BytecodeContext -import app.revanced.patcher.patch.BytecodePatch -import app.revanced.patcher.patch.PatchException -import app.revanced.patcher.patch.annotation.Patch -import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch -import app.revanced.patches.youtube.layout.buttons.navigation.fingerprints.InitializeButtonsFingerprint -import app.revanced.patches.youtube.layout.buttons.navigation.fingerprints.PivotBarConstructorFingerprint - -@Patch( - description = "Resolves necessary fingerprints.", - dependencies = [ResourceMappingPatch::class] -) -internal object ResolvePivotBarFingerprintsPatch : BytecodePatch( - setOf(PivotBarConstructorFingerprint) -) { - internal var imageOnlyTabResourceId: Long = -1 - - override fun execute(context: BytecodeContext) { - // imageOnlyTabResourceId is used in InitializeButtonsFingerprint fingerprint - ResourceMappingPatch.resourceMappings.find { it.type == "layout" && it.name == "image_only_tab" } - ?.let { imageOnlyTabResourceId = it.id } ?: throw PatchException("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 - ) - ) throw InitializeButtonsFingerprint.exception - } ?: throw PivotBarConstructorFingerprint.exception - } - -} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/fingerprints/PivotBarButtonsViewFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/fingerprints/PivotBarButtonsViewFingerprint.kt deleted file mode 100644 index 7827a4f4f..000000000 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/fingerprints/PivotBarButtonsViewFingerprint.kt +++ /dev/null @@ -1,12 +0,0 @@ -package app.revanced.patches.youtube.layout.buttons.navigation.fingerprints - -import app.revanced.patcher.fingerprint.MethodFingerprint -import com.android.tools.smali.dexlib2.Opcode - -internal object PivotBarButtonsViewFingerprint : MethodFingerprint( - opcodes = listOf( - Opcode.INVOKE_VIRTUAL_RANGE, - Opcode.MOVE_RESULT_OBJECT, // target reference - Opcode.GOTO, - ) -) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/fingerprints/PivotBarCreateButtonViewFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/fingerprints/PivotBarCreateButtonViewFingerprint.kt deleted file mode 100644 index a32ee0cb7..000000000 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/fingerprints/PivotBarCreateButtonViewFingerprint.kt +++ /dev/null @@ -1,13 +0,0 @@ -package app.revanced.patches.youtube.layout.buttons.navigation.fingerprints - -import app.revanced.patcher.fingerprint.MethodFingerprint -import com.android.tools.smali.dexlib2.Opcode - -internal object PivotBarCreateButtonViewFingerprint : MethodFingerprint( - opcodes = listOf( - Opcode.INVOKE_DIRECT_RANGE, - Opcode.INVOKE_VIRTUAL, - Opcode.MOVE_RESULT_OBJECT, - Opcode.INVOKE_STATIC - ) -) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/fingerprints/PivotBarEnumFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/fingerprints/PivotBarEnumFingerprint.kt deleted file mode 100644 index df40cc9eb..000000000 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/fingerprints/PivotBarEnumFingerprint.kt +++ /dev/null @@ -1,15 +0,0 @@ -package app.revanced.patches.youtube.layout.buttons.navigation.fingerprints - -import app.revanced.patcher.fingerprint.MethodFingerprint -import com.android.tools.smali.dexlib2.Opcode - -internal object PivotBarEnumFingerprint : MethodFingerprint( - opcodes = listOf( - Opcode.INVOKE_STATIC, - Opcode.MOVE_RESULT_OBJECT, - Opcode.IF_NEZ, // target reference - Opcode.SGET_OBJECT, - Opcode.INVOKE_VIRTUAL, - Opcode.MOVE_RESULT, - ) -) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/utils/InjectionUtils.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/utils/InjectionUtils.kt deleted file mode 100644 index f97305c12..000000000 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/utils/InjectionUtils.kt +++ /dev/null @@ -1,30 +0,0 @@ -package app.revanced.patches.youtube.layout.buttons.navigation.utils - -import app.revanced.patcher.extensions.InstructionExtensions.addInstruction -import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod -import com.android.tools.smali.dexlib2.Opcode.MOVE_RESULT_OBJECT -import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction - -internal object InjectionUtils { - const val REGISTER_TEMPLATE_REPLACEMENT: String = "REGISTER_INDEX" - - /** - * Injects an instruction into insertIndex of the hook. - * @param hook The hook to insert. - * @param insertIndex The index to insert the instruction at. - * [MOVE_RESULT_OBJECT] has to be the previous instruction before [insertIndex]. - */ - fun MutableMethod.injectHook(hook: String, insertIndex: Int) { - val injectTarget = this - - // Register to pass to the hook - val registerIndex = insertIndex - 1 // MOVE_RESULT_OBJECT is always the previous instruction - val register = injectTarget.getInstruction(registerIndex).registerA - - injectTarget.addInstruction( - insertIndex, - hook.replace("REGISTER_INDEX", register.toString()), - ) - } -} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt index 8123fc833..5a7428d7e 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt @@ -12,6 +12,7 @@ import app.revanced.patcher.patch.annotation.Patch import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patches.all.misc.resources.AddResourcesPatch import app.revanced.patches.shared.misc.settings.preference.InputType +import app.revanced.patches.shared.misc.settings.preference.NonInteractivePreference import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen import app.revanced.patches.shared.misc.settings.preference.PreferenceScreen.Sorting import app.revanced.patches.shared.misc.settings.preference.SwitchPreference @@ -20,8 +21,10 @@ import app.revanced.patches.youtube.layout.hide.general.fingerprints.ParseElemen import app.revanced.patches.youtube.layout.hide.general.fingerprints.PlayerOverlayFingerprint import app.revanced.patches.youtube.layout.hide.general.fingerprints.ShowWatermarkFingerprint import app.revanced.patches.youtube.misc.litho.filter.LithoFilterPatch +import app.revanced.patches.youtube.misc.navigation.NavigationBarHookPatch +import app.revanced.patches.youtube.misc.playertype.PlayerTypeHookPatch import app.revanced.patches.youtube.misc.settings.SettingsPatch -import app.revanced.util.exception +import app.revanced.util.resultOrThrow import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction @@ -33,6 +36,8 @@ import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction LithoFilterPatch::class, SettingsPatch::class, AddResourcesPatch::class, + NavigationBarHookPatch::class, + PlayerTypeHookPatch::class // Used by Keyword Content filter. ], compatiblePackages = [ CompatiblePackage( @@ -69,6 +74,8 @@ object HideLayoutComponentsPatch : BytecodePatch( "Lapp/revanced/integrations/youtube/patches/components/DescriptionComponentsFilter;" private const val CUSTOM_FILTER_CLASS_NAME = "Lapp/revanced/integrations/youtube/patches/components/CustomFilter;" + private const val KEYWORD_FILTER_CLASS_NAME = + "Lapp/revanced/integrations/youtube/patches/components/KeywordContentFilter;" override fun execute(context: BytecodeContext) { AddResourcesPatch(this::class) @@ -117,6 +124,20 @@ object HideLayoutComponentsPatch : BytecodePatch( SwitchPreference("revanced_hide_search_result_shelf_header"), ) + SettingsPatch.PreferenceScreen.FEED.addPreferences( + PreferenceScreen( + key = "revanced_hide_keyword_content_screen", + sorting = Sorting.UNSORTED, + preferences = setOf( + SwitchPreference("revanced_hide_keyword_content_home"), + SwitchPreference("revanced_hide_keyword_content_subscriptions"), + SwitchPreference("revanced_hide_keyword_content_search"), + TextPreference("revanced_hide_keyword_content_phrases", inputType = InputType.TEXT_MULTI_LINE), + NonInteractivePreference("revanced_hide_keyword_content_about") + ) + ) + ) + SettingsPatch.PreferenceScreen.GENERAL_LAYOUT.addPreferences( SwitchPreference("revanced_hide_gray_separator"), PreferenceScreen( @@ -136,19 +157,19 @@ object HideLayoutComponentsPatch : BytecodePatch( LithoFilterPatch.addFilter(LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR) LithoFilterPatch.addFilter(DESCRIPTION_COMPONENTS_FILTER_CLASS_NAME) + LithoFilterPatch.addFilter(KEYWORD_FILTER_CLASS_NAME) LithoFilterPatch.addFilter(CUSTOM_FILTER_CLASS_NAME) // region Mix playlists - ParseElementFromBufferFingerprint.result?.let { result -> - val returnEmptyComponentInstruction = - result.mutableMethod.getInstructions().last { it.opcode == Opcode.INVOKE_STATIC } + ParseElementFromBufferFingerprint.resultOrThrow().let { result -> + val consumeByteBufferIndex = result.scanResult.patternScanResult!!.startIndex result.mutableMethod.apply { - val consumeByteBufferIndex = result.scanResult.patternScanResult!!.startIndex val conversionContextRegister = getInstruction(consumeByteBufferIndex - 2).registerA val byteBufferRegister = getInstruction(consumeByteBufferIndex).registerD + val returnEmptyComponentInstruction = getInstructions().last { it.opcode == Opcode.INVOKE_STATIC } addInstructionsWithLabels( consumeByteBufferIndex, @@ -160,15 +181,15 @@ object HideLayoutComponentsPatch : BytecodePatch( ExternalLabel("return_empty_component", returnEmptyComponentInstruction), ) } - } ?: throw ParseElementFromBufferFingerprint.exception + } // endregion // region Watermark (legacy code for old versions of YouTube) ShowWatermarkFingerprint.also { - it.resolve(context, PlayerOverlayFingerprint.result?.classDef ?: throw PlayerOverlayFingerprint.exception) - }.result?.mutableMethod?.apply { + it.resolve(context, PlayerOverlayFingerprint.resultOrThrow().classDef) + }.resultOrThrow().mutableMethod.apply { val index = implementation!!.instructions.size - 5 removeInstruction(index) @@ -179,7 +200,7 @@ object HideLayoutComponentsPatch : BytecodePatch( move-result p2 """, ) - } ?: throw ShowWatermarkFingerprint.exception + } // endregion } diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/NavigationBarHookPatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/NavigationBarHookPatch.kt new file mode 100644 index 000000000..5b19cdc0a --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/NavigationBarHookPatch.kt @@ -0,0 +1,130 @@ +package app.revanced.patches.youtube.misc.navigation + +import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.extensions.InstructionExtensions.getInstructions +import app.revanced.patcher.patch.BytecodePatch +import app.revanced.patcher.patch.PatchException +import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod +import app.revanced.patches.youtube.misc.integrations.IntegrationsPatch +import app.revanced.patches.youtube.misc.navigation.fingerprints.* +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstruction +import app.revanced.util.resultOrThrow +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction +import com.android.tools.smali.dexlib2.iface.instruction.Instruction +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.MethodReference +import com.android.tools.smali.dexlib2.util.MethodUtil + +@Patch( + description = "Hooks the active navigation or search bar.", + dependencies = [ + IntegrationsPatch::class, + NavigationBarHookResourcePatch::class, + ], +) +@Suppress("unused") +object NavigationBarHookPatch : BytecodePatch( + setOf( + PivotBarConstructorFingerprint, + NavigationEnumFingerprint, + PivotBarButtonsCreateDrawableViewFingerprint, + PivotBarButtonsCreateResourceViewFingerprint, + NavigationBarHookCallbackFingerprint, + ActionBarSearchResultsFingerprint, + ), +) { + internal const val INTEGRATIONS_CLASS_DESCRIPTOR = + "Lapp/revanced/integrations/youtube/shared/NavigationBar;" + + internal const val INTEGRATIONS_NAVIGATION_BUTTON_DESCRIPTOR = + "Lapp/revanced/integrations/youtube/shared/NavigationBar\$NavigationButton;" + + override fun execute(context: BytecodeContext) { + fun MutableMethod.addHook(hook: Hook, insertPredicate: Instruction.() -> Boolean) { + val filtered = getInstructions().filter(insertPredicate) + if (filtered.isEmpty()) throw PatchException("Could not find insert indexes") + filtered.forEach { + val insertIndex = it.location.index + 2 + val register = getInstruction(insertIndex - 1).registerA + + addInstruction( + insertIndex, + "invoke-static { v$register }, " + + "$INTEGRATIONS_CLASS_DESCRIPTOR->${hook.methodName}(${hook.parameters})V", + ) + } + } + + InitializeButtonsFingerprint.apply { + resolve(context, PivotBarConstructorFingerprint.resultOrThrow().classDef) + }.resultOrThrow().mutableMethod.apply { + // Hook the current navigation bar enum value. Note, the 'You' tab does not have an enum value. + val navigationEnumClassName = NavigationEnumFingerprint.resultOrThrow().mutableClass.type + addHook(Hook.SET_LAST_APP_NAVIGATION_ENUM) { + opcode == Opcode.INVOKE_STATIC && + getReference()?.definingClass == navigationEnumClassName + } + + // Hook the creation of navigation tab views. + val drawableTabMethod = PivotBarButtonsCreateDrawableViewFingerprint.resultOrThrow().mutableMethod + addHook(Hook.NAVIGATION_TAB_LOADED) predicate@{ + MethodUtil.methodSignaturesMatch( + getReference() ?: return@predicate false, + drawableTabMethod, + ) + } + + val imageResourceTabMethod = PivotBarButtonsCreateResourceViewFingerprint.resultOrThrow().method + addHook(Hook.NAVIGATION_IMAGE_RESOURCE_TAB_LOADED) predicate@{ + MethodUtil.methodSignaturesMatch( + getReference() ?: return@predicate false, + imageResourceTabMethod, + ) + } + } + + // Hook the search bar. + + // Two different layouts are used at the hooked code. + // Insert before the first ViewGroup method call after inflating, + // so this works regardless which layout is used. + ActionBarSearchResultsFingerprint.resultOrThrow().mutableMethod.apply { + val instructionIndex = indexOfFirstInstruction { + opcode == Opcode.INVOKE_VIRTUAL && getReference()?.name == "setLayoutDirection" + } + + val viewRegister = getInstruction(instructionIndex).registerC + + addInstruction( + instructionIndex, + "invoke-static { v$viewRegister }, " + + "$INTEGRATIONS_CLASS_DESCRIPTOR->searchBarResultsViewLoaded(Landroid/view/View;)V", + ) + } + } + + val hookNavigationButtonCreated: (String) -> Unit by lazy { + val method = NavigationBarHookCallbackFingerprint.resultOrThrow().mutableMethod + + { integrationsClassDescriptor -> + method.addInstruction( + 0, + "invoke-static { p0, p1 }, " + + "$integrationsClassDescriptor->navigationTabCreated" + + "(${INTEGRATIONS_NAVIGATION_BUTTON_DESCRIPTOR}Landroid/view/View;)V", + ) + } + } + + private enum class Hook(val methodName: String, val parameters: String) { + SET_LAST_APP_NAVIGATION_ENUM("setLastAppNavigationEnum", "Ljava/lang/Enum;"), + NAVIGATION_TAB_LOADED("navigationTabLoaded", "Landroid/view/View;"), + NAVIGATION_IMAGE_RESOURCE_TAB_LOADED("navigationImageResourceTabLoaded", "Landroid/view/View;"), + SEARCH_BAR_RESULTS_VIEW_LOADED("searchBarResultsViewLoaded", "Landroid/view/View;"), + } +} diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/NavigationBarHookResourcePatch.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/NavigationBarHookResourcePatch.kt new file mode 100644 index 000000000..3841f2bfa --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/NavigationBarHookResourcePatch.kt @@ -0,0 +1,24 @@ +package app.revanced.patches.youtube.misc.navigation + +import app.revanced.patcher.data.ResourceContext +import app.revanced.patcher.patch.ResourcePatch +import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch + +@Patch( + dependencies = [ResourceMappingPatch::class] +) +internal object NavigationBarHookResourcePatch : ResourcePatch() { + internal var imageOnlyTabResourceId: Long = -1 + internal var actionBarSearchResultsViewMicId: Long = -1 + + override fun execute(context: ResourceContext) { + imageOnlyTabResourceId = ResourceMappingPatch.resourceMappings.first { + it.type == "layout" && it.name == "image_only_tab" + }.id + + actionBarSearchResultsViewMicId = ResourceMappingPatch.resourceMappings.first { + it.type == "layout" && it.name == "action_bar_search_results_view_mic" + }.id + } +} \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/ActionBarSearchResultsFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/ActionBarSearchResultsFingerprint.kt new file mode 100644 index 000000000..2ba8106f9 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/ActionBarSearchResultsFingerprint.kt @@ -0,0 +1,13 @@ +package app.revanced.patches.youtube.misc.navigation.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patches.youtube.misc.navigation.NavigationBarHookResourcePatch +import app.revanced.util.patch.LiteralValueFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal object ActionBarSearchResultsFingerprint : LiteralValueFingerprint( + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + returnType = "Landroid/view/View;", + parameters = listOf("Landroid/view/LayoutInflater;"), + literalSupplier = { NavigationBarHookResourcePatch.actionBarSearchResultsViewMicId } +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/fingerprints/InitializeButtonsFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/InitializeButtonsFingerprint.kt similarity index 52% rename from src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/fingerprints/InitializeButtonsFingerprint.kt rename to src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/InitializeButtonsFingerprint.kt index f64015c78..f91d1b36f 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/fingerprints/InitializeButtonsFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/InitializeButtonsFingerprint.kt @@ -1,13 +1,16 @@ -package app.revanced.patches.youtube.layout.buttons.navigation.fingerprints +package app.revanced.patches.youtube.misc.navigation.fingerprints import app.revanced.patcher.extensions.or -import app.revanced.patches.youtube.layout.buttons.navigation.ResolvePivotBarFingerprintsPatch +import app.revanced.patches.youtube.misc.navigation.NavigationBarHookResourcePatch import app.revanced.util.patch.LiteralValueFingerprint import com.android.tools.smali.dexlib2.AccessFlags +/** + * Resolves to the class found in [PivotBarConstructorFingerprint]. + */ internal object InitializeButtonsFingerprint : LiteralValueFingerprint( accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, returnType = "V", parameters = listOf(), - literalSupplier = { ResolvePivotBarFingerprintsPatch.imageOnlyTabResourceId } + literalSupplier = { NavigationBarHookResourcePatch.imageOnlyTabResourceId } ) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/NavigationBarHookCallbackFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/NavigationBarHookCallbackFingerprint.kt new file mode 100644 index 000000000..1d69dfe89 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/NavigationBarHookCallbackFingerprint.kt @@ -0,0 +1,21 @@ +package app.revanced.patches.youtube.misc.navigation.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint +import app.revanced.patches.youtube.layout.buttons.navigation.NavigationButtonsPatch +import app.revanced.patches.youtube.misc.navigation.NavigationBarHookPatch +import com.android.tools.smali.dexlib2.AccessFlags + +/** + * Integrations method, used for callback into to other patches. + * Specifically, [NavigationButtonsPatch]. + */ +internal object NavigationBarHookCallbackFingerprint : MethodFingerprint( + accessFlags = AccessFlags.PRIVATE or AccessFlags.STATIC, + returnType = "V", + parameters = listOf(NavigationBarHookPatch.INTEGRATIONS_NAVIGATION_BUTTON_DESCRIPTOR, "Landroid/view/View;"), + customFingerprint = { methodDef, _ -> + methodDef.name == "navigationTabCreatedCallback" && + methodDef.definingClass == NavigationBarHookPatch.INTEGRATIONS_CLASS_DESCRIPTOR + }, +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/NavigationEnumFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/NavigationEnumFingerprint.kt new file mode 100644 index 000000000..c084cf5ec --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/NavigationEnumFingerprint.kt @@ -0,0 +1,21 @@ +package app.revanced.patches.youtube.misc.navigation.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +/** + * Resolves to the Enum class that looks up ordinal -> instance. + */ +internal object NavigationEnumFingerprint : MethodFingerprint( + accessFlags = AccessFlags.STATIC or AccessFlags.CONSTRUCTOR, + strings = listOf( + "PIVOT_HOME", + "TAB_SHORTS", + "CREATION_TAB_LARGE", + "PIVOT_SUBSCRIPTIONS", + "TAB_ACTIVITY", + "VIDEO_LIBRARY_WHITE", + "INCOGNITO_CIRCLE" + ) +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/PivotBarButtonsCreateDrawableViewFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/PivotBarButtonsCreateDrawableViewFingerprint.kt new file mode 100644 index 000000000..0192f6e3d --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/PivotBarButtonsCreateDrawableViewFingerprint.kt @@ -0,0 +1,17 @@ +package app.revanced.patches.youtube.misc.navigation.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal object PivotBarButtonsCreateDrawableViewFingerprint : MethodFingerprint( + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + // Method has different number of parameters in some app targets. + // Parameters are checked in custom fingerprint. + returnType = "Landroid/view/View;", + customFingerprint = { methodDef, classDef -> + classDef.type == "Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;" && + // Only one method has a Drawable parameter. + methodDef.parameterTypes.firstOrNull() == "Landroid/graphics/drawable/Drawable;" + } +) diff --git a/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/PivotBarButtonsCreateResourceViewFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/PivotBarButtonsCreateResourceViewFingerprint.kt new file mode 100644 index 000000000..cef0b517e --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/PivotBarButtonsCreateResourceViewFingerprint.kt @@ -0,0 +1,14 @@ +package app.revanced.patches.youtube.misc.navigation.fingerprints + +import app.revanced.patcher.extensions.or +import app.revanced.patcher.fingerprint.MethodFingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal object PivotBarButtonsCreateResourceViewFingerprint : MethodFingerprint( + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + parameters = listOf("L", "Z", "I", "L"), + returnType = "Landroid/view/View;", + customFingerprint = { _, classDef -> + classDef.type == "Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;" + } +) \ No newline at end of file diff --git a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/fingerprints/PivotBarConstructorFingerprint.kt b/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/PivotBarConstructorFingerprint.kt similarity index 82% rename from src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/fingerprints/PivotBarConstructorFingerprint.kt rename to src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/PivotBarConstructorFingerprint.kt index 37674a2bf..57bc63e4d 100644 --- a/src/main/kotlin/app/revanced/patches/youtube/layout/buttons/navigation/fingerprints/PivotBarConstructorFingerprint.kt +++ b/src/main/kotlin/app/revanced/patches/youtube/misc/navigation/fingerprints/PivotBarConstructorFingerprint.kt @@ -1,4 +1,4 @@ -package app.revanced.patches.youtube.layout.buttons.navigation.fingerprints +package app.revanced.patches.youtube.misc.navigation.fingerprints import app.revanced.patcher.extensions.or import app.revanced.patcher.fingerprint.MethodFingerprint diff --git a/src/main/resources/addresources/values/strings.xml b/src/main/resources/addresources/values/strings.xml index 92dc6e352..ac361a958 100644 --- a/src/main/resources/addresources/values/strings.xml +++ b/src/main/resources/addresources/values/strings.xml @@ -167,9 +167,24 @@ Custom filter is disabled Custom filter List of component path builder strings to filter separated by new line - Invalid custom filter (must be ASCII only): %s Invalid custom filter: %s - Custom filter reset to default + Hide keyword content + Hide search and feed videos using keyword filters + Hide home videos by keywords + Videos in the home tab are filtered by keywords + Videos in the home tab are not filtered by keywords + Hide subscription videos by keywordsx + Videos in the subscriptions tab are filtered by keywords + Videos in the subscriptions tab are not filtered by keywords + Hide search results by keywords + Search results are filtered by keywords + Search results are not filtered by keywords + Keywords to hide + Keywords and phrases to hide, separated by new lines\n\nWords with uppercase letters in the middle must be entered with the casing (ie: iPhone, TikTok, LeBlanc) + About keyword filtering + Home/Subscription/Search results are filtered to hide content that matches keyword phrases\n\nLimitations\n• Some Shorts may not be hidden\n• Some UI components may not be hidden\n• Searching for a keyword may show no results + Invalid keyword. Cannot use: \'%s\' as a filter + Invalid keyword. \'%s\' is less than %s characters Hide general ads