mirror of
https://github.com/inotia00/revanced-patches.git
synced 2025-06-12 05:07:41 +02:00
feat(YouTube): Update Overlay buttons
, add Hide live chat messages
and more Start page
options (#50)
* feat(YouTube): Add `Hide live chat messages` * feat(YouTube/Overlay buttons): Add ability to reset speed back to default on second tap and hold * feat(YouTube/Overlay buttons): Add `Time-ordered playlist` from channel videos * feat(YouTube/Overlay buttons): Add more icon types and selectable bottom margin * feat(YouTube): Add more start page options * refactor: remove period from speed reset toast * feat(YouTube): Add `Hide live chat replay` * feat(YouTube/Swipe controls): Add adjustable `Swipe overlay screen size` * fix(YouTube/Swipe controls): Normalize default text size * chore: clarify swipe overlay screen size description * chore: remove whitelist icons --------- Co-authored-by: inotia00 <108592928+inotia00@users.noreply.github.com>
This commit is contained in:
@ -1,7 +1,7 @@
|
||||
package app.revanced.patches.youtube.player.overlaybuttons
|
||||
|
||||
import app.revanced.patcher.data.ResourceContext
|
||||
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.booleanPatchOption
|
||||
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.stringPatchOption
|
||||
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
|
||||
import app.revanced.patches.youtube.utils.fix.fullscreen.FullscreenButtonViewStubPatch
|
||||
import app.revanced.patches.youtube.utils.fix.suggestedvideoendscreen.SuggestedVideoEndScreenPatch
|
||||
@ -16,6 +16,12 @@ import app.revanced.util.doRecursively
|
||||
import app.revanced.util.patch.BaseResourcePatch
|
||||
import org.w3c.dom.Element
|
||||
|
||||
/**
|
||||
* Patch to add overlay buttons in the YouTube video player.
|
||||
*
|
||||
* This patch integrates various buttons such as copy URL, speed, repeat, etc., into the video player's
|
||||
* control overlay, providing enhanced functionality directly in the player interface.
|
||||
*/
|
||||
@Suppress("DEPRECATION", "unused")
|
||||
object OverlayButtonsPatch : BaseResourcePatch(
|
||||
name = "Overlay buttons",
|
||||
@ -30,39 +36,59 @@ object OverlayButtonsPatch : BaseResourcePatch(
|
||||
),
|
||||
compatiblePackages = COMPATIBLE_PACKAGE
|
||||
) {
|
||||
private val OutlineIcon by booleanPatchOption(
|
||||
key = "OutlineIcon",
|
||||
default = false,
|
||||
title = "Outline icons",
|
||||
description = "Apply the outline icon",
|
||||
required = true
|
||||
private const val DEFAULT_MARGIN = "0.0dip"
|
||||
private const val WIDER_MARGIN = "6.0dip"
|
||||
|
||||
private const val DEFAULT_ICON_KEY = "Rounded"
|
||||
|
||||
// Mapping of icon types to their respective resource folder names
|
||||
private val iconTypes = mapOf(
|
||||
"Bold" to "bold",
|
||||
DEFAULT_ICON_KEY to "rounded",
|
||||
"Thin" to "thin"
|
||||
)
|
||||
|
||||
private val WiderBottomPadding by booleanPatchOption(
|
||||
key = "WiderBottomPadding",
|
||||
default = false,
|
||||
title = "Wider bottom padding",
|
||||
description = "Apply wider bottom padding. Click effect may not be shown in the correct position."
|
||||
// Option to select icon type
|
||||
private val IconType by stringPatchOption(
|
||||
key = "IconType",
|
||||
default = DEFAULT_ICON_KEY,
|
||||
values = iconTypes,
|
||||
title = "Icon type",
|
||||
description = "Apply icon type"
|
||||
)
|
||||
|
||||
// Option to set bottom margin
|
||||
private val BottomMargin by stringPatchOption(
|
||||
key = "BottomMargin",
|
||||
default = DEFAULT_MARGIN,
|
||||
values = mapOf(
|
||||
"Wider" to WIDER_MARGIN,
|
||||
"Default" to DEFAULT_MARGIN
|
||||
),
|
||||
title = "Bottom margin",
|
||||
description = "Apply bottom margin to Overlay buttons and Timestamp"
|
||||
)
|
||||
|
||||
/**
|
||||
* Main execution method for applying the patch.
|
||||
*
|
||||
* @param context The resource context for patching.
|
||||
*/
|
||||
override fun execute(context: ResourceContext) {
|
||||
|
||||
/**
|
||||
* Inject hook
|
||||
*/
|
||||
// Inject hooks for overlay buttons.
|
||||
arrayOf(
|
||||
"AlwaysRepeat;",
|
||||
"CopyVideoUrl;",
|
||||
"CopyVideoUrlTimestamp;",
|
||||
"ExternalDownload;",
|
||||
"SpeedDialog;"
|
||||
"SpeedDialog;",
|
||||
"TimeOrderedPlaylist;"
|
||||
).forEach { className ->
|
||||
PlayerControlsPatch.hookOverlayButtons("$OVERLAY_BUTTONS_PATH/$className")
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy resources
|
||||
*/
|
||||
// Copy necessary resources for the overlay buttons.
|
||||
arrayOf(
|
||||
ResourceGroup(
|
||||
"drawable",
|
||||
@ -74,120 +100,50 @@ object OverlayButtonsPatch : BaseResourcePatch(
|
||||
context.copyResources("youtube/overlaybuttons/shared", resourceGroup)
|
||||
}
|
||||
|
||||
if (OutlineIcon == true) {
|
||||
arrayOf(
|
||||
ResourceGroup(
|
||||
"drawable-xxhdpi",
|
||||
"ic_fullscreen_vertical_button.png",
|
||||
"quantum_ic_fullscreen_exit_grey600_24.png",
|
||||
"quantum_ic_fullscreen_exit_white_24.png",
|
||||
"quantum_ic_fullscreen_grey600_24.png",
|
||||
"quantum_ic_fullscreen_white_24.png",
|
||||
"revanced_copy_icon.png",
|
||||
"revanced_copy_icon_with_time.png",
|
||||
"revanced_download_icon.png",
|
||||
"revanced_speed_icon.png",
|
||||
"yt_fill_arrow_repeat_white_24.png",
|
||||
"yt_outline_arrow_repeat_1_white_24.png",
|
||||
"yt_outline_arrow_shuffle_1_white_24.png",
|
||||
"yt_outline_screen_full_exit_white_24.png",
|
||||
"yt_outline_screen_full_white_24.png"
|
||||
)
|
||||
).forEach { resourceGroup ->
|
||||
context.copyResources("youtube/overlaybuttons/outline", resourceGroup)
|
||||
// Apply the selected icon type to the overlay buttons
|
||||
IconType?.let { iconType ->
|
||||
val iconValue = iconType.lowercase()
|
||||
val commonResources = arrayOf(
|
||||
"ic_fullscreen_vertical_button.png",
|
||||
"ic_vr.png",
|
||||
"quantum_ic_fullscreen_exit_grey600_24.png",
|
||||
"quantum_ic_fullscreen_exit_white_24.png",
|
||||
"quantum_ic_fullscreen_grey600_24.png",
|
||||
"quantum_ic_fullscreen_white_24.png",
|
||||
"revanced_time_ordered_playlist.png",
|
||||
"revanced_copy_icon.png",
|
||||
"revanced_copy_icon_with_time.png",
|
||||
"revanced_download_icon.png",
|
||||
"revanced_speed_icon.png",
|
||||
"yt_fill_arrow_repeat_white_24.png",
|
||||
"yt_outline_arrow_repeat_1_white_24.png",
|
||||
"yt_outline_arrow_shuffle_1_white_24.png",
|
||||
"yt_outline_screen_full_exit_white_24.png",
|
||||
"yt_outline_screen_full_white_24.png"
|
||||
)
|
||||
val specificResources = if (iconValue == "thin") {
|
||||
arrayOf("yt_outline_screen_vertical_vd_theme_24.xml")
|
||||
} else {
|
||||
arrayOf("yt_outline_screen_vertical_vd_theme_24.png")
|
||||
}
|
||||
} else {
|
||||
arrayOf(
|
||||
ResourceGroup(
|
||||
"drawable-xxhdpi",
|
||||
"ic_fullscreen_vertical_button.png",
|
||||
"ic_vr.png",
|
||||
"quantum_ic_fullscreen_exit_grey600_24.png",
|
||||
"quantum_ic_fullscreen_exit_white_24.png",
|
||||
"quantum_ic_fullscreen_grey600_24.png",
|
||||
"quantum_ic_fullscreen_white_24.png",
|
||||
"revanced_copy_icon.png",
|
||||
"revanced_copy_icon_with_time.png",
|
||||
"revanced_download_icon.png",
|
||||
"revanced_speed_icon.png",
|
||||
"yt_fill_arrow_repeat_white_24.png",
|
||||
"yt_outline_arrow_repeat_1_white_24.png",
|
||||
"yt_outline_arrow_shuffle_1_white_24.png",
|
||||
"yt_outline_screen_full_exit_white_24.png",
|
||||
"yt_outline_screen_full_white_24.png",
|
||||
"yt_outline_screen_vertical_vd_theme_24.png"
|
||||
)
|
||||
).forEach { resourceGroup ->
|
||||
context.copyResources("youtube/overlaybuttons/default", resourceGroup)
|
||||
val resources = commonResources + specificResources
|
||||
resources.forEach { resource ->
|
||||
val folderName = if (resource.endsWith(".xml")) "drawable" else "drawable-xxhdpi"
|
||||
context.copyResources("youtube/overlaybuttons/$iconValue", ResourceGroup(folderName, resource))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge xml nodes from the host to their real xml files
|
||||
*/
|
||||
// Merge XML nodes from the host to their respective XML files.
|
||||
context.copyXmlNode(
|
||||
"youtube/overlaybuttons/shared/host",
|
||||
"layout/youtube_controls_bottom_ui_container.xml",
|
||||
"android.support.constraint.ConstraintLayout"
|
||||
)
|
||||
|
||||
val fullscreenButtonId = if (SettingsPatch.upward1909)
|
||||
"youtube_controls_fullscreen_button_stub"
|
||||
else
|
||||
"fullscreen_button"
|
||||
|
||||
val bottomPadding = if (WiderBottomPadding == true) "31.0dip" else "22.0dip"
|
||||
context.xmlEditor["res/layout/youtube_controls_bottom_ui_container.xml"].use { editor ->
|
||||
editor.file.doRecursively loop@{
|
||||
if (it !is Element) return@loop
|
||||
|
||||
// Change the relationship between buttons
|
||||
it.getAttributeNode("yt:layout_constraintRight_toLeftOf")?.let { attribute ->
|
||||
if (attribute.textContent == "@id/fullscreen_button") {
|
||||
attribute.textContent = "@+id/speed_dialog_button"
|
||||
}
|
||||
}
|
||||
|
||||
it.getAttributeNode("android:id")?.let { attribute ->
|
||||
// Adjust Fullscreen Button size and padding
|
||||
arrayOf(
|
||||
"speed_dialog_button",
|
||||
"copy_video_url_button",
|
||||
"copy_video_url_timestamp_button",
|
||||
"always_repeat_button",
|
||||
"external_download_button",
|
||||
fullscreenButtonId
|
||||
).forEach { targetId ->
|
||||
if (attribute.textContent.endsWith(targetId)) {
|
||||
arrayOf(
|
||||
"0.0dip" to arrayOf("paddingLeft", "paddingRight"),
|
||||
bottomPadding to arrayOf("paddingBottom"),
|
||||
"48.0dip" to arrayOf("layout_height", "layout_width")
|
||||
).forEach { (replace, attributes) ->
|
||||
attributes.forEach { name ->
|
||||
it.getAttributeNode("android:$name")?.textContent = replace
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (WiderBottomPadding == false) {
|
||||
// Adjust TimeBar and Chapter bottom padding
|
||||
arrayOf(
|
||||
"@id/time_bar_chapter_title" to "14.0dip",
|
||||
"@id/timestamps_container" to "12.0dip"
|
||||
).forEach { (id, replace) ->
|
||||
it.getAttributeNode("android:id")?.let { attribute ->
|
||||
if (attribute.textContent == id) {
|
||||
it.getAttributeNode("android:paddingBottom").textContent = replace
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val marginBottom = BottomMargin
|
||||
?: DEFAULT_MARGIN
|
||||
|
||||
// Modify the layout of fullscreen button for newer YouTube versions (19.09.xx+)
|
||||
arrayOf(
|
||||
"youtube_controls_cf_fullscreen_button.xml",
|
||||
"youtube_controls_fullscreen_button.xml"
|
||||
@ -195,21 +151,19 @@ object OverlayButtonsPatch : BaseResourcePatch(
|
||||
val targetXml = context["res"].resolve("layout").resolve(xmlFile)
|
||||
if (targetXml.exists()) {
|
||||
context.xmlEditor["res/layout/$xmlFile"].use { editor ->
|
||||
editor.file.doRecursively loop@{
|
||||
if (it !is Element) return@loop
|
||||
editor.file.doRecursively loop@{ node ->
|
||||
if (node !is Element) return@loop
|
||||
|
||||
it.getAttributeNode("android:id")?.let { attribute ->
|
||||
// Adjust Fullscreen Button size and padding
|
||||
if (attribute.textContent.endsWith("fullscreen_button")) {
|
||||
arrayOf(
|
||||
"0.0dip" to arrayOf("paddingLeft", "paddingRight"),
|
||||
bottomPadding to arrayOf("paddingBottom"),
|
||||
"48.0dip" to arrayOf("layout_height", "layout_width")
|
||||
).forEach { (replace, attributes) ->
|
||||
attributes.forEach { name ->
|
||||
it.getAttributeNode("android:$name")?.textContent = replace
|
||||
}
|
||||
}
|
||||
if (node.getAttribute("android:id").endsWith("_button")) {
|
||||
node.setAttribute("android:layout_marginBottom", marginBottom)
|
||||
node.setAttribute("android:paddingLeft", "0.0dip")
|
||||
node.setAttribute("android:paddingRight", "0.0dip")
|
||||
node.setAttribute("android:paddingBottom", "22.0dip")
|
||||
if (!node.getAttribute("android:layout_height").equals("0.0dip") &&
|
||||
!node.getAttribute("android:layout_width").equals("0.0dip")
|
||||
) {
|
||||
node.setAttribute("android:layout_height", "48.0dip")
|
||||
node.setAttribute("android:layout_width", "48.0dip")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -217,9 +171,64 @@ object OverlayButtonsPatch : BaseResourcePatch(
|
||||
}
|
||||
}
|
||||
|
||||
context.xmlEditor["res/layout/youtube_controls_bottom_ui_container.xml"].use { editor ->
|
||||
editor.file.doRecursively loop@{ node ->
|
||||
if (node !is Element) return@loop
|
||||
|
||||
// Change the relationship between buttons
|
||||
node.getAttributeNode("yt:layout_constraintRight_toLeftOf")
|
||||
?.let { attribute ->
|
||||
if (attribute.textContent == "@id/fullscreen_button") {
|
||||
attribute.textContent = "@+id/speed_dialog_button"
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust TimeBar and Chapter bottom padding
|
||||
arrayOf(
|
||||
"@id/time_bar_chapter_title" to "16.0dip",
|
||||
"@id/timestamps_container" to "14.0dip"
|
||||
).forEach { (id, replace) ->
|
||||
node.getAttributeNode("android:id")?.let { attribute ->
|
||||
if (attribute.textContent == id) {
|
||||
node.getAttributeNode("android:paddingBottom").textContent =
|
||||
replace
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust layout for fullscreen button stub
|
||||
if (node.getAttribute("android:id") == "@id/youtube_controls_fullscreen_button_stub") {
|
||||
node.setAttribute("android:layout_marginBottom", marginBottom)
|
||||
if (!node.getAttribute("android:layout_height").equals("0.0dip") &&
|
||||
!node.getAttribute("android:layout_width").equals("0.0dip")
|
||||
) {
|
||||
node.setAttribute("android:layout_height", "48.0dip")
|
||||
node.setAttribute("android:layout_width", "48.0dip")
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust margin and padding for other buttons
|
||||
if (node.getAttribute("android:id").endsWith("_button")) {
|
||||
node.setAttribute("android:layout_marginBottom", marginBottom)
|
||||
node.setAttribute("android:paddingLeft", "0.0dip")
|
||||
node.setAttribute("android:paddingRight", "0.0dip")
|
||||
node.setAttribute("android:paddingBottom", "22.0dip")
|
||||
if (!node.getAttribute("android:layout_height").equals("0.0dip") &&
|
||||
!node.getAttribute("android:layout_width").equals("0.0dip")
|
||||
) {
|
||||
node.setAttribute("android:layout_height", "48.0dip")
|
||||
node.setAttribute("android:layout_width", "48.0dip")
|
||||
}
|
||||
} else if (node.getAttribute("android:id") == "@id/time_bar_chapter_title_container" ||
|
||||
node.getAttribute("android:id") == "@id/timestamps_container"
|
||||
) {
|
||||
node.setAttribute("android:layout_marginBottom", marginBottom)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add settings
|
||||
* Add settings for the overlay buttons.
|
||||
*/
|
||||
SettingsPatch.addPreference(
|
||||
arrayOf(
|
||||
@ -229,6 +238,7 @@ object OverlayButtonsPatch : BaseResourcePatch(
|
||||
)
|
||||
)
|
||||
|
||||
// Update the patch status in settings to reflect the applied changes
|
||||
SettingsPatch.updatePatchStatus(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user