diff --git a/extensions/shared/src/main/java/app/revanced/extension/music/patches/navigation/NavigationPatch.java b/extensions/shared/src/main/java/app/revanced/extension/music/patches/navigation/NavigationPatch.java index bf99b8fe6..904aced74 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/music/patches/navigation/NavigationPatch.java +++ b/extensions/shared/src/main/java/app/revanced/extension/music/patches/navigation/NavigationPatch.java @@ -8,13 +8,16 @@ import android.widget.TextView; import androidx.annotation.NonNull; +import app.revanced.extension.music.patches.utils.PatchStatus; import app.revanced.extension.music.settings.Settings; import app.revanced.extension.shared.utils.ResourceUtils; @SuppressWarnings("unused") public class NavigationPatch { - private static final int colorGrey12 = - ResourceUtils.getColor("revanced_color_grey_12"); + private static final int colorGrey12 = PatchStatus.DarkTheme() + ? ResourceUtils.getColor("ytm_color_grey_12") + : ResourceUtils.getColor("revanced_color_grey_12"); + public static Enum lastPivotTab; public static int enableBlackNavigationBar() { diff --git a/extensions/shared/src/main/java/app/revanced/extension/music/patches/utils/DrawableColorPatch.java b/extensions/shared/src/main/java/app/revanced/extension/music/patches/utils/DrawableColorPatch.java index ddb7b0101..febdf3cb3 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/music/patches/utils/DrawableColorPatch.java +++ b/extensions/shared/src/main/java/app/revanced/extension/music/patches/utils/DrawableColorPatch.java @@ -1,21 +1,44 @@ package app.revanced.extension.music.patches.utils; +import android.graphics.drawable.Drawable; +import android.view.ViewGroup; +import android.widget.ImageView; + +import org.apache.commons.lang3.ArrayUtils; + +import app.revanced.extension.shared.utils.ResourceUtils; +import app.revanced.extension.shared.utils.Utils; + @SuppressWarnings("unused") public class DrawableColorPatch { private static final int[] DARK_VALUES = { - -14606047 // comments box background + -14606047, // comments box background + -16579837, // button container background in album + -16777216, // button container background in playlist }; - public static int getLithoColor(int originalValue) { - if (anyEquals(originalValue, DARK_VALUES)) - return -16777215; + // background colors + private static final Drawable headerGradient = + ResourceUtils.getDrawable("revanced_header_gradient"); + private static final int blackColor = + ResourceUtils.getColor("yt_black1"); - return originalValue; + public static int getLithoColor(int originalValue) { + return ArrayUtils.contains(DARK_VALUES, originalValue) + ? blackColor + : originalValue; } - private static boolean anyEquals(int value, int... of) { - for (int v : of) if (value == v) return true; - return false; + public static void setHeaderGradient(ViewGroup viewGroup) { + viewGroup.getViewTreeObserver().addOnGlobalLayoutListener(() -> Utils.runOnMainThreadDelayed(() -> { + if (!(viewGroup.getChildAt(0) instanceof ViewGroup parentViewGroup)) + return; + if (!(parentViewGroup.getChildAt(0) instanceof ImageView gradientView)) + return; + if (headerGradient != null) { + gradientView.setForeground(headerGradient); + } + }, 0)); } } diff --git a/extensions/shared/src/main/java/app/revanced/extension/music/patches/utils/PatchStatus.java b/extensions/shared/src/main/java/app/revanced/extension/music/patches/utils/PatchStatus.java index 08abcf94a..1976df37c 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/music/patches/utils/PatchStatus.java +++ b/extensions/shared/src/main/java/app/revanced/extension/music/patches/utils/PatchStatus.java @@ -2,6 +2,11 @@ package app.revanced.extension.music.patches.utils; @SuppressWarnings("unused") public class PatchStatus { + public static boolean DarkTheme() { + // Replace this with true if the Dark theme patch succeeds + return false; + } + public static boolean SpoofAppVersionDefaultBoolean() { return false; } diff --git a/patches/src/main/kotlin/app/revanced/patches/music/general/amoled/AmoledPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/general/amoled/AmoledPatch.kt deleted file mode 100644 index c2a79075d..000000000 --- a/patches/src/main/kotlin/app/revanced/patches/music/general/amoled/AmoledPatch.kt +++ /dev/null @@ -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) - - } -} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/theme/DarkThemePatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/theme/DarkThemePatch.kt new file mode 100644 index 000000000..c13c3e7e7 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/theme/DarkThemePatch.kt @@ -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(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) + + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/theme/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/theme/Fingerprints.kt new file mode 100644 index 000000000..7b5a00b94 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/theme/Fingerprints.kt @@ -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) +) diff --git a/patches/src/main/kotlin/app/revanced/patches/music/utils/patch/PatchList.kt b/patches/src/main/kotlin/app/revanced/patches/music/utils/patch/PatchList.kt index 2ec3454ce..11d02b637 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/utils/patch/PatchList.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/utils/patch/PatchList.kt @@ -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", diff --git a/patches/src/main/kotlin/app/revanced/patches/music/utils/resourceid/SharedResourceIdPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/utils/resourceid/SharedResourceIdPatch.kt index 94d001193..3363e56b0 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/utils/resourceid/SharedResourceIdPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/utils/resourceid/SharedResourceIdPatch.kt @@ -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" diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/materialyou/BaseMaterialYouPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/materialyou/BaseMaterialYouPatch.kt new file mode 100644 index 000000000..d95d24c3c --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/shared/materialyou/BaseMaterialYouPatch.kt @@ -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" + ) +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/MaterialYouPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/MaterialYouPatch.kt index 9688864a0..c148fcb4d 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/MaterialYouPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/theme/MaterialYouPatch.kt @@ -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") diff --git a/patches/src/main/resources/music/materialyou/host/values-v31/colors.xml b/patches/src/main/resources/music/materialyou/host/values-v31/colors.xml new file mode 100644 index 000000000..16d8f326d --- /dev/null +++ b/patches/src/main/resources/music/materialyou/host/values-v31/colors.xml @@ -0,0 +1,20 @@ + + + @android:color/system_neutral1_900 + @android:color/system_neutral1_900 + @android:color/system_neutral1_900 + @android:color/system_neutral1_900 + @android:color/system_neutral1_900 + @android:color/system_neutral1_900 + @android:color/system_neutral1_900 + @android:color/system_neutral1_900 + @android:color/system_neutral1_900 + @android:color/system_neutral1_900 + @android:color/system_neutral1_900 + @android:color/system_neutral1_900 + @android:color/system_neutral1_900 + @android:color/system_neutral1_900 + @android:color/system_neutral1_900 + @android:color/system_neutral1_900 + @android:color/system_neutral1_900 + diff --git a/patches/src/main/resources/music/theme/drawable/revanced_header_gradient.xml b/patches/src/main/resources/music/theme/drawable/revanced_header_gradient.xml new file mode 100644 index 000000000..f024c86f5 --- /dev/null +++ b/patches/src/main/resources/music/theme/drawable/revanced_header_gradient.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file