From 0cb38f9f367a7fe742d8ca336150049181d637b6 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Sat, 19 Apr 2025 18:33:12 +0200 Subject: [PATCH] fix(YouTube - Wide search bar): Do not force phone layout for tablet devices (#4827) --- .../youtube/patches/WideSearchbarPatch.java | 39 ++++++- .../layout/hide/general/Fingerprints.kt | 4 + .../hide/general/HideLayoutComponentsPatch.kt | 2 - .../youtube/layout/searchbar/Fingerprints.kt | 38 +++--- .../layout/searchbar/WideSearchbarPatch.kt | 110 +++++++++++++----- 5 files changed, 137 insertions(+), 56 deletions(-) diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/WideSearchbarPatch.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/WideSearchbarPatch.java index 57ec7442f..aeff4ac26 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/WideSearchbarPatch.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/patches/WideSearchbarPatch.java @@ -1,11 +1,48 @@ package app.revanced.extension.youtube.patches; +import android.content.res.Resources; +import android.util.TypedValue; +import android.view.View; + +import app.revanced.extension.shared.Logger; +import app.revanced.extension.shared.Utils; import app.revanced.extension.youtube.settings.Settings; @SuppressWarnings("unused") public final class WideSearchbarPatch { + private static final Boolean WIDE_SEARCHBAR_ENABLED = Settings.WIDE_SEARCHBAR.get(); + + /** + * Injection point. + */ public static boolean enableWideSearchbar(boolean original) { - return Settings.WIDE_SEARCHBAR.get() || original; + return WIDE_SEARCHBAR_ENABLED || original; + } + + /** + * Injection point. + */ + public static void setActionBar(View view) { + try { + if (!WIDE_SEARCHBAR_ENABLED) return; + + View searchBarView = Utils.getChildViewByResourceName(view, "search_bar"); + + final int paddingLeft = searchBarView.getPaddingLeft(); + final int paddingRight = searchBarView.getPaddingRight(); + final int paddingTop = searchBarView.getPaddingTop(); + final int paddingBottom = searchBarView.getPaddingBottom(); + final int paddingStart = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, + 8, Resources.getSystem().getDisplayMetrics()); + + if (Utils.isRightToLeftTextLayout()) { + searchBarView.setPadding(paddingLeft, paddingTop, paddingStart, paddingBottom); + } else { + searchBarView.setPadding(paddingStart, paddingTop, paddingRight, paddingBottom); + } + } catch (Exception ex) { + Logger.printException(() -> "setActionBar failure", ex); + } } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/Fingerprints.kt index 3b7ec91fa..e83689607 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/Fingerprints.kt @@ -1,6 +1,7 @@ package app.revanced.patches.youtube.layout.hide.general import app.revanced.patcher.fingerprint +import app.revanced.patches.youtube.layout.searchbar.wideSearchbarLayoutFingerprint import app.revanced.util.literal import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode @@ -67,6 +68,9 @@ internal val showWatermarkFingerprint = fingerprint { parameters("L", "L") } +/** + * Matches same method as [wideSearchbarLayoutFingerprint]. + */ internal val yoodlesImageViewFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("Landroid/view/View;") diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt index 4e63a058e..59d05ef5e 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/hide/general/HideLayoutComponentsPatch.kt @@ -44,14 +44,12 @@ var crowdfundingBoxId = -1L private set var youTubeLogo = -1L private set - var filterBarHeightId = -1L private set var relatedChipCloudMarginId = -1L private set var barContainerHeightId = -1L private set - var fabButtonId = -1L private set diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/searchbar/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/searchbar/Fingerprints.kt index 69fc26dae..e1b8bfcbf 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/searchbar/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/searchbar/Fingerprints.kt @@ -1,31 +1,27 @@ package app.revanced.patches.youtube.layout.searchbar import app.revanced.patcher.fingerprint +import app.revanced.patches.youtube.layout.hide.general.yoodlesImageViewFingerprint +import app.revanced.util.containsLiteralInstruction +import app.revanced.util.literal import com.android.tools.smali.dexlib2.AccessFlags -import com.android.tools.smali.dexlib2.Opcode - -internal val createSearchSuggestionsFingerprint = fingerprint { - opcodes( - Opcode.INVOKE_STATIC, - Opcode.MOVE_RESULT, - Opcode.CONST_4, - ) - strings("ss_rds") -} internal val setWordmarkHeaderFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) returns("V") parameters("Landroid/widget/ImageView;") - opcodes( - Opcode.IGET_OBJECT, - Opcode.INVOKE_STATIC, - Opcode.MOVE_RESULT, - Opcode.IF_NEZ, - Opcode.IGET_BOOLEAN, - Opcode.IF_EQZ, - Opcode.IGET_OBJECT, - Opcode.CONST, - null, // invoke-static or invoke-virtual. - ) + custom { methodDef, _ -> + methodDef.containsLiteralInstruction(ytWordmarkHeaderId) && + methodDef.containsLiteralInstruction(ytPremiumWordmarkHeaderId) + } +} + +/** + * Matches the same method as [yoodlesImageViewFingerprint]. + */ +internal val wideSearchbarLayoutFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Landroid/view/View;") + parameters("L", "L") + literal { actionBarRingoId } } diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/searchbar/WideSearchbarPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/searchbar/WideSearchbarPatch.kt index 9df1c00eb..89920b53c 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/searchbar/WideSearchbarPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/searchbar/WideSearchbarPatch.kt @@ -1,30 +1,68 @@ package app.revanced.patches.youtube.layout.searchbar -import app.revanced.patcher.Fingerprint -import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction -import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod +import app.revanced.patcher.patch.resourcePatch import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.mapping.get +import app.revanced.patches.shared.misc.mapping.resourceMappingPatch +import app.revanced.patches.shared.misc.mapping.resourceMappings import app.revanced.patches.shared.misc.settings.preference.SwitchPreference import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch import app.revanced.patches.youtube.misc.settings.PreferenceScreen import app.revanced.patches.youtube.misc.settings.settingsPatch +import app.revanced.util.addInstructionsAtControlFlowLabel +import app.revanced.util.findInstructionIndicesReversedOrThrow +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstructionOrThrow +import app.revanced.util.indexOfFirstLiteralInstructionOrThrow +import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.reference.MethodReference private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/WideSearchbarPatch;" +internal var ytWordmarkHeaderId = -1L + private set +internal var ytPremiumWordmarkHeaderId = -1L + private set +internal var actionBarRingoId = -1L + private set + +private val wideSearchbarResourcePatch = resourcePatch { + dependsOn(resourceMappingPatch) + + execute { + ytWordmarkHeaderId = resourceMappings[ + "attr", + "ytWordmarkHeader", + ] + + ytPremiumWordmarkHeaderId = resourceMappings[ + "attr", + "ytPremiumWordmarkHeader", + ] + + actionBarRingoId = resourceMappings[ + "layout", + "action_bar_ringo_background", + ] + } +} + val wideSearchbarPatch = bytecodePatch( name = "Wide search bar", - description = "Adds an option to replace the search icon with a wide search bar. This will hide the YouTube logo when active.", + description = "Adds an option to replace the search icon with a wide search bar. " + + "This will hide the YouTube logo when active.", ) { dependsOn( sharedExtensionPatch, settingsPatch, addResourcesPatch, + wideSearchbarResourcePatch, ) compatibleWith( @@ -46,37 +84,45 @@ val wideSearchbarPatch = bytecodePatch( SwitchPreference("revanced_wide_searchbar"), ) - /** - * Navigate a fingerprints method at a given index mutably. - * - * @param index The index to navigate to. - * @param from The fingerprint to navigate the method on. - * @return The [MutableMethod] which was navigated on. - */ - fun BytecodePatchContext.walkMutable(index: Int, from: Fingerprint) = - navigate(from.originalMethod).to(index).stop() + setWordmarkHeaderFingerprint.let { + // Navigate to the method that checks if the YT logo is shown beside the search bar. + val shouldShowLogoMethod = with(it.originalMethod) { + val invokeStaticIndex = indexOfFirstInstructionOrThrow { + opcode == Opcode.INVOKE_STATIC && + getReference()?.returnType == "Z" + } + navigate(this).to(invokeStaticIndex).stop() + } - /** - * Injects instructions required for certain methods. - */ - fun MutableMethod.injectSearchBarHook() { - val insertIndex = implementation!!.instructions.size - 1 - val insertRegister = getInstruction(insertIndex).registerA + shouldShowLogoMethod.apply { + findInstructionIndicesReversedOrThrow(Opcode.RETURN).forEach { index -> + val register = getInstruction(index).registerA - addInstructions( - insertIndex, - """ - invoke-static {v$insertRegister}, $EXTENSION_CLASS_DESCRIPTOR->enableWideSearchbar(Z)Z - move-result v$insertRegister - """, - ) + addInstructionsAtControlFlowLabel( + index, + """ + invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->enableWideSearchbar(Z)Z + move-result v$register + """ + ) + } + } } - mapOf( - setWordmarkHeaderFingerprint to 1, - createSearchSuggestionsFingerprint to createSearchSuggestionsFingerprint.patternMatch!!.startIndex, - ).forEach { (fingerprint, callIndex) -> - walkMutable(callIndex, fingerprint).injectSearchBarHook() + // Fix missing left padding when using wide searchbar. + wideSearchbarLayoutFingerprint.method.apply { + val layoutIndex = indexOfFirstLiteralInstructionOrThrow(actionBarRingoId) + val inflateIndex = indexOfFirstInstructionOrThrow(layoutIndex) { + val reference = getReference() + reference?.definingClass == "Landroid/view/LayoutInflater;" + && reference.name == "inflate" + } + val register = getInstruction(inflateIndex + 1).registerA + + addInstruction( + inflateIndex + 2, + "invoke-static { v$register }, $EXTENSION_CLASS_DESCRIPTOR->setActionBar(Landroid/view/View;)V" + ) } } }