feat(youtube/hide-shorts-components): hide navigation bar

This commit is contained in:
oSumAtrIX 2023-05-10 02:14:26 +02:00
parent 64868f41be
commit 24f376a4b8
No known key found for this signature in database
GPG Key ID: A9B3094ACDB604B4
8 changed files with 167 additions and 23 deletions

View File

@ -6,7 +6,10 @@ import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import app.revanced.patcher.patch.PatchResultError import app.revanced.patcher.patch.PatchResultError
import app.revanced.patcher.util.proxy.mutableTypes.MutableClass import app.revanced.patcher.util.proxy.mutableTypes.MutableClass
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.shared.mapping.misc.patch.ResourceMappingPatch
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.iface.Method import org.jf.dexlib2.iface.Method
import org.jf.dexlib2.iface.instruction.WideLiteralInstruction
import org.jf.dexlib2.util.MethodUtil import org.jf.dexlib2.util.MethodUtil
import org.w3c.dom.Node import org.w3c.dom.Node
@ -16,7 +19,7 @@ import org.w3c.dom.Node
* *
* @return A [PatchResultError] for the [MethodFingerprint]. * @return A [PatchResultError] for the [MethodFingerprint].
*/ */
fun MethodFingerprint.toErrorResult() = PatchResultError("Failed to resolve $name") internal fun MethodFingerprint.toErrorResult() = PatchResultError("Failed to resolve $name")
/** /**
* Find the [MutableMethod] from a given [Method] in a [MutableClass]. * Find the [MutableMethod] from a given [Method] in a [MutableClass].
@ -24,7 +27,7 @@ fun MethodFingerprint.toErrorResult() = PatchResultError("Failed to resolve $nam
* @param method The [Method] to find. * @param method The [Method] to find.
* @return The [MutableMethod]. * @return The [MutableMethod].
*/ */
fun MutableClass.findMutableMethodOf(method: Method) = this.methods.first { internal fun MutableClass.findMutableMethodOf(method: Method) = this.methods.first {
MethodUtil.methodSignaturesMatch(it, method) MethodUtil.methodSignaturesMatch(it, method)
} }
@ -33,7 +36,7 @@ fun MutableClass.findMutableMethodOf(method: Method) = this.methods.first {
* *
* @param transform the transformation function. original method goes in, transformed method goes out * @param transform the transformation function. original method goes in, transformed method goes out
*/ */
fun MutableClass.transformMethods(transform: MutableMethod.() -> MutableMethod) { internal fun MutableClass.transformMethods(transform: MutableMethod.() -> MutableMethod) {
val transformedMethods = methods.map { it.transform() } val transformedMethods = methods.map { it.transform() }
methods.clear() methods.clear()
methods.addAll(transformedMethods) methods.addAll(transformedMethods)
@ -44,7 +47,7 @@ internal fun Node.doRecursively(action: (Node) -> Unit) {
for (i in 0 until this.childNodes.length) this.childNodes.item(i).doRecursively(action) for (i in 0 until this.childNodes.length) this.childNodes.item(i).doRecursively(action)
} }
fun MutableMethod.injectHideViewCall( internal fun MutableMethod.injectHideViewCall(
insertIndex: Int, insertIndex: Int,
viewRegister: Int, viewRegister: Int,
classDescriptor: String, classDescriptor: String,
@ -53,3 +56,18 @@ fun MutableMethod.injectHideViewCall(
insertIndex, insertIndex,
"invoke-static { v$viewRegister }, $classDescriptor->$targetMethod(Landroid/view/View;)V" "invoke-static { v$viewRegister }, $classDescriptor->$targetMethod(Landroid/view/View;)V"
) )
internal fun MutableMethod.findIndexForIdResource(resourceName: String): Int {
fun getIdResourceId(resourceName: String) = ResourceMappingPatch.resourceMappings.single {
it.type == "id" && it.name == resourceName
}.id
val resourceId = getIdResourceId(resourceName)
return implementation!!.instructions.indexOfFirst {
if (it.opcode != Opcode.CONST) return@indexOfFirst false
val literal = (it as WideLiteralInstruction).wideLiteral
return@indexOfFirst resourceId == literal
}
}

View File

@ -0,0 +1,18 @@
package app.revanced.patches.youtube.layout.hide.shorts.bytecode.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.Opcode
object BottomNavigationBarFingerprint : MethodFingerprint(
opcodes = listOf(
Opcode.MOVE_RESULT_OBJECT, // Refers to bottom navigation bar
Opcode.IF_EQZ,
Opcode.IGET_OBJECT,
Opcode.IGET_OBJECT,
Opcode.IGET_OBJECT,
),
strings = listOf(
"navigation_endpoint_interaction_logging_extension",
"reel_watch_fragment_watch_while",
),
)

View File

@ -0,0 +1,19 @@
package app.revanced.patches.youtube.layout.hide.shorts.bytecode.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.Opcode
object RenderBottomNavigationBarFingerprint : MethodFingerprint(
opcodes = listOf(
Opcode.IGET_OBJECT,
Opcode.MONITOR_ENTER,
Opcode.IGET_OBJECT,
Opcode.IF_EQZ,
Opcode.INVOKE_INTERFACE,
Opcode.MONITOR_EXIT,
Opcode.RETURN_VOID,
Opcode.MOVE_EXCEPTION,
Opcode.MONITOR_EXIT,
Opcode.THROW,
)
)

View File

@ -0,0 +1,8 @@
package app.revanced.patches.youtube.layout.hide.shorts.bytecode.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
object RenderBottomNavigationBarParentFingerprint : MethodFingerprint(
parameters = listOf("I", "I", "L", "L", "J", "L"),
strings = listOf("aa")
)

View File

@ -0,0 +1,19 @@
package app.revanced.patches.youtube.layout.hide.shorts.bytecode.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
import org.jf.dexlib2.Opcode
object SetPivotBarVisibilityFingerprint : MethodFingerprint(
parameters = listOf("Z"),
opcodes = listOf(
Opcode.IGET_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT,
Opcode.IF_EQZ,
Opcode.RETURN_VOID,
Opcode.IGET_OBJECT,
Opcode.INVOKE_VIRTUAL,
Opcode.MOVE_RESULT_OBJECT,
Opcode.CHECK_CAST,
)
)

View File

@ -0,0 +1,8 @@
package app.revanced.patches.youtube.layout.hide.shorts.bytecode.fingerprints
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
object SetPivotBarVisibilityParentFingerprint : MethodFingerprint(
parameters = listOf("Z"),
strings = listOf("FEnotifications_inbox")
)

View File

@ -1,12 +1,15 @@
package app.revanced.patches.youtube.layout.hide.shorts.bytecode.patch package app.revanced.patches.youtube.layout.hide.shorts.bytecode.patch
import app.revanced.extensions.findIndexForIdResource
import app.revanced.extensions.injectHideViewCall import app.revanced.extensions.injectHideViewCall
import app.revanced.extensions.toErrorResult import app.revanced.extensions.toErrorResult
import app.revanced.patcher.annotation.Description import app.revanced.patcher.annotation.Description
import app.revanced.patcher.annotation.Name import app.revanced.patcher.annotation.Name
import app.revanced.patcher.annotation.Version import app.revanced.patcher.annotation.Version
import app.revanced.patcher.data.BytecodeContext import app.revanced.patcher.data.BytecodeContext
import app.revanced.patcher.extensions.addInstruction
import app.revanced.patcher.extensions.instruction import app.revanced.patcher.extensions.instruction
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint.Companion.resolve
import app.revanced.patcher.patch.BytecodePatch import app.revanced.patcher.patch.BytecodePatch
import app.revanced.patcher.patch.PatchResult import app.revanced.patcher.patch.PatchResult
import app.revanced.patcher.patch.PatchResultSuccess import app.revanced.patcher.patch.PatchResultSuccess
@ -15,15 +18,13 @@ import app.revanced.patcher.patch.annotations.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.shared.mapping.misc.patch.ResourceMappingPatch import app.revanced.patches.shared.mapping.misc.patch.ResourceMappingPatch
import app.revanced.patches.youtube.layout.hide.shorts.annotations.HideShortsComponentsCompatibility import app.revanced.patches.youtube.layout.hide.shorts.annotations.HideShortsComponentsCompatibility
import app.revanced.patches.youtube.layout.hide.shorts.bytecode.fingerprints.CreateShortsButtonsFingerprint import app.revanced.patches.youtube.layout.hide.shorts.bytecode.fingerprints.*
import app.revanced.patches.youtube.layout.hide.shorts.bytecode.fingerprints.ReelConstructorFingerprint
import app.revanced.patches.youtube.layout.hide.shorts.resource.patch.HideShortsComponentsResourcePatch import app.revanced.patches.youtube.layout.hide.shorts.resource.patch.HideShortsComponentsResourcePatch
import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch import app.revanced.patches.youtube.misc.integrations.patch.IntegrationsPatch
import app.revanced.patches.youtube.misc.litho.filter.patch.LithoFilterPatch import app.revanced.patches.youtube.misc.litho.filter.patch.LithoFilterPatch
import org.jf.dexlib2.Opcode
import org.jf.dexlib2.iface.instruction.FiveRegisterInstruction import org.jf.dexlib2.iface.instruction.FiveRegisterInstruction
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction
import org.jf.dexlib2.iface.instruction.TwoRegisterInstruction import org.jf.dexlib2.iface.instruction.TwoRegisterInstruction
import org.jf.dexlib2.iface.instruction.WideLiteralInstruction
@Patch @Patch
@DependsOn( @DependsOn(
@ -38,9 +39,18 @@ import org.jf.dexlib2.iface.instruction.WideLiteralInstruction
@Description("Hides components from YouTube Shorts.") @Description("Hides components from YouTube Shorts.")
@HideShortsComponentsCompatibility @HideShortsComponentsCompatibility
@Version("0.0.1") @Version("0.0.1")
class HideShortsComponentsPatch : BytecodePatch(listOf(CreateShortsButtonsFingerprint, ReelConstructorFingerprint)) { class HideShortsComponentsPatch : BytecodePatch(
listOf(
CreateShortsButtonsFingerprint,
ReelConstructorFingerprint,
BottomNavigationBarFingerprint,
RenderBottomNavigationBarParentFingerprint,
SetPivotBarVisibilityParentFingerprint
)
) {
override fun execute(context: BytecodeContext): PatchResult { override fun execute(context: BytecodeContext): PatchResult {
// Hide the Shorts shelf. // region Hide the Shorts shelf.
ReelConstructorFingerprint.result?.let { ReelConstructorFingerprint.result?.let {
it.mutableMethod.apply { it.mutableMethod.apply {
val insertIndex = it.scanResult.patternScanResult!!.startIndex + 2 val insertIndex = it.scanResult.patternScanResult!!.startIndex + 2
@ -55,10 +65,64 @@ class HideShortsComponentsPatch : BytecodePatch(listOf(CreateShortsButtonsFinger
} }
} ?: return ReelConstructorFingerprint.toErrorResult() } ?: return ReelConstructorFingerprint.toErrorResult()
// endregion
// region Hide the Shorts buttons.
// Some Shorts buttons are views, hide them by setting their visibility to GONE. // Some Shorts buttons are views, hide them by setting their visibility to GONE.
CreateShortsButtonsFingerprint.result?.let { CreateShortsButtonsFingerprint.result?.let {
ShortsButtons.values().forEach { button -> button.injectHideCall(it.mutableMethod) } ShortsButtons.values().forEach { button -> button.injectHideCall(it.mutableMethod) }
} ?: return CreateShortsButtonsFingerprint.toErrorResult() } ?: return CreateShortsButtonsFingerprint.toErrorResult()
// endregion
// region Hide the navigation bar.
// Hook to get the pivotBar view.
SetPivotBarVisibilityParentFingerprint.result?.let {
if (!SetPivotBarVisibilityFingerprint.resolve(context, it.classDef))
throw SetPivotBarVisibilityFingerprint.toErrorResult()
SetPivotBarVisibilityFingerprint.result!!.let { result ->
result.mutableMethod.apply {
val checkCastIndex = result.scanResult.patternScanResult!!.endIndex
val viewRegister = instruction<OneRegisterInstruction>(checkCastIndex).registerA
addInstruction(
checkCastIndex + 1,
"sput-object v$viewRegister, $CLASS_DESCRIPTOR->pivotBar:" +
"Lcom/google/android/libraries/youtube/rendering/ui/pivotbar/PivotBar;"
)
}
}
} ?: return SetPivotBarVisibilityParentFingerprint.toErrorResult()
// Hook to hide the navigation bar when Shorts are being played.
RenderBottomNavigationBarParentFingerprint.result?.let {
if (!RenderBottomNavigationBarFingerprint.resolve(context, it.classDef))
throw RenderBottomNavigationBarFingerprint.toErrorResult()
RenderBottomNavigationBarFingerprint.result!!.mutableMethod.apply {
addInstruction(0, "invoke-static { }, $CLASS_DESCRIPTOR->hideNavigationBar()V")
}
} ?: return RenderBottomNavigationBarParentFingerprint.toErrorResult()
// Required to prevent a black bar from appearing at the bottom of the screen.
BottomNavigationBarFingerprint.result?.let {
it.mutableMethod.apply {
val moveResultIndex = it.scanResult.patternScanResult!!.startIndex
val viewRegister = instruction<OneRegisterInstruction>(moveResultIndex).registerA
val insertIndex = moveResultIndex + 1
addInstruction(
insertIndex,
"invoke-static { v$viewRegister }, $CLASS_DESCRIPTOR->" +
"hideNavigationBar(Landroid/view/View;)Landroid/view/View;"
)
}
} ?: return BottomNavigationBarFingerprint.toErrorResult()
// endregion
return PatchResultSuccess() return PatchResultSuccess()
} }
@ -71,18 +135,7 @@ class HideShortsComponentsPatch : BytecodePatch(listOf(CreateShortsButtonsFinger
SHARE("reel_dyn_share", "hideShortsShareButton"); SHARE("reel_dyn_share", "hideShortsShareButton");
fun injectHideCall(method: MutableMethod) { fun injectHideCall(method: MutableMethod) {
val resourceId = ResourceMappingPatch.resourceMappings.single { val referencedIndex = method.findIndexForIdResource(resourceName)
it.type == "id" && it.name == resourceName
}.id
// Get the index of the reference to the view's resource ID.
val referencedIndex = method.implementation!!.instructions.indexOfFirst {
if (it.opcode != Opcode.CONST) return@indexOfFirst false
val literal = (it as WideLiteralInstruction).wideLiteral
return@indexOfFirst resourceId == literal
}
val setIdIndex = referencedIndex + 1 val setIdIndex = referencedIndex + 1
val viewRegister = method.instruction<FiveRegisterInstruction>(setIdIndex).registerC val viewRegister = method.instruction<FiveRegisterInstruction>(setIdIndex).registerC

View File

@ -44,6 +44,7 @@ class SettingsPatch : BytecodePatch(
ThemeSetterSystemFingerprint.result!!.let { result -> ThemeSetterSystemFingerprint.result!!.let { result ->
val call = buildInvokeInstructionsString() val call = buildInvokeInstructionsString()
result.mutableMethod.apply { result.mutableMethod.apply {
// TODO: The label is pointing to the instruction below, but should instead point to the new instruction.
addInstruction( addInstruction(
result.scanResult.patternScanResult!!.startIndex, call result.scanResult.patternScanResult!!.startIndex, call
) )