Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(YouTube - Change header): Change to ReVanced header by default #2512

Merged
merged 11 commits into from
Jan 2, 2024
6 changes: 6 additions & 0 deletions api/revanced-patches.api
Original file line number Diff line number Diff line change
Expand Up @@ -1124,6 +1124,12 @@ public final class app/revanced/patches/youtube/layout/branding/CustomBrandingPa
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 static final field INSTANCE Lapp/revanced/patches/youtube/layout/branding/header/PremiumHeadingPatch;
public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ object CustomBrandingPatch : ResourcePatch() {
)
}
}
} else resourceGroups.forEach { context.copyResources("branding", it) }
} else resourceGroups.forEach { context.copyResources("custom-branding", it) }
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
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 header in top bar. Defaults to the ReVanced header.",
compatiblePackages = [
CompatiblePackage("com.google.android.youtube")
]
)
@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_MINIMAL_HEADER_NAME = "ReVanced minimal"

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 minimal",
values = mapOf(
"YouTube" to HEADER_NAME,
"YouTube Premium" to PREMIUM_HEADER_NAME,
"ReVanced" to REVANCED_HEADER_NAME,
"ReVanced minimal" to REVANCED_MINIMAL_HEADER_NAME,
LisoUseInAIKyrios marked this conversation as resolved.
Show resolved Hide resolved
),
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) {
val targetResourceDirectories = targetResourceDirectoryNames.mapNotNull {
context["res"].resolve(it).takeIf(File::exists)
}
val targetResourceFiles = targetResourceDirectoryNames.map { directoryName ->
ResourceGroup(
directoryName,
*variants.map { variant -> "${HEADER_NAME}_$variant.png" }.toTypedArray()
)
}

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

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 toReVancedMinimal = {
// Copy the ReVanced header to the resource directories.
targetResourceFiles.forEach { context.copyResources("change-header/revanced-minimal", 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_MINIMAL_HEADER_NAME -> toReVancedMinimal
else -> toCustom
}()
}
}
Original file line number Diff line number Diff line change
@@ -1,62 +1,9 @@
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.booleanPatchOption
import kotlin.io.path.copyTo

@Patch(
name = "Premium heading",
description = "Show or hide the premium heading.",
compatiblePackages = [
CompatiblePackage("com.google.android.youtube")
]
)
@Suppress("unused")
@Deprecated("Use PremiumHeadingPatch instead.")
object PremiumHeadingPatch : ResourcePatch() {
private const val DEFAULT_HEADING_RES = "yt_wordmark_header"
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 premium heading.",
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)
}
}
}
override fun execute(context: ResourceContext) = ChangeHeaderPatch.execute(context)
}
8 changes: 7 additions & 1 deletion src/main/kotlin/app/revanced/util/ResourceUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import app.revanced.patcher.util.DomFileEditor
import app.revanced.patches.shared.settings.preference.impl.StringResource
import app.revanced.patches.youtube.misc.settings.SettingsPatch
import org.w3c.dom.Node
import java.io.InputStream
import java.nio.file.Files
import java.nio.file.StandardCopyOption

Expand Down Expand Up @@ -53,13 +54,18 @@ fun ResourceContext.copyResources(sourceResourceDirectory: String, vararg resour
resourceGroup.resources.forEach { resource ->
val resourceFile = "${resourceGroup.resourceDirectoryName}/$resource"
Files.copy(
classLoader.getResourceAsStream("$sourceResourceDirectory/$resourceFile")!!,
inputStreamFromBundledResource(sourceResourceDirectory, resourceFile)!!,
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.
* @param resourceDirectoryName The name of the directory of the resource.
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.