feat(YouTube - Change header): Change to ReVanced borderless logo header by default (#2512)
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
|
@ -1128,6 +1128,12 @@ public final class app/revanced/patches/youtube/layout/branding/CustomBrandingPa
|
||||||
public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
|
public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/youtube/layout/branding/header/ChangeHeaderPatch : app/revanced/patcher/patch/ResourcePatch {
|
||||||
|
public static final field INSTANCE Lapp/revanced/patches/youtube/layout/branding/header/ChangeHeaderPatch;
|
||||||
|
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/layout/branding/header/PremiumHeadingPatch : app/revanced/patcher/patch/ResourcePatch {
|
public final class app/revanced/patches/youtube/layout/branding/header/PremiumHeadingPatch : app/revanced/patcher/patch/ResourcePatch {
|
||||||
public static final field INSTANCE Lapp/revanced/patches/youtube/layout/branding/header/PremiumHeadingPatch;
|
public static final field INSTANCE Lapp/revanced/patches/youtube/layout/branding/header/PremiumHeadingPatch;
|
||||||
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
|
||||||
|
|
|
@ -93,7 +93,7 @@ object CustomBrandingPatch : ResourcePatch() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else resourceGroups.forEach { context.copyResources("branding", it) }
|
} else resourceGroups.forEach { context.copyResources("custom-branding", it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,137 @@
|
||||||
|
package app.revanced.patches.youtube.layout.branding.header
|
||||||
|
|
||||||
|
import app.revanced.patcher.data.ResourceContext
|
||||||
|
import app.revanced.patcher.patch.PatchException
|
||||||
|
import app.revanced.patcher.patch.ResourcePatch
|
||||||
|
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
||||||
|
import app.revanced.patcher.patch.annotation.Patch
|
||||||
|
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.stringPatchOption
|
||||||
|
import app.revanced.util.ResourceGroup
|
||||||
|
import app.revanced.util.copyResources
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
@Patch(
|
||||||
|
name = "Change header",
|
||||||
|
description = "Change the in app header. Defaults to the ReVanced header.",
|
||||||
|
compatiblePackages = [
|
||||||
|
CompatiblePackage("com.google.android.youtube")
|
||||||
|
],
|
||||||
|
use = false
|
||||||
|
)
|
||||||
|
@Suppress("unused")
|
||||||
|
object ChangeHeaderPatch : ResourcePatch() {
|
||||||
|
private const val HEADER_NAME = "yt_wordmark_header"
|
||||||
|
private const val PREMIUM_HEADER_NAME = "yt_premium_wordmark_header"
|
||||||
|
private const val REVANCED_HEADER_NAME = "ReVanced"
|
||||||
|
private const val REVANCED_BORDERLESS_HEADER_NAME = "ReVanced (borderless logo)"
|
||||||
|
|
||||||
|
private val targetResourceDirectoryNames = arrayOf(
|
||||||
|
"xxxhdpi",
|
||||||
|
"xxhdpi",
|
||||||
|
"xhdpi",
|
||||||
|
"mdpi",
|
||||||
|
"hdpi",
|
||||||
|
).map { dpi ->
|
||||||
|
"drawable-$dpi"
|
||||||
|
}
|
||||||
|
|
||||||
|
private val variants = arrayOf("light", "dark")
|
||||||
|
|
||||||
|
private val header by stringPatchOption(
|
||||||
|
key = "header",
|
||||||
|
default = REVANCED_BORDERLESS_HEADER_NAME,
|
||||||
|
values = mapOf(
|
||||||
|
"YouTube" to HEADER_NAME,
|
||||||
|
"YouTube Premium" to PREMIUM_HEADER_NAME,
|
||||||
|
"ReVanced" to REVANCED_HEADER_NAME,
|
||||||
|
"ReVanced (borderless logo)" to REVANCED_BORDERLESS_HEADER_NAME,
|
||||||
|
),
|
||||||
|
title = "Header",
|
||||||
|
description = """
|
||||||
|
The header to use in top bar or the path to a custom header.
|
||||||
|
The path to a folder containing one or more of the following folders matching the DPI of your device:
|
||||||
|
|
||||||
|
${targetResourceDirectoryNames.joinToString("\n") { "- $it" }}
|
||||||
|
|
||||||
|
These folders must contain the following files:
|
||||||
|
|
||||||
|
${variants.joinToString("\n") { variant -> "- ${HEADER_NAME}_$variant.png" }}
|
||||||
|
""".trimIndent(),
|
||||||
|
required = true,
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun execute(context: ResourceContext) {
|
||||||
|
// The directories to copy the header to.
|
||||||
|
val targetResourceDirectories = targetResourceDirectoryNames.mapNotNull {
|
||||||
|
context["res"].resolve(it).takeIf(File::exists)
|
||||||
|
}
|
||||||
|
// The files to replace in the target directories.
|
||||||
|
val targetResourceFiles = targetResourceDirectoryNames.map { directoryName ->
|
||||||
|
ResourceGroup(
|
||||||
|
directoryName,
|
||||||
|
*variants.map { variant -> "${HEADER_NAME}_$variant.png" }.toTypedArray()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function that overwrites both header variants from [from] to [to] in the target resource directories.
|
||||||
|
*/
|
||||||
|
val overwriteFromTo: (String, String) -> Unit = { from: String, to: String ->
|
||||||
|
targetResourceDirectories.forEach { directory ->
|
||||||
|
variants.forEach { variant ->
|
||||||
|
val fromPath = directory.resolve("${from}_$variant.png")
|
||||||
|
val toPath = directory.resolve("${to}_$variant.png")
|
||||||
|
|
||||||
|
fromPath.copyTo(toPath, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Functions to overwrite the header to the different variants.
|
||||||
|
val toPremium = { overwriteFromTo(PREMIUM_HEADER_NAME, HEADER_NAME) }
|
||||||
|
val toHeader = { overwriteFromTo(HEADER_NAME, PREMIUM_HEADER_NAME) }
|
||||||
|
val toReVanced = {
|
||||||
|
// Copy the ReVanced header to the resource directories.
|
||||||
|
targetResourceFiles.forEach { context.copyResources("change-header/revanced", it) }
|
||||||
|
|
||||||
|
// Overwrite the premium with the custom header as well.
|
||||||
|
toHeader()
|
||||||
|
}
|
||||||
|
val toReVancedBorderless = {
|
||||||
|
// Copy the ReVanced borderless header to the resource directories.
|
||||||
|
targetResourceFiles.forEach { context.copyResources("change-header/revanced-borderless", it) }
|
||||||
|
|
||||||
|
// Overwrite the premium with the custom header as well.
|
||||||
|
toHeader()
|
||||||
|
}
|
||||||
|
val toCustom = {
|
||||||
|
var copiedReplacementImages = false
|
||||||
|
// For all the resource groups in the custom header folder, copy them to the resource directories.
|
||||||
|
File(header!!).listFiles { file -> file.isDirectory }?.forEach { folder ->
|
||||||
|
val targetDirectory = context["res"].resolve(folder.name)
|
||||||
|
// Skip if the target directory (DPI) doesn't exist.
|
||||||
|
if (!targetDirectory.exists()) return@forEach
|
||||||
|
|
||||||
|
folder.listFiles { file -> file.isFile }?.forEach {
|
||||||
|
val targetResourceFile = targetDirectory.resolve(it.name)
|
||||||
|
|
||||||
|
it.copyTo(targetResourceFile, true)
|
||||||
|
copiedReplacementImages = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!copiedReplacementImages) throw PatchException("Could not find any custom images resources in directory: $header")
|
||||||
|
|
||||||
|
// Overwrite the premium with the custom header as well.
|
||||||
|
toHeader()
|
||||||
|
}
|
||||||
|
|
||||||
|
when (header) {
|
||||||
|
HEADER_NAME -> toHeader
|
||||||
|
PREMIUM_HEADER_NAME -> toPremium
|
||||||
|
REVANCED_HEADER_NAME -> toReVanced
|
||||||
|
REVANCED_BORDERLESS_HEADER_NAME -> toReVancedBorderless
|
||||||
|
else -> toCustom
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,62 +1,9 @@
|
||||||
package app.revanced.patches.youtube.layout.branding.header
|
package app.revanced.patches.youtube.layout.branding.header
|
||||||
|
|
||||||
import app.revanced.patcher.data.ResourceContext
|
import app.revanced.patcher.data.ResourceContext
|
||||||
import app.revanced.patcher.patch.PatchException
|
|
||||||
import app.revanced.patcher.patch.ResourcePatch
|
import app.revanced.patcher.patch.ResourcePatch
|
||||||
import app.revanced.patcher.patch.annotation.CompatiblePackage
|
|
||||||
import app.revanced.patcher.patch.annotation.Patch
|
|
||||||
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.booleanPatchOption
|
|
||||||
import kotlin.io.path.copyTo
|
|
||||||
|
|
||||||
@Patch(
|
@Deprecated("Use PremiumHeadingPatch instead.")
|
||||||
name = "Premium heading",
|
|
||||||
description = "Adds or removes the YouTube Premium logo at the top of feeds.",
|
|
||||||
compatiblePackages = [
|
|
||||||
CompatiblePackage("com.google.android.youtube")
|
|
||||||
]
|
|
||||||
)
|
|
||||||
@Suppress("unused")
|
|
||||||
object PremiumHeadingPatch : ResourcePatch() {
|
object PremiumHeadingPatch : ResourcePatch() {
|
||||||
private const val DEFAULT_HEADING_RES = "yt_wordmark_header"
|
override fun execute(context: ResourceContext) = ChangeHeaderPatch.execute(context)
|
||||||
private const val PREMIUM_HEADING_RES = "yt_premium_wordmark_header"
|
|
||||||
|
|
||||||
private val usePremiumHeading by booleanPatchOption(
|
|
||||||
key = "usePremiumHeading",
|
|
||||||
default = true,
|
|
||||||
title = "Use premium heading",
|
|
||||||
description = "Whether to use the YouTube Premium logo.",
|
|
||||||
required = true,
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun execute(context: ResourceContext) {
|
|
||||||
val resDirectory = context["res"]
|
|
||||||
|
|
||||||
val (original, replacement) = if (usePremiumHeading!!)
|
|
||||||
PREMIUM_HEADING_RES to DEFAULT_HEADING_RES
|
|
||||||
else
|
|
||||||
DEFAULT_HEADING_RES to PREMIUM_HEADING_RES
|
|
||||||
|
|
||||||
val variants = arrayOf("light", "dark")
|
|
||||||
|
|
||||||
arrayOf(
|
|
||||||
"xxxhdpi",
|
|
||||||
"xxhdpi",
|
|
||||||
"xhdpi",
|
|
||||||
"hdpi",
|
|
||||||
"mdpi"
|
|
||||||
).mapNotNull { dpi ->
|
|
||||||
resDirectory.resolve("drawable-$dpi").takeIf { it.exists() }?.toPath()
|
|
||||||
}.also {
|
|
||||||
if (it.isEmpty())
|
|
||||||
throw PatchException("The drawable folder can not be found. Therefore, the patch can not be applied.")
|
|
||||||
}.forEach { path ->
|
|
||||||
|
|
||||||
variants.forEach { mode ->
|
|
||||||
val fromPath = path.resolve("${original}_$mode.png")
|
|
||||||
val toPath = path.resolve("${replacement}_$mode.png")
|
|
||||||
|
|
||||||
fromPath.copyTo(toPath, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import app.revanced.patcher.util.DomFileEditor
|
||||||
import app.revanced.patches.shared.settings.preference.impl.StringResource
|
import app.revanced.patches.shared.settings.preference.impl.StringResource
|
||||||
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
import app.revanced.patches.youtube.misc.settings.SettingsPatch
|
||||||
import org.w3c.dom.Node
|
import org.w3c.dom.Node
|
||||||
|
import java.io.InputStream
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.StandardCopyOption
|
import java.nio.file.StandardCopyOption
|
||||||
|
|
||||||
|
@ -53,13 +54,18 @@ fun ResourceContext.copyResources(sourceResourceDirectory: String, vararg resour
|
||||||
resourceGroup.resources.forEach { resource ->
|
resourceGroup.resources.forEach { resource ->
|
||||||
val resourceFile = "${resourceGroup.resourceDirectoryName}/$resource"
|
val resourceFile = "${resourceGroup.resourceDirectoryName}/$resource"
|
||||||
Files.copy(
|
Files.copy(
|
||||||
classLoader.getResourceAsStream("$sourceResourceDirectory/$resourceFile")!!,
|
inputStreamFromBundledResource(sourceResourceDirectory, resourceFile)!!,
|
||||||
targetResourceDirectory.resolve(resourceFile).toPath(), StandardCopyOption.REPLACE_EXISTING
|
targetResourceDirectory.resolve(resourceFile).toPath(), StandardCopyOption.REPLACE_EXISTING
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun inputStreamFromBundledResource(
|
||||||
|
sourceResourceDirectory: String,
|
||||||
|
resourceFile: String
|
||||||
|
): InputStream? = classLoader.getResourceAsStream("$sourceResourceDirectory/$resourceFile")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resource names mapped to their corresponding resource data.
|
* Resource names mapped to their corresponding resource data.
|
||||||
* @param resourceDirectoryName The name of the directory of the resource.
|
* @param resourceDirectoryName The name of the directory of the resource.
|
||||||
|
|
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 3 KiB |
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 6.5 KiB |
After Width: | Height: | Size: 6.4 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 2 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 7.2 KiB |
After Width: | Height: | Size: 7.1 KiB |
After Width: | Height: | Size: 9.5 KiB |
After Width: | Height: | Size: 9.5 KiB |
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 2 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 6 KiB After Width: | Height: | Size: 6 KiB |
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 8.1 KiB |
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 9.6 KiB |
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 9.6 KiB |