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:
Aaron Veil
2024-05-21 16:59:38 +03:00
committed by GitHub
parent 71edf8c762
commit 84dd65f8a7
72 changed files with 196 additions and 143 deletions

View File

@ -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)
}
}
}