mirror of
https://github.com/inotia00/revanced-patches.git
synced 2025-06-12 05:07:41 +02:00
feat(YouTube Music): Add Dark theme
patch, Remove Amoled
patch
This commit is contained in:
@ -1,46 +0,0 @@
|
||||
package app.revanced.patches.music.general.amoled
|
||||
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.patches.music.utils.compatibility.Constants.COMPATIBLE_PACKAGE
|
||||
import app.revanced.patches.music.utils.extension.Constants.UTILS_PATH
|
||||
import app.revanced.patches.music.utils.patch.PatchList.AMOLED
|
||||
import app.revanced.patches.music.utils.settings.ResourceUtils.updatePatchStatus
|
||||
import app.revanced.patches.music.utils.settings.settingsPatch
|
||||
import app.revanced.patches.shared.drawable.addDrawableColorHook
|
||||
import app.revanced.patches.shared.drawable.drawableColorHookPatch
|
||||
import org.w3c.dom.Element
|
||||
|
||||
@Suppress("unused")
|
||||
val amoledPatch = resourcePatch(
|
||||
AMOLED.title,
|
||||
AMOLED.summary,
|
||||
) {
|
||||
compatibleWith(COMPATIBLE_PACKAGE)
|
||||
|
||||
dependsOn(
|
||||
drawableColorHookPatch,
|
||||
settingsPatch
|
||||
)
|
||||
|
||||
execute {
|
||||
addDrawableColorHook("$UTILS_PATH/DrawableColorPatch;->getLithoColor(I)I")
|
||||
|
||||
document("res/values/colors.xml").use { document ->
|
||||
val resourcesNode = document.getElementsByTagName("resources").item(0) as Element
|
||||
|
||||
for (i in 0 until resourcesNode.childNodes.length) {
|
||||
val node = resourcesNode.childNodes.item(i) as? Element ?: continue
|
||||
|
||||
node.textContent = when (node.getAttribute("name")) {
|
||||
"yt_black0", "yt_black1", "yt_black1_opacity95", "yt_black1_opacity98", "yt_black2", "yt_black3",
|
||||
"yt_black4", "yt_status_bar_background_dark", "ytm_color_grey_12", "material_grey_850" -> "@android:color/black"
|
||||
|
||||
else -> continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updatePatchStatus(AMOLED)
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,151 @@
|
||||
package app.revanced.patches.music.layout.theme
|
||||
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||
import app.revanced.patcher.patch.booleanOption
|
||||
import app.revanced.patcher.patch.bytecodePatch
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.patcher.patch.stringOption
|
||||
import app.revanced.patches.music.utils.compatibility.Constants.COMPATIBLE_PACKAGE
|
||||
import app.revanced.patches.music.utils.extension.Constants.PATCH_STATUS_CLASS_DESCRIPTOR
|
||||
import app.revanced.patches.music.utils.extension.Constants.UTILS_PATH
|
||||
import app.revanced.patches.music.utils.patch.PatchList.DARK_THEME
|
||||
import app.revanced.patches.music.utils.resourceid.sharedResourceIdPatch
|
||||
import app.revanced.patches.music.utils.settings.ResourceUtils.updatePatchStatus
|
||||
import app.revanced.patches.music.utils.settings.settingsPatch
|
||||
import app.revanced.patches.shared.drawable.addDrawableColorHook
|
||||
import app.revanced.patches.shared.drawable.drawableColorHookPatch
|
||||
import app.revanced.patches.shared.materialyou.baseMaterialYou
|
||||
import app.revanced.util.ResourceGroup
|
||||
import app.revanced.util.copyResources
|
||||
import app.revanced.util.findMethodOrThrow
|
||||
import app.revanced.util.fingerprint.methodOrThrow
|
||||
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
||||
import app.revanced.util.valueOrThrow
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||
import org.w3c.dom.Element
|
||||
|
||||
private const val EXTENSION_CLASS_DESCRIPTOR =
|
||||
"$UTILS_PATH/DrawableColorPatch;"
|
||||
|
||||
private val darkThemeBytecodePatch = bytecodePatch(
|
||||
description = "darkThemeBytecodePatch"
|
||||
) {
|
||||
dependsOn(
|
||||
settingsPatch,
|
||||
sharedResourceIdPatch,
|
||||
drawableColorHookPatch,
|
||||
)
|
||||
|
||||
execute {
|
||||
addDrawableColorHook("$EXTENSION_CLASS_DESCRIPTOR->getLithoColor(I)I")
|
||||
|
||||
elementsContainerFingerprint.methodOrThrow().apply {
|
||||
val index = indexOfFirstInstructionReversedOrThrow(Opcode.CHECK_CAST)
|
||||
val register = getInstruction<OneRegisterInstruction>(index).registerA
|
||||
|
||||
addInstruction(
|
||||
index + 1,
|
||||
"invoke-static {v$register}, $EXTENSION_CLASS_DESCRIPTOR->setHeaderGradient(Landroid/view/ViewGroup;)V"
|
||||
)
|
||||
}
|
||||
|
||||
findMethodOrThrow(PATCH_STATUS_CLASS_DESCRIPTOR) {
|
||||
name == "DarkTheme"
|
||||
}.replaceInstruction(
|
||||
0,
|
||||
"const/4 v0, 0x1"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val DARK_COLOR = arrayOf(
|
||||
"yt_black0", "yt_black1", "yt_black1_opacity95", "yt_black1_opacity98",
|
||||
"yt_black2", "yt_black3", "yt_black4","yt_black_pure",
|
||||
"yt_black_pure_opacity80", "yt_status_bar_background_dark",
|
||||
"ytm_color_grey_12", "material_grey_800", "material_grey_850",
|
||||
)
|
||||
|
||||
@Suppress("unused")
|
||||
val darkThemePatch = resourcePatch(
|
||||
DARK_THEME.title,
|
||||
DARK_THEME.summary,
|
||||
) {
|
||||
compatibleWith(COMPATIBLE_PACKAGE)
|
||||
|
||||
dependsOn(darkThemeBytecodePatch)
|
||||
|
||||
val amoledBlackColor = "@android:color/black"
|
||||
|
||||
val darkThemeBackgroundColor = stringOption(
|
||||
key = "darkThemeBackgroundColor",
|
||||
default = amoledBlackColor,
|
||||
values = mapOf(
|
||||
"Amoled Black" to amoledBlackColor,
|
||||
"Catppuccin (Mocha)" to "#FF181825",
|
||||
"Dark Pink" to "#FF290025",
|
||||
"Dark Blue" to "#FF001029",
|
||||
"Dark Green" to "#FF002905",
|
||||
"Dark Yellow" to "#FF282900",
|
||||
"Dark Orange" to "#FF291800",
|
||||
"Dark Red" to "#FF290000",
|
||||
),
|
||||
title = "Dark theme background color",
|
||||
description = "Can be a hex color (#AARRGGBB) or a color resource reference.",
|
||||
)
|
||||
|
||||
val materialYou by booleanOption(
|
||||
key = "materialYou",
|
||||
default = false,
|
||||
title = "MaterialYou",
|
||||
description = "Applies the MaterialYou theme for Android 12+ devices.",
|
||||
required = true
|
||||
)
|
||||
|
||||
execute {
|
||||
// Check patch options first.
|
||||
val darkThemeColor = darkThemeBackgroundColor
|
||||
.valueOrThrow()
|
||||
|
||||
document("res/values/colors.xml").use { document ->
|
||||
val resourcesNode = document.getElementsByTagName("resources").item(0) as Element
|
||||
|
||||
for (i in 0 until resourcesNode.childNodes.length) {
|
||||
val node = resourcesNode.childNodes.item(i) as? Element ?: continue
|
||||
val colorName = node.getAttribute("name")
|
||||
|
||||
if (DARK_COLOR.contains(colorName)) {
|
||||
node.textContent = darkThemeColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
arrayOf(
|
||||
ResourceGroup(
|
||||
"drawable",
|
||||
"revanced_header_gradient.xml",
|
||||
)
|
||||
).forEach { resourceGroup ->
|
||||
copyResources("music/theme", resourceGroup)
|
||||
}
|
||||
|
||||
if (materialYou == true) {
|
||||
baseMaterialYou()
|
||||
|
||||
document("res/values-v31/colors.xml").use { document ->
|
||||
DARK_COLOR.forEach { name ->
|
||||
val colorElement = document.createElement("color")
|
||||
colorElement.setAttribute("name", name)
|
||||
colorElement.textContent = "@android:color/system_neutral1_900"
|
||||
|
||||
document.getElementsByTagName("resources").item(0).appendChild(colorElement)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updatePatchStatus(DARK_THEME)
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package app.revanced.patches.music.layout.theme
|
||||
|
||||
import app.revanced.patches.music.utils.resourceid.elementsContainer
|
||||
import app.revanced.util.fingerprint.legacyFingerprint
|
||||
import app.revanced.util.or
|
||||
import com.android.tools.smali.dexlib2.AccessFlags
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
|
||||
internal val elementsContainerFingerprint = legacyFingerprint(
|
||||
name = "elementsContainerFingerprint",
|
||||
returnType = "V",
|
||||
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||
opcodes = listOf(Opcode.INVOKE_DIRECT_RANGE),
|
||||
literals = listOf(elementsContainer)
|
||||
)
|
@ -5,10 +5,6 @@ internal enum class PatchList(
|
||||
val summary: String,
|
||||
var included: Boolean? = false
|
||||
) {
|
||||
AMOLED(
|
||||
"Amoled",
|
||||
"Applies a pure black theme to some components."
|
||||
),
|
||||
BITRATE_DEFAULT_VALUE(
|
||||
"Bitrate default value",
|
||||
"Sets the audio quality to 'Always High' when you first install the app."
|
||||
@ -41,6 +37,10 @@ internal enum class PatchList(
|
||||
"Custom header for YouTube Music",
|
||||
"Applies a custom header in the top left corner within the app."
|
||||
),
|
||||
DARK_THEME(
|
||||
"Dark theme",
|
||||
"Changes the app's dark theme to the values specified in patch options."
|
||||
),
|
||||
DISABLE_CAIRO_SPLASH_ANIMATION(
|
||||
"Disable Cairo splash animation",
|
||||
"Adds an option to disable Cairo splash animation."
|
||||
@ -59,7 +59,7 @@ internal enum class PatchList(
|
||||
),
|
||||
DISABLE_MUSIC_VIDEO_IN_ALBUM(
|
||||
"Disable music video in album",
|
||||
"Adds option to redirect music videos from albums."
|
||||
"Adds option to redirect music videos from albums for non-premium users."
|
||||
),
|
||||
ENABLE_OPUS_CODEC(
|
||||
"Enable OPUS codec",
|
||||
|
@ -33,6 +33,8 @@ var darkBackground = -1L
|
||||
private set
|
||||
var designBottomSheetDialog = -1L
|
||||
private set
|
||||
var elementsContainer = -1L
|
||||
private set
|
||||
var endButtonsContainer = -1L
|
||||
private set
|
||||
var floatingLayout = -1L
|
||||
@ -150,6 +152,10 @@ internal val sharedResourceIdPatch = resourcePatch(
|
||||
LAYOUT,
|
||||
"design_bottom_sheet_dialog"
|
||||
]
|
||||
elementsContainer = resourceMappings[
|
||||
ID,
|
||||
"elements_container"
|
||||
]
|
||||
endButtonsContainer = resourceMappings[
|
||||
ID,
|
||||
"end_buttons_container"
|
||||
|
@ -0,0 +1,130 @@
|
||||
package app.revanced.patches.shared.materialyou
|
||||
|
||||
import app.revanced.patcher.patch.PatchException
|
||||
import app.revanced.patcher.patch.ResourcePatchContext
|
||||
import app.revanced.util.copyXmlNode
|
||||
import app.revanced.util.inputStreamFromBundledResource
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.Node
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.StandardCopyOption
|
||||
import javax.xml.parsers.DocumentBuilderFactory
|
||||
import javax.xml.transform.OutputKeys
|
||||
import javax.xml.transform.TransformerFactory
|
||||
import javax.xml.transform.dom.DOMSource
|
||||
import javax.xml.transform.stream.StreamResult
|
||||
|
||||
private fun ResourcePatchContext.patchXmlFile(
|
||||
fromDir: String,
|
||||
toDir: String,
|
||||
xmlFileName: String,
|
||||
parentNode: String,
|
||||
targetNode: String? = null,
|
||||
attribute: String,
|
||||
newValue: String
|
||||
) {
|
||||
val resourceDirectory = get("res")
|
||||
val fromDirectory = resourceDirectory.resolve(fromDir)
|
||||
val toDirectory = resourceDirectory.resolve(toDir)
|
||||
|
||||
if (!toDirectory.isDirectory) Files.createDirectories(toDirectory.toPath())
|
||||
|
||||
val fromXmlFile = fromDirectory.resolve(xmlFileName)
|
||||
val toXmlFile = toDirectory.resolve(xmlFileName)
|
||||
|
||||
if (!fromXmlFile.exists()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!toXmlFile.exists()) {
|
||||
Files.copy(
|
||||
fromXmlFile.toPath(),
|
||||
toXmlFile.toPath()
|
||||
)
|
||||
}
|
||||
|
||||
document("res/$toDir/$xmlFileName").use { document ->
|
||||
val parentList = document.getElementsByTagName(parentNode).item(0) as Element
|
||||
|
||||
if (targetNode != null) {
|
||||
for (i in 0 until parentList.childNodes.length) {
|
||||
val node = parentList.childNodes.item(i) as? Element ?: continue
|
||||
|
||||
if (node.nodeName == targetNode && node.hasAttribute(attribute)) {
|
||||
node.getAttributeNode(attribute).textContent = newValue
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (parentList.hasAttribute(attribute)) {
|
||||
parentList.getAttributeNode(attribute).textContent = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun ResourcePatchContext.baseMaterialYou() {
|
||||
patchXmlFile(
|
||||
"drawable",
|
||||
"drawable-night-v31",
|
||||
"new_content_dot_background.xml",
|
||||
"shape",
|
||||
"solid",
|
||||
"android:color",
|
||||
"@android:color/system_accent1_100"
|
||||
)
|
||||
patchXmlFile(
|
||||
"drawable",
|
||||
"drawable-night-v31",
|
||||
"new_content_dot_background_cairo.xml",
|
||||
"shape",
|
||||
"solid",
|
||||
"android:color",
|
||||
"@android:color/system_accent1_100"
|
||||
)
|
||||
patchXmlFile(
|
||||
"drawable",
|
||||
"drawable-v31",
|
||||
"new_content_dot_background.xml",
|
||||
"shape",
|
||||
"solid",
|
||||
"android:color",
|
||||
"@android:color/system_accent1_200"
|
||||
)
|
||||
patchXmlFile(
|
||||
"drawable",
|
||||
"drawable-v31",
|
||||
"new_content_dot_background_cairo.xml",
|
||||
"shape",
|
||||
"solid",
|
||||
"android:color",
|
||||
"@android:color/system_accent1_200"
|
||||
)
|
||||
patchXmlFile(
|
||||
"drawable",
|
||||
"drawable-v31",
|
||||
"new_content_count_background.xml",
|
||||
"shape",
|
||||
"solid",
|
||||
"android:color",
|
||||
"@android:color/system_accent1_100"
|
||||
)
|
||||
patchXmlFile(
|
||||
"drawable",
|
||||
"drawable-v31",
|
||||
"new_content_count_background_cairo.xml",
|
||||
"shape",
|
||||
"solid",
|
||||
"android:color",
|
||||
"@android:color/system_accent1_100"
|
||||
)
|
||||
patchXmlFile(
|
||||
"layout",
|
||||
"layout-v31",
|
||||
"new_content_count.xml",
|
||||
"TextView",
|
||||
null,
|
||||
"android:textColor",
|
||||
"@android:color/system_neutral1_900"
|
||||
)
|
||||
}
|
@ -1,14 +1,13 @@
|
||||
package app.revanced.patches.youtube.layout.theme
|
||||
|
||||
import app.revanced.patcher.patch.resourcePatch
|
||||
import app.revanced.patches.shared.materialyou.baseMaterialYou
|
||||
import app.revanced.patches.youtube.utils.compatibility.Constants.COMPATIBLE_PACKAGE
|
||||
import app.revanced.patches.youtube.utils.patch.PatchList.MATERIALYOU
|
||||
import app.revanced.patches.youtube.utils.settings.ResourceUtils.addPreference
|
||||
import app.revanced.patches.youtube.utils.settings.ResourceUtils.updatePatchStatusTheme
|
||||
import app.revanced.patches.youtube.utils.settings.settingsPatch
|
||||
import app.revanced.util.copyXmlNode
|
||||
import org.w3c.dom.Element
|
||||
import java.nio.file.Files
|
||||
|
||||
@Suppress("unused")
|
||||
val materialYouPatch = resourcePatch(
|
||||
@ -24,117 +23,7 @@ val materialYouPatch = resourcePatch(
|
||||
)
|
||||
|
||||
execute {
|
||||
fun patchXmlFile(
|
||||
fromDir: String,
|
||||
toDir: String,
|
||||
xmlFileName: String,
|
||||
parentNode: String,
|
||||
targetNode: String? = null,
|
||||
attribute: String,
|
||||
newValue: String
|
||||
) {
|
||||
val resourceDirectory = get("res")
|
||||
val fromDirectory = resourceDirectory.resolve(fromDir)
|
||||
val toDirectory = resourceDirectory.resolve(toDir)
|
||||
|
||||
if (!toDirectory.isDirectory) Files.createDirectories(toDirectory.toPath())
|
||||
|
||||
val fromXmlFile = fromDirectory.resolve(xmlFileName)
|
||||
val toXmlFile = toDirectory.resolve(xmlFileName)
|
||||
|
||||
if (!fromXmlFile.exists()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!toXmlFile.exists()) {
|
||||
Files.copy(
|
||||
fromXmlFile.toPath(),
|
||||
toXmlFile.toPath()
|
||||
)
|
||||
}
|
||||
|
||||
document("res/$toDir/$xmlFileName").use { document ->
|
||||
val parentList = document.getElementsByTagName(parentNode).item(0) as Element
|
||||
|
||||
if (targetNode != null) {
|
||||
for (i in 0 until parentList.childNodes.length) {
|
||||
val node = parentList.childNodes.item(i) as? Element ?: continue
|
||||
|
||||
if (node.nodeName == targetNode && node.hasAttribute(attribute)) {
|
||||
node.getAttributeNode(attribute).textContent = newValue
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (parentList.hasAttribute(attribute)) {
|
||||
parentList.getAttributeNode(attribute).textContent = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
patchXmlFile(
|
||||
"drawable",
|
||||
"drawable-night-v31",
|
||||
"new_content_dot_background.xml",
|
||||
"shape",
|
||||
"solid",
|
||||
"android:color",
|
||||
"@android:color/system_accent1_100"
|
||||
)
|
||||
patchXmlFile(
|
||||
"drawable",
|
||||
"drawable-night-v31",
|
||||
"new_content_dot_background_cairo.xml",
|
||||
"shape",
|
||||
"solid",
|
||||
"android:color",
|
||||
"@android:color/system_accent1_100"
|
||||
)
|
||||
patchXmlFile(
|
||||
"drawable",
|
||||
"drawable-v31",
|
||||
"new_content_dot_background.xml",
|
||||
"shape",
|
||||
"solid",
|
||||
"android:color",
|
||||
"@android:color/system_accent1_200"
|
||||
)
|
||||
patchXmlFile(
|
||||
"drawable",
|
||||
"drawable-v31",
|
||||
"new_content_dot_background_cairo.xml",
|
||||
"shape",
|
||||
"solid",
|
||||
"android:color",
|
||||
"@android:color/system_accent1_200"
|
||||
)
|
||||
patchXmlFile(
|
||||
"drawable",
|
||||
"drawable-v31",
|
||||
"new_content_count_background.xml",
|
||||
"shape",
|
||||
"solid",
|
||||
"android:color",
|
||||
"@android:color/system_accent1_100"
|
||||
)
|
||||
patchXmlFile(
|
||||
"drawable",
|
||||
"drawable-v31",
|
||||
"new_content_count_background_cairo.xml",
|
||||
"shape",
|
||||
"solid",
|
||||
"android:color",
|
||||
"@android:color/system_accent1_100"
|
||||
)
|
||||
patchXmlFile(
|
||||
"layout",
|
||||
"layout-v31",
|
||||
"new_content_count.xml",
|
||||
"TextView",
|
||||
null,
|
||||
"android:textColor",
|
||||
"@android:color/system_neutral1_900"
|
||||
)
|
||||
baseMaterialYou()
|
||||
|
||||
copyXmlNode("youtube/materialyou/host", "values-v31/colors.xml", "resources")
|
||||
|
||||
|
Reference in New Issue
Block a user