feat(YouTube/Hide layout components): filter home/search results by keywords

This commit is contained in:
inotia00
2024-04-08 22:18:05 +09:00
parent 017ef705f8
commit 3bb394643e
16 changed files with 274 additions and 136 deletions

View File

@ -21,13 +21,16 @@ object LayoutComponentsPatch : BaseBytecodePatch(
"$COMPONENTS_PATH/ChannelBarFilter;"
private const val CUSTOM_FILTER_CLASS_DESCRIPTOR =
"$COMPONENTS_PATH/CustomFilter;"
private const val FILTER_CLASS_DESCRIPTOR =
private const val LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR =
"$COMPONENTS_PATH/LayoutComponentsFilter;"
private const val KEYWORD_FILTER_CLASS_NAME =
"$COMPONENTS_PATH/KeywordContentFilter;"
override fun execute(context: BytecodeContext) {
LithoFilterPatch.addFilter(CHANNEL_BAR_FILTER_CLASS_DESCRIPTOR)
LithoFilterPatch.addFilter(CUSTOM_FILTER_CLASS_DESCRIPTOR)
LithoFilterPatch.addFilter(FILTER_CLASS_DESCRIPTOR)
LithoFilterPatch.addFilter(LAYOUT_COMPONENTS_FILTER_CLASS_DESCRIPTOR)
LithoFilterPatch.addFilter(KEYWORD_FILTER_CLASS_NAME)
/**
* Add settings

View File

@ -1,26 +1,16 @@
package app.revanced.patches.youtube.navigation.navigationbuttons
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.youtube.navigation.navigationbuttons.fingerprints.AutoMotiveFingerprint
import app.revanced.patches.youtube.navigation.navigationbuttons.fingerprints.PivotBarButtonViewFingerprint
import app.revanced.patches.youtube.navigation.navigationbuttons.fingerprints.PivotBarEnumFingerprint
import app.revanced.patches.youtube.utils.fingerprints.PivotBarCreateButtonViewFingerprint
import app.revanced.patches.youtube.utils.integrations.Constants.COMPATIBLE_PACKAGE
import app.revanced.patches.youtube.utils.integrations.Constants.NAVIGATION_CLASS_DESCRIPTOR
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.ImageOnlyTab
import app.revanced.patches.youtube.utils.navigation.NavigationBarHookPatch
import app.revanced.patches.youtube.utils.settings.SettingsPatch
import app.revanced.util.getStringInstructionIndex
import app.revanced.util.getTargetIndex
import app.revanced.util.getWideLiteralInstructionIndex
import app.revanced.util.patch.BaseBytecodePatch
import app.revanced.util.resultOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.Opcode.MOVE_RESULT_OBJECT
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@Suppress("unused")
@ -29,60 +19,13 @@ object NavigationButtonsPatch : BaseBytecodePatch(
description = "Adds options to hide and change navigation buttons (such as the Shorts button).",
dependencies = setOf(
SettingsPatch::class,
SharedResourceIdPatch::class
NavigationBarHookPatch::class
),
compatiblePackages = COMPATIBLE_PACKAGE,
fingerprints = setOf(
AutoMotiveFingerprint,
PivotBarCreateButtonViewFingerprint
)
fingerprints = setOf(AutoMotiveFingerprint)
) {
override fun execute(context: BytecodeContext) {
PivotBarCreateButtonViewFingerprint.resultOrThrow().let { parentResult ->
/**
* Home, Shorts, Subscriptions Button
*/
with(
arrayOf(
PivotBarEnumFingerprint,
PivotBarButtonViewFingerprint
).onEach {
it.resolve(
context,
parentResult.mutableMethod,
parentResult.mutableClass
)
}.map {
it.resultOrThrow().scanResult.patternScanResult!!
}
) {
val enumScanResult = this[0]
val buttonViewResult = this[1]
val enumHookInsertIndex = enumScanResult.startIndex + 2
val buttonHookInsertIndex = buttonViewResult.endIndex
mapOf(
BUTTON_HOOK to buttonHookInsertIndex,
ENUM_HOOK to enumHookInsertIndex
).forEach { (hook, insertIndex) ->
parentResult.mutableMethod.injectHook(hook, insertIndex)
}
}
/**
* Create Button
*/
parentResult.mutableMethod.apply {
val constIndex = getWideLiteralInstructionIndex(ImageOnlyTab)
val insertIndex = getTargetIndex(constIndex, Opcode.INVOKE_VIRTUAL) + 2
injectHook(CREATE_BUTTON_HOOK, insertIndex)
}
}
/**
* Switch create button with notifications button
*/
@ -93,13 +36,17 @@ object NavigationButtonsPatch : BaseBytecodePatch(
addInstructions(
insertIndex, """
invoke-static {v$register}, $NAVIGATION_CLASS_DESCRIPTOR->switchCreateNotification(Z)Z
invoke-static {v$register}, $NAVIGATION_CLASS_DESCRIPTOR->switchCreateWithNotificationButton(Z)Z
move-result v$register
"""
)
}
}
// Hook navigation button created, in order to hide them.
NavigationBarHookPatch.hookNavigationButtonCreated(NAVIGATION_CLASS_DESCRIPTOR)
/**
* Add settings
*/
@ -113,40 +60,4 @@ object NavigationButtonsPatch : BaseBytecodePatch(
SettingsPatch.updatePatchStatus("Hide navigation buttons")
}
private const val REGISTER_TEMPLATE_REPLACEMENT: String = "REGISTER_INDEX"
private const val ENUM_HOOK =
"sput-object v$REGISTER_TEMPLATE_REPLACEMENT, $NAVIGATION_CLASS_DESCRIPTOR" +
"->" +
"lastPivotTab:Ljava/lang/Enum;"
private const val BUTTON_HOOK =
"invoke-static { v$REGISTER_TEMPLATE_REPLACEMENT }, $NAVIGATION_CLASS_DESCRIPTOR" +
"->" +
"hideNavigationButton(Landroid/view/View;)V"
private const val CREATE_BUTTON_HOOK =
"invoke-static { v$REGISTER_TEMPLATE_REPLACEMENT }, $NAVIGATION_CLASS_DESCRIPTOR" +
"->" +
"hideCreateButton(Landroid/view/View;)V"
/**
* 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].
*/
private 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<OneRegisterInstruction>(registerIndex).registerA
injectTarget.addInstruction(
insertIndex,
hook.replace("REGISTER_INDEX", register.toString()),
)
}
}

View File

@ -1,12 +0,0 @@
package app.revanced.patches.youtube.navigation.navigationbuttons.fingerprints
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.Opcode
internal object PivotBarButtonViewFingerprint : MethodFingerprint(
opcodes = listOf(
Opcode.INVOKE_VIRTUAL_RANGE,
Opcode.MOVE_RESULT_OBJECT, // target reference
null,
)
)

View File

@ -1,15 +0,0 @@
package app.revanced.patches.youtube.navigation.navigationbuttons.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,
)
)

View File

@ -8,7 +8,7 @@ import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patches.youtube.shorts.components.fingerprints.BottomNavigationBarFingerprint
import app.revanced.patches.youtube.shorts.components.fingerprints.RenderBottomNavigationBarFingerprint
import app.revanced.patches.youtube.shorts.components.fingerprints.SetPivotBarFingerprint
import app.revanced.patches.youtube.utils.fingerprints.PivotBarCreateButtonViewFingerprint
import app.revanced.patches.youtube.utils.fingerprints.InitializeButtonsFingerprint
import app.revanced.patches.youtube.utils.integrations.Constants.SHORTS_CLASS_DESCRIPTOR
import app.revanced.util.getTargetIndexWithMethodReferenceName
import app.revanced.util.getWalkerMethod
@ -18,13 +18,13 @@ import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
object ShortsNavigationBarPatch : BytecodePatch(
setOf(
BottomNavigationBarFingerprint,
PivotBarCreateButtonViewFingerprint,
InitializeButtonsFingerprint,
RenderBottomNavigationBarFingerprint
)
) {
override fun execute(context: BytecodeContext) {
PivotBarCreateButtonViewFingerprint.resultOrThrow().let { parentResult ->
InitializeButtonsFingerprint.resultOrThrow().let { parentResult ->
SetPivotBarFingerprint.also { it.resolve(context, parentResult.classDef) }.resultOrThrow().let {
it.mutableMethod.apply {
val startIndex = it.scanResult.patternScanResult!!.startIndex

View File

@ -5,8 +5,9 @@ import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.Image
import app.revanced.util.fingerprint.LiteralValueFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object PivotBarCreateButtonViewFingerprint : LiteralValueFingerprint(
returnType = "V",
internal object InitializeButtonsFingerprint : LiteralValueFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
returnType = "V",
parameters = emptyList(),
literalSupplier = { ImageOnlyTab }
)

View File

@ -1,12 +1,12 @@
package app.revanced.patches.youtube.utils.flyoutpanel
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
import app.revanced.patcher.extensions.or
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.MutableField.Companion.toMutable
import app.revanced.patches.youtube.utils.flyoutpanel.fingerprints.PlaybackRateBottomSheetClassFingerprint
import app.revanced.patches.youtube.utils.integrations.Constants.INTEGRATIONS_PATH

View File

@ -0,0 +1,138 @@
package app.revanced.patches.youtube.utils.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.utils.fingerprints.InitializeButtonsFingerprint
import app.revanced.patches.youtube.utils.integrations.Constants.INTEGRATIONS_PATH
import app.revanced.patches.youtube.utils.navigation.fingerprints.ActionBarSearchResultsFingerprint
import app.revanced.patches.youtube.utils.navigation.fingerprints.NavigationEnumFingerprint
import app.revanced.patches.youtube.utils.navigation.fingerprints.PivotBarButtonsCreateDrawableViewFingerprint
import app.revanced.patches.youtube.utils.navigation.fingerprints.PivotBarButtonsCreateResourceViewFingerprint
import app.revanced.patches.youtube.utils.navigation.fingerprints.PivotBarConstructorFingerprint
import app.revanced.patches.youtube.utils.playertype.PlayerTypeHookPatch
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch
import app.revanced.util.getReference
import app.revanced.util.getTargetIndexWithMethodReferenceName
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 = [
PlayerTypeHookPatch::class,
SharedResourceIdPatch::class
],
)
@Suppress("unused")
object NavigationBarHookPatch : BytecodePatch(
setOf(
ActionBarSearchResultsFingerprint,
NavigationEnumFingerprint,
PivotBarButtonsCreateDrawableViewFingerprint,
PivotBarButtonsCreateResourceViewFingerprint,
PivotBarConstructorFingerprint
),
) {
internal const val INTEGRATIONS_CLASS_DESCRIPTOR =
"$INTEGRATIONS_PATH/shared/NavigationBar;"
private const val INTEGRATIONS_NAVIGATION_BUTTON_DESCRIPTOR =
"$INTEGRATIONS_PATH/shared/NavigationBar\$NavigationButton;"
private lateinit var navigationTabCreatedCallback: MutableMethod
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<OneRegisterInstruction>(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<MethodReference>()?.definingClass == navigationEnumClassName
}
// Hook the creation of navigation tab views.
val drawableTabMethod = PivotBarButtonsCreateDrawableViewFingerprint.resultOrThrow().mutableMethod
addHook(Hook.NAVIGATION_TAB_LOADED) predicate@{
MethodUtil.methodSignaturesMatch(
getReference<MethodReference>() ?: return@predicate false,
drawableTabMethod,
)
}
val imageResourceTabMethod = PivotBarButtonsCreateResourceViewFingerprint.resultOrThrow().method
addHook(Hook.NAVIGATION_IMAGE_RESOURCE_TAB_LOADED) predicate@{
MethodUtil.methodSignaturesMatch(
getReference<MethodReference>() ?: 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 = getTargetIndexWithMethodReferenceName("setLayoutDirection")
val viewRegister = getInstruction<FiveRegisterInstruction>(instructionIndex).registerC
addInstruction(
instructionIndex,
"invoke-static { v$viewRegister }, " +
"$INTEGRATIONS_CLASS_DESCRIPTOR->searchBarResultsViewLoaded(Landroid/view/View;)V",
)
}
navigationTabCreatedCallback = context.findClass(INTEGRATIONS_CLASS_DESCRIPTOR)?.mutableClass?.methods?.first { method ->
method.name == "navigationTabCreatedCallback"
} ?: throw PatchException("Could not find navigationTabCreatedCallback method")
}
val hookNavigationButtonCreated: (String) -> Unit by lazy {
navigationTabCreatedCallback
{ integrationsClassDescriptor ->
navigationTabCreatedCallback.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;"),
}
}

View File

@ -0,0 +1,13 @@
package app.revanced.patches.youtube.utils.navigation.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patches.youtube.utils.resourceid.SharedResourceIdPatch.ActionBarSearchResultsViewMic
import app.revanced.util.fingerprint.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 = { ActionBarSearchResultsViewMic }
)

View File

@ -0,0 +1,21 @@
package app.revanced.patches.youtube.utils.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"
)
)

View File

@ -0,0 +1,17 @@
package app.revanced.patches.youtube.utils.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;"
}
)

View File

@ -0,0 +1,14 @@
package app.revanced.patches.youtube.utils.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;"
}
)

View File

@ -0,0 +1,10 @@
package app.revanced.patches.youtube.utils.navigation.fingerprints
import app.revanced.patcher.extensions.or
import app.revanced.patcher.fingerprint.MethodFingerprint
import com.android.tools.smali.dexlib2.AccessFlags
internal object PivotBarConstructorFingerprint : MethodFingerprint(
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
strings = listOf("com.google.android.apps.youtube.app.endpoint.flags")
)

View File

@ -18,6 +18,7 @@ import app.revanced.patches.shared.mapping.ResourceType.STYLE
object SharedResourceIdPatch : ResourcePatch() {
var AccountSwitcherAccessibility = -1L
var ActionBarRingo = -1L
var ActionBarSearchResultsViewMic = -1L
var AdAttribution = -1L
var Appearance = -1L
var AppRelatedEndScreenResults = -1L
@ -92,6 +93,7 @@ object SharedResourceIdPatch : ResourcePatch() {
AccountSwitcherAccessibility = getId(STRING, "account_switcher_accessibility_label")
ActionBarRingo = getId(LAYOUT, "action_bar_ringo")
ActionBarSearchResultsViewMic = getId(LAYOUT, "action_bar_search_results_view_mic")
AdAttribution = getId(ID, "ad_attribution")
Appearance = getId(STRING, "app_theme_appearance_dark")
AppRelatedEndScreenResults = getId(LAYOUT, "app_related_endscreen_results")