Skip to content

Commit

Permalink
fix(YouTube): Show video chapter titles without clipping when overlay…
Browse files Browse the repository at this point in the history
… buttons are enabled (ReVanced#3674)
  • Loading branch information
LisoUseInAIKyrios authored Sep 23, 2024
1 parent f27cbec commit 4b88c31
Show file tree
Hide file tree
Showing 41 changed files with 530 additions and 266 deletions.
15 changes: 12 additions & 3 deletions api/revanced-patches.api
Original file line number Diff line number Diff line change
Expand Up @@ -2000,13 +2000,19 @@ public final class app/revanced/patches/youtube/misc/playercontrols/BottomContro

public final class app/revanced/patches/youtube/misc/playercontrols/PlayerControlsBytecodePatch : app/revanced/patcher/patch/BytecodePatch {
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/playercontrols/PlayerControlsBytecodePatch;
public static field showPlayerControlsFingerprintResult Lapp/revanced/patcher/fingerprint/MethodFingerprintResult;
public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
public final fun getShowPlayerControlsFingerprintResult ()Lapp/revanced/patcher/fingerprint/MethodFingerprintResult;
public final fun initializeBottomControl (Ljava/lang/String;)V
public final fun initializeControl (Ljava/lang/String;)V
public final fun injectVisibilityCheckCall (Ljava/lang/String;)V
public final fun setShowPlayerControlsFingerprintResult (Lapp/revanced/patcher/fingerprint/MethodFingerprintResult;)V
}

public final class app/revanced/patches/youtube/misc/playercontrols/PlayerControlsResourcePatch : app/revanced/patcher/patch/ResourcePatch, java/io/Closeable {
public static final field INSTANCE Lapp/revanced/patches/youtube/misc/playercontrols/PlayerControlsResourcePatch;
public final fun addBottomControls (Ljava/lang/String;)V
public fun close ()V
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
}

public final class app/revanced/patches/youtube/misc/playeroverlay/PlayerOverlaysHookPatch : app/revanced/patcher/patch/BytecodePatch {
Expand Down Expand Up @@ -2174,6 +2180,8 @@ public final class app/revanced/util/BytecodeUtilsKt {
public static synthetic fun indexOfFirstInstructionOrThrow$default (Lcom/android/tools/smali/dexlib2/iface/Method;ILkotlin/jvm/functions/Function1;ILjava/lang/Object;)I
public static final fun indexOfFirstWideLiteralInstructionValue (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
public static final fun indexOfFirstWideLiteralInstructionValueOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
public static final fun indexOfFirstWideLiteralInstructionValueReversed (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
public static final fun indexOfFirstWideLiteralInstructionValueReversedOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;J)I
public static final fun indexOfIdResource (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
public static final fun indexOfIdResourceOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
public static final fun injectHideViewCall (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;IILjava/lang/String;Ljava/lang/String;)V
Expand Down Expand Up @@ -2201,6 +2209,7 @@ public final class app/revanced/util/ResourceUtilsKt {
public static final fun copyXmlNode (Ljava/lang/String;Lapp/revanced/patcher/util/DomFileEditor;Lapp/revanced/patcher/util/DomFileEditor;)Ljava/lang/AutoCloseable;
public static final fun doRecursively (Lorg/w3c/dom/Node;Lkotlin/jvm/functions/Function1;)V
public static final fun forEachChildElement (Lorg/w3c/dom/Node;Lkotlin/jvm/functions/Function1;)V
public static final fun insertFirst (Lorg/w3c/dom/Node;Lorg/w3c/dom/Node;)V
public static final fun iterateXmlNodeChildren (Lapp/revanced/patcher/data/ResourceContext;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import app.revanced.patches.shared.misc.settings.preference.IntentPreference
import app.revanced.util.ResourceGroup
import app.revanced.util.copyResources
import app.revanced.util.getNode
import app.revanced.util.insertFirst
import org.w3c.dom.Node
import java.io.Closeable

Expand Down Expand Up @@ -47,11 +48,7 @@ abstract class BaseSettingsResourcePatch(
// It may be necessary to ask for the desired resourceValue in the future.
AddResourcesPatch("values", resource)
}.let { preferenceNode ->
if (prepend && firstChild != null) {
insertBefore(preferenceNode, firstChild)
} else {
appendChild(preferenceNode)
}
insertFirst(preferenceNode)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ object CopyVideoUrlBytecodePatch : BytecodePatch(emptySet()) {

override fun execute(context: BytecodeContext) {
BUTTONS_DESCRIPTORS.forEach { descriptor ->
PlayerControlsBytecodePatch.initializeControl("$descriptor->initializeButton(Landroid/view/View;)V")
PlayerControlsBytecodePatch.injectVisibilityCheckCall("$descriptor->changeVisibility(Z)V")
PlayerControlsBytecodePatch.initializeBottomControl(descriptor)
PlayerControlsBytecodePatch.injectVisibilityCheckCall(descriptor)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
import app.revanced.patches.youtube.misc.playercontrols.BottomControlsResourcePatch
import app.revanced.patches.youtube.misc.playercontrols.PlayerControlsResourcePatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.ResourceGroup
import app.revanced.util.copyResources

@Patch(
dependencies = [
SettingsPatch::class,
BottomControlsResourcePatch::class,
PlayerControlsResourcePatch::class,
AddResourcesPatch::class
]
)
Expand All @@ -34,6 +34,6 @@ internal object CopyVideoUrlResourcePatch : ResourcePatch() {
)
)

BottomControlsResourcePatch.addControls("copyvideourl")
PlayerControlsResourcePatch.addBottomControls("copyvideourl")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ object DownloadsPatch : BytecodePatch(
private const val BUTTON_DESCRIPTOR = "Lapp/revanced/integrations/youtube/videoplayer/ExternalDownloadButton;"

override fun execute(context: BytecodeContext) {
PlayerControlsBytecodePatch.initializeControl("$BUTTON_DESCRIPTOR->initializeButton(Landroid/view/View;)V")
PlayerControlsBytecodePatch.injectVisibilityCheckCall("$BUTTON_DESCRIPTOR->changeVisibility(Z)V")
PlayerControlsBytecodePatch.initializeBottomControl(BUTTON_DESCRIPTOR)
PlayerControlsBytecodePatch.injectVisibilityCheckCall(BUTTON_DESCRIPTOR)

// Main activity is used to launch downloader intent.
MainActivityFingerprint.resultOrThrow().mutableMethod.apply {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ 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.shared.misc.settings.preference.TextPreference
import app.revanced.patches.youtube.misc.playercontrols.BottomControlsResourcePatch
import app.revanced.patches.youtube.misc.playercontrols.PlayerControlsResourcePatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.util.ResourceGroup
import app.revanced.util.copyResources

@Patch(
dependencies = [
BottomControlsResourcePatch::class,
PlayerControlsResourcePatch::class,
SettingsPatch::class,
AddResourcesPatch::class,
],
Expand All @@ -42,6 +42,6 @@ internal object DownloadsResourcePatch : ResourcePatch() {
ResourceGroup("drawable", "revanced_yt_download_button.xml"),
)

BottomControlsResourcePatch.addControls("downloads")
PlayerControlsResourcePatch.addBottomControls("downloads")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.annotation.CompatiblePackage
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
import app.revanced.patches.youtube.layout.sponsorblock.fingerprints.AppendTimeFingerprint
import app.revanced.patches.youtube.layout.sponsorblock.fingerprints.ControlsOverlayFingerprint
import app.revanced.patches.youtube.layout.sponsorblock.fingerprints.RectangleFieldInvalidatorFingerprint
Expand All @@ -26,7 +25,10 @@ import app.revanced.patches.youtube.video.information.VideoInformationPatch
import app.revanced.patches.youtube.video.videoid.VideoIdPatch
import app.revanced.util.exception
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.*
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.instruction.ReferenceInstruction
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
Expand Down Expand Up @@ -169,59 +171,14 @@ object SponsorBlockBytecodePatch : BytecodePatch(
break
}

/*
* Voting & Shield button
*/
val controlsMethodResult = PlayerControlsBytecodePatch.showPlayerControlsFingerprintResult

val controlsLayoutStubResourceId =
ResourceMappingPatch["id", "controls_layout_stub"]
val zoomOverlayResourceId =
ResourceMappingPatch["id", "video_zoom_overlay_stub"]

methods@ for (method in controlsMethodResult.mutableClass.methods) {
val instructions = method.implementation?.instructions!!
instructions@ for ((index, instruction) in instructions.withIndex()) {
// search for method which inflates the controls layout view
if (instruction.opcode != Opcode.CONST) continue@instructions

when ((instruction as NarrowLiteralInstruction).wideLiteral) {
controlsLayoutStubResourceId -> {
// replace the view with the YouTubeControlsOverlay
val moveResultInstructionIndex = index + 5
val inflatedViewRegister =
(instructions[moveResultInstructionIndex] as OneRegisterInstruction).registerA
// initialize with the player overlay object
method.addInstructions(
moveResultInstructionIndex + 1, // insert right after moving the view to the register and use that register
"""
invoke-static {v$inflatedViewRegister}, $INTEGRATIONS_CREATE_SEGMENT_BUTTON_CONTROLLER_CLASS_DESCRIPTOR->initialize(Landroid/view/View;)V
invoke-static {v$inflatedViewRegister}, $INTEGRATIONS_VOTING_BUTTON_CONTROLLER_CLASS_DESCRIPTOR->initialize(Landroid/view/View;)V
""",
)
}

zoomOverlayResourceId -> {
val invertVisibilityMethod =
context.toMethodWalker(method).nextMethod(index - 6, true).getMethod() as MutableMethod
// change visibility of the buttons
invertVisibilityMethod.addInstructions(
0,
"""
invoke-static {p1}, $INTEGRATIONS_CREATE_SEGMENT_BUTTON_CONTROLLER_CLASS_DESCRIPTOR->changeVisibilityNegatedImmediate(Z)V
invoke-static {p1}, $INTEGRATIONS_VOTING_BUTTON_CONTROLLER_CLASS_DESCRIPTOR->changeVisibilityNegatedImmediate(Z)V
""".trimIndent(),
)
}
}
}
}
// Change visibility of the buttons.
PlayerControlsBytecodePatch.initializeTopControl(INTEGRATIONS_CREATE_SEGMENT_BUTTON_CONTROLLER_CLASS_DESCRIPTOR)
PlayerControlsBytecodePatch.injectVisibilityCheckCall(INTEGRATIONS_CREATE_SEGMENT_BUTTON_CONTROLLER_CLASS_DESCRIPTOR)

// change visibility of the buttons
PlayerControlsBytecodePatch.injectVisibilityCheckCall("$INTEGRATIONS_CREATE_SEGMENT_BUTTON_CONTROLLER_CLASS_DESCRIPTOR->changeVisibility(Z)V")
PlayerControlsBytecodePatch.injectVisibilityCheckCall("$INTEGRATIONS_VOTING_BUTTON_CONTROLLER_CLASS_DESCRIPTOR->changeVisibility(Z)V")
PlayerControlsBytecodePatch.initializeTopControl(INTEGRATIONS_VOTING_BUTTON_CONTROLLER_CLASS_DESCRIPTOR)
PlayerControlsBytecodePatch.injectVisibilityCheckCall(INTEGRATIONS_VOTING_BUTTON_CONTROLLER_CLASS_DESCRIPTOR)

// append the new time to the player layout
// Append the new time to the player layout.
val appendTimeFingerprintResult = AppendTimeFingerprint.result!!
val appendTimePatternScanStartIndex = appendTimeFingerprintResult.scanResult.patternScanResult!!.startIndex
val targetRegister =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
package app.revanced.patches.youtube.layout.sponsorblock

import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.PatchException
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patches.all.misc.resources.AddResourcesPatch
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
import app.revanced.patches.shared.misc.settings.preference.IntentPreference
import app.revanced.patches.youtube.misc.playercontrols.PlayerControlsResourcePatch
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import app.revanced.patches.youtube.misc.settings.SettingsResourcePatch
import app.revanced.util.ResourceGroup
import app.revanced.util.copyResources
import app.revanced.util.copyXmlNode
import app.revanced.util.inputStreamFromBundledResource

@Patch(
dependencies = [
Expand Down Expand Up @@ -60,49 +58,6 @@ internal object SponsorBlockResourcePatch : ResourcePatch() {
context.copyResources("sponsorblock", resourceGroup)
}

// copy nodes from host resources to their real xml files

val hostingResourceStream =
inputStreamFromBundledResource(
"sponsorblock",
"host/layout/youtube_controls_layout.xml",
)!!

var modifiedControlsLayout = false
val editor = context.xmlEditor["res/layout/youtube_controls_layout.xml"]
"RelativeLayout".copyXmlNode(
context.xmlEditor[hostingResourceStream],
editor,
).also {
val document = editor.file

val children = document.getElementsByTagName("RelativeLayout").item(0).childNodes

// Replace the startOf with the voting button view so that the button does not overlap
for (i in 1 until children.length) {
val view = children.item(i)

// Replace the attribute for a specific node only
if (!(
view.hasAttributes() &&
view.attributes.getNamedItem(
"android:id",
).nodeValue.endsWith("live_chat_overlay_button")
)
) {
continue
}

// voting button id from the voting button view from the youtube_controls_layout.xml host file
val votingButtonId = "@+id/revanced_sb_voting_button"

view.attributes.getNamedItem("android:layout_toStartOf").nodeValue = votingButtonId

modifiedControlsLayout = true
break
}
}.close()

if (!modifiedControlsLayout) throw PatchException("Could not modify controls layout")
PlayerControlsResourcePatch.addTopControls("sponsorblock")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,70 +3,18 @@ package app.revanced.patches.youtube.misc.playercontrols
import app.revanced.patcher.data.ResourceContext
import app.revanced.patcher.patch.ResourcePatch
import app.revanced.patcher.patch.annotation.Patch
import app.revanced.patcher.util.DomFileEditor
import app.revanced.patches.shared.misc.mapping.ResourceMappingPatch
import java.io.Closeable

@Patch(dependencies = [ResourceMappingPatch::class])
@Patch(
dependencies = [PlayerControlsBytecodePatch::class],
)
@Deprecated("Patch renamed to PlayerControlsResourcePatch", replaceWith = ReplaceWith("PlayerControlsBytecodePatch"))
object BottomControlsResourcePatch : ResourcePatch(), Closeable {
internal var bottomUiContainerResourceId: Long = -1
override fun execute(context: ResourceContext) {}

private const val TARGET_RESOURCE_NAME = "youtube_controls_bottom_ui_container.xml"
private const val TARGET_RESOURCE = "res/layout/$TARGET_RESOURCE_NAME"

// The element to the left of the element being added.
private var lastLeftOf = "fullscreen_button"

private lateinit var resourceContext: ResourceContext
private lateinit var targetDocumentEditor: DomFileEditor

override fun execute(context: ResourceContext) {
resourceContext = context
targetDocumentEditor = context.xmlEditor[TARGET_RESOURCE]

bottomUiContainerResourceId = ResourceMappingPatch["id", "bottom_ui_container_stub"]
}

/**
* Add new controls to the bottom of the YouTube player.
*
* @param resourceDirectoryName The name of the directory containing the hosting resource.
*/
fun addControls(resourceDirectoryName: String) {
val sourceDocumentEditor = resourceContext.xmlEditor[
this::class.java.classLoader.getResourceAsStream(
"$resourceDirectoryName/host/layout/$TARGET_RESOURCE_NAME",
)!!,
]
val sourceDocument = sourceDocumentEditor.file
val targetDocument = targetDocumentEditor.file

val targetElementTag = "android.support.constraint.ConstraintLayout"

val sourceElements = sourceDocument.getElementsByTagName(targetElementTag).item(0).childNodes
val targetElement = targetDocument.getElementsByTagName(targetElementTag).item(0)

for (index in 1 until sourceElements.length) {
val element = sourceElements.item(index).cloneNode(true)

// If the element has no attributes there's no point to adding it to the destination.
if (!element.hasAttributes()) continue

// Set the elements lastLeftOf attribute to the lastLeftOf value.
val namespace = "@+id"
element.attributes.getNamedItem("yt:layout_constraintRight_toLeftOf").nodeValue =
"$namespace/$lastLeftOf"

// Set lastLeftOf attribute to the current element.
val nameSpaceLength = 5
lastLeftOf = element.attributes.getNamedItem("android:id").nodeValue.substring(nameSpaceLength)

// Add the element.
targetDocument.adoptNode(element)
targetElement.appendChild(element)
}
sourceDocumentEditor.close()
PlayerControlsResourcePatch.addBottomControls(resourceDirectoryName)
}

override fun close() = targetDocumentEditor.close()
}
override fun close() {}
}
Loading

0 comments on commit 4b88c31

Please sign in to comment.