Skip to content

Commit 75f785d

Browse files
feat(YouTube - Change header): Change to ReVanced borderless logo header by default (ReVanced#2512)
Co-authored-by: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com>
1 parent a2c4876 commit 75f785d

File tree

45 files changed

+153
-57
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+153
-57
lines changed

api/revanced-patches.api

+6
Original file line numberDiff line numberDiff line change
@@ -1128,6 +1128,12 @@ public final class app/revanced/patches/youtube/layout/branding/CustomBrandingPa
11281128
public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
11291129
}
11301130

1131+
public final class app/revanced/patches/youtube/layout/branding/header/ChangeHeaderPatch : app/revanced/patcher/patch/ResourcePatch {
1132+
public static final field INSTANCE Lapp/revanced/patches/youtube/layout/branding/header/ChangeHeaderPatch;
1133+
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
1134+
public fun execute (Lapp/revanced/patcher/data/ResourceContext;)V
1135+
}
1136+
11311137
public final class app/revanced/patches/youtube/layout/branding/header/PremiumHeadingPatch : app/revanced/patcher/patch/ResourcePatch {
11321138
public static final field INSTANCE Lapp/revanced/patches/youtube/layout/branding/header/PremiumHeadingPatch;
11331139
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V

src/main/kotlin/app/revanced/patches/youtube/layout/branding/CustomBrandingPatch.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ object CustomBrandingPatch : ResourcePatch() {
9393
)
9494
}
9595
}
96-
} else resourceGroups.forEach { context.copyResources("branding", it) }
96+
} else resourceGroups.forEach { context.copyResources("custom-branding", it) }
9797
}
9898
}
9999

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
package app.revanced.patches.youtube.layout.branding.header
2+
3+
import app.revanced.patcher.data.ResourceContext
4+
import app.revanced.patcher.patch.PatchException
5+
import app.revanced.patcher.patch.ResourcePatch
6+
import app.revanced.patcher.patch.annotation.CompatiblePackage
7+
import app.revanced.patcher.patch.annotation.Patch
8+
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.stringPatchOption
9+
import app.revanced.util.ResourceGroup
10+
import app.revanced.util.copyResources
11+
import java.io.File
12+
13+
@Patch(
14+
name = "Change header",
15+
description = "Change the in app header. Defaults to the ReVanced header.",
16+
compatiblePackages = [
17+
CompatiblePackage("com.google.android.youtube")
18+
],
19+
use = false
20+
)
21+
@Suppress("unused")
22+
object ChangeHeaderPatch : ResourcePatch() {
23+
private const val HEADER_NAME = "yt_wordmark_header"
24+
private const val PREMIUM_HEADER_NAME = "yt_premium_wordmark_header"
25+
private const val REVANCED_HEADER_NAME = "ReVanced"
26+
private const val REVANCED_BORDERLESS_HEADER_NAME = "ReVanced (borderless logo)"
27+
28+
private val targetResourceDirectoryNames = arrayOf(
29+
"xxxhdpi",
30+
"xxhdpi",
31+
"xhdpi",
32+
"mdpi",
33+
"hdpi",
34+
).map { dpi ->
35+
"drawable-$dpi"
36+
}
37+
38+
private val variants = arrayOf("light", "dark")
39+
40+
private val header by stringPatchOption(
41+
key = "header",
42+
default = REVANCED_BORDERLESS_HEADER_NAME,
43+
values = mapOf(
44+
"YouTube" to HEADER_NAME,
45+
"YouTube Premium" to PREMIUM_HEADER_NAME,
46+
"ReVanced" to REVANCED_HEADER_NAME,
47+
"ReVanced (borderless logo)" to REVANCED_BORDERLESS_HEADER_NAME,
48+
),
49+
title = "Header",
50+
description = """
51+
The header to use in top bar or the path to a custom header.
52+
The path to a folder containing one or more of the following folders matching the DPI of your device:
53+
54+
${targetResourceDirectoryNames.joinToString("\n") { "- $it" }}
55+
56+
These folders must contain the following files:
57+
58+
${variants.joinToString("\n") { variant -> "- ${HEADER_NAME}_$variant.png" }}
59+
""".trimIndent(),
60+
required = true,
61+
)
62+
63+
override fun execute(context: ResourceContext) {
64+
// The directories to copy the header to.
65+
val targetResourceDirectories = targetResourceDirectoryNames.mapNotNull {
66+
context["res"].resolve(it).takeIf(File::exists)
67+
}
68+
// The files to replace in the target directories.
69+
val targetResourceFiles = targetResourceDirectoryNames.map { directoryName ->
70+
ResourceGroup(
71+
directoryName,
72+
*variants.map { variant -> "${HEADER_NAME}_$variant.png" }.toTypedArray()
73+
)
74+
}
75+
76+
/**
77+
* A function that overwrites both header variants from [from] to [to] in the target resource directories.
78+
*/
79+
val overwriteFromTo: (String, String) -> Unit = { from: String, to: String ->
80+
targetResourceDirectories.forEach { directory ->
81+
variants.forEach { variant ->
82+
val fromPath = directory.resolve("${from}_$variant.png")
83+
val toPath = directory.resolve("${to}_$variant.png")
84+
85+
fromPath.copyTo(toPath, true)
86+
}
87+
}
88+
}
89+
90+
// Functions to overwrite the header to the different variants.
91+
val toPremium = { overwriteFromTo(PREMIUM_HEADER_NAME, HEADER_NAME) }
92+
val toHeader = { overwriteFromTo(HEADER_NAME, PREMIUM_HEADER_NAME) }
93+
val toReVanced = {
94+
// Copy the ReVanced header to the resource directories.
95+
targetResourceFiles.forEach { context.copyResources("change-header/revanced", it) }
96+
97+
// Overwrite the premium with the custom header as well.
98+
toHeader()
99+
}
100+
val toReVancedBorderless = {
101+
// Copy the ReVanced borderless header to the resource directories.
102+
targetResourceFiles.forEach { context.copyResources("change-header/revanced-borderless", it) }
103+
104+
// Overwrite the premium with the custom header as well.
105+
toHeader()
106+
}
107+
val toCustom = {
108+
var copiedReplacementImages = false
109+
// For all the resource groups in the custom header folder, copy them to the resource directories.
110+
File(header!!).listFiles { file -> file.isDirectory }?.forEach { folder ->
111+
val targetDirectory = context["res"].resolve(folder.name)
112+
// Skip if the target directory (DPI) doesn't exist.
113+
if (!targetDirectory.exists()) return@forEach
114+
115+
folder.listFiles { file -> file.isFile }?.forEach {
116+
val targetResourceFile = targetDirectory.resolve(it.name)
117+
118+
it.copyTo(targetResourceFile, true)
119+
copiedReplacementImages = true
120+
}
121+
}
122+
123+
if (!copiedReplacementImages) throw PatchException("Could not find any custom images resources in directory: $header")
124+
125+
// Overwrite the premium with the custom header as well.
126+
toHeader()
127+
}
128+
129+
when (header) {
130+
HEADER_NAME -> toHeader
131+
PREMIUM_HEADER_NAME -> toPremium
132+
REVANCED_HEADER_NAME -> toReVanced
133+
REVANCED_BORDERLESS_HEADER_NAME -> toReVancedBorderless
134+
else -> toCustom
135+
}()
136+
}
137+
}
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,9 @@
11
package app.revanced.patches.youtube.layout.branding.header
22

33
import app.revanced.patcher.data.ResourceContext
4-
import app.revanced.patcher.patch.PatchException
54
import app.revanced.patcher.patch.ResourcePatch
6-
import app.revanced.patcher.patch.annotation.CompatiblePackage
7-
import app.revanced.patcher.patch.annotation.Patch
8-
import app.revanced.patcher.patch.options.PatchOption.PatchExtensions.booleanPatchOption
9-
import kotlin.io.path.copyTo
105

11-
@Patch(
12-
name = "Premium heading",
13-
description = "Adds or removes the YouTube Premium logo at the top of feeds.",
14-
compatiblePackages = [
15-
CompatiblePackage("com.google.android.youtube")
16-
]
17-
)
18-
@Suppress("unused")
6+
@Deprecated("Use PremiumHeadingPatch instead.")
197
object PremiumHeadingPatch : ResourcePatch() {
20-
private const val DEFAULT_HEADING_RES = "yt_wordmark_header"
21-
private const val PREMIUM_HEADING_RES = "yt_premium_wordmark_header"
22-
23-
private val usePremiumHeading by booleanPatchOption(
24-
key = "usePremiumHeading",
25-
default = true,
26-
title = "Use premium heading",
27-
description = "Whether to use the YouTube Premium logo.",
28-
required = true,
29-
)
30-
31-
override fun execute(context: ResourceContext) {
32-
val resDirectory = context["res"]
33-
34-
val (original, replacement) = if (usePremiumHeading!!)
35-
PREMIUM_HEADING_RES to DEFAULT_HEADING_RES
36-
else
37-
DEFAULT_HEADING_RES to PREMIUM_HEADING_RES
38-
39-
val variants = arrayOf("light", "dark")
40-
41-
arrayOf(
42-
"xxxhdpi",
43-
"xxhdpi",
44-
"xhdpi",
45-
"hdpi",
46-
"mdpi"
47-
).mapNotNull { dpi ->
48-
resDirectory.resolve("drawable-$dpi").takeIf { it.exists() }?.toPath()
49-
}.also {
50-
if (it.isEmpty())
51-
throw PatchException("The drawable folder can not be found. Therefore, the patch can not be applied.")
52-
}.forEach { path ->
53-
54-
variants.forEach { mode ->
55-
val fromPath = path.resolve("${original}_$mode.png")
56-
val toPath = path.resolve("${replacement}_$mode.png")
57-
58-
fromPath.copyTo(toPath, true)
59-
}
60-
}
61-
}
8+
override fun execute(context: ResourceContext) = ChangeHeaderPatch.execute(context)
629
}

src/main/kotlin/app/revanced/util/ResourceUtils.kt

+7-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import app.revanced.patcher.util.DomFileEditor
55
import app.revanced.patches.shared.settings.preference.impl.StringResource
66
import app.revanced.patches.youtube.misc.settings.SettingsPatch
77
import org.w3c.dom.Node
8+
import java.io.InputStream
89
import java.nio.file.Files
910
import java.nio.file.StandardCopyOption
1011

@@ -53,13 +54,18 @@ fun ResourceContext.copyResources(sourceResourceDirectory: String, vararg resour
5354
resourceGroup.resources.forEach { resource ->
5455
val resourceFile = "${resourceGroup.resourceDirectoryName}/$resource"
5556
Files.copy(
56-
classLoader.getResourceAsStream("$sourceResourceDirectory/$resourceFile")!!,
57+
inputStreamFromBundledResource(sourceResourceDirectory, resourceFile)!!,
5758
targetResourceDirectory.resolve(resourceFile).toPath(), StandardCopyOption.REPLACE_EXISTING
5859
)
5960
}
6061
}
6162
}
6263

64+
internal fun inputStreamFromBundledResource(
65+
sourceResourceDirectory: String,
66+
resourceFile: String
67+
): InputStream? = classLoader.getResourceAsStream("$sourceResourceDirectory/$resourceFile")
68+
6369
/**
6470
* Resource names mapped to their corresponding resource data.
6571
* @param resourceDirectoryName The name of the directory of the resource.

0 commit comments

Comments
 (0)