manager: provide monet color to webui (#2981)
Provide app color scheme using css variable in suPath, follow MMRL monet
color scheme standard since some module has already support this for a
while. This will not break current module's WebUI, it is an opt-in
feature, you'll need to import before using it. Example:
```css
@import url('https://mui.kernelsu.org/internal/colors.css');
:root {
--my-background-color: var(--surface, #FEF7FF);
--my-text-color: var(--onSurface, #1D1B20);
}
```
This is only provided when monet color is selected in settings.
Co-Authored-By: Der_Googler
<54764558+dergoogler@users.noreply.github.com>
Co-Authored-By: Rifat Azad <33044977+rifsxd@users.noreply.github.com>
Signed-off-by: KOWX712 <leecc0503@gmail.com>
---------
Signed-off-by: KOWX712 <leecc0503@gmail.com>
Co-authored-by: Der_Googler <54764558+dergoogler@users.noreply.github.com>
Co-authored-by: Rifat Azad <33044977+rifsxd@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -3,6 +3,7 @@ package com.sukisu.ultra.ui.theme
|
|||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import com.sukisu.ultra.ui.webui.MonetColorsProvider.UpdateCss
|
||||||
import top.yukonga.miuix.kmp.theme.ColorSchemeMode
|
import top.yukonga.miuix.kmp.theme.ColorSchemeMode
|
||||||
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
||||||
import top.yukonga.miuix.kmp.theme.ThemeController
|
import top.yukonga.miuix.kmp.theme.ThemeController
|
||||||
@@ -37,6 +38,9 @@ fun KernelSUTheme(
|
|||||||
}
|
}
|
||||||
return MiuixTheme(
|
return MiuixTheme(
|
||||||
controller = controller,
|
controller = controller,
|
||||||
content = content
|
content = {
|
||||||
|
UpdateCss()
|
||||||
|
content()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
package com.sukisu.ultra.ui.webui
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
||||||
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author rifsxd
|
||||||
|
* @date 2025/6/2.
|
||||||
|
*/
|
||||||
|
object MonetColorsProvider {
|
||||||
|
|
||||||
|
private val colorsCss: AtomicReference<String?> = AtomicReference(null)
|
||||||
|
|
||||||
|
fun getColorsCss(): String {
|
||||||
|
return colorsCss.get() ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun UpdateCss() {
|
||||||
|
val colorScheme = MiuixTheme.colorScheme
|
||||||
|
|
||||||
|
LaunchedEffect(colorScheme) {
|
||||||
|
// Generate CSS only when colorScheme changes
|
||||||
|
val monetColors = mapOf(
|
||||||
|
// App Base Colors
|
||||||
|
"primary" to colorScheme.primary.toCssValue(),
|
||||||
|
"onPrimary" to colorScheme.onPrimary.toCssValue(),
|
||||||
|
"primaryContainer" to colorScheme.primaryContainer.toCssValue(),
|
||||||
|
"onPrimaryContainer" to colorScheme.onPrimaryContainer.toCssValue(),
|
||||||
|
"inversePrimary" to colorScheme.primaryVariant.toCssValue(),
|
||||||
|
"secondary" to colorScheme.secondary.toCssValue(),
|
||||||
|
"onSecondary" to colorScheme.onSecondary.toCssValue(),
|
||||||
|
"secondaryContainer" to colorScheme.secondaryContainer.toCssValue(),
|
||||||
|
"onSecondaryContainer" to colorScheme.onSecondaryContainer.toCssValue(),
|
||||||
|
"tertiary" to colorScheme.tertiaryContainerVariant.toCssValue(),
|
||||||
|
"onTertiary" to colorScheme.tertiaryContainer.toCssValue(),
|
||||||
|
"tertiaryContainer" to colorScheme.tertiaryContainer.toCssValue(),
|
||||||
|
"onTertiaryContainer" to colorScheme.onTertiaryContainer.toCssValue(),
|
||||||
|
"background" to colorScheme.background.toCssValue(),
|
||||||
|
"onBackground" to colorScheme.onBackground.toCssValue(),
|
||||||
|
"surface" to colorScheme.surface.toCssValue(),
|
||||||
|
"tonalSurface" to colorScheme.surfaceContainer.toCssValue(),
|
||||||
|
"onSurface" to colorScheme.onSurface.toCssValue(),
|
||||||
|
"surfaceVariant" to colorScheme.surfaceVariant.toCssValue(),
|
||||||
|
"onSurfaceVariant" to colorScheme.onSurfaceVariantSummary.toCssValue(),
|
||||||
|
"surfaceTint" to colorScheme.surface.toCssValue(),
|
||||||
|
"inverseSurface" to colorScheme.disabledOnSurface.toCssValue(),
|
||||||
|
"inverseOnSurface" to colorScheme.surfaceContainer.toCssValue(),
|
||||||
|
"error" to colorScheme.error.toCssValue(),
|
||||||
|
"onError" to colorScheme.onError.toCssValue(),
|
||||||
|
"errorContainer" to colorScheme.errorContainer.toCssValue(),
|
||||||
|
"onErrorContainer" to colorScheme.onErrorContainer.toCssValue(),
|
||||||
|
"outline" to colorScheme.outline.toCssValue(),
|
||||||
|
"outlineVariant" to colorScheme.dividerLine.toCssValue(),
|
||||||
|
"scrim" to colorScheme.windowDimming.toCssValue(),
|
||||||
|
"surfaceBright" to colorScheme.surface.toCssValue(),
|
||||||
|
"surfaceDim" to colorScheme.surface.toCssValue(),
|
||||||
|
"surfaceContainer" to colorScheme.surfaceContainer.toCssValue(),
|
||||||
|
"surfaceContainerHigh" to colorScheme.surfaceContainerHigh.toCssValue(),
|
||||||
|
"surfaceContainerHighest" to colorScheme.surfaceContainerHighest.toCssValue(),
|
||||||
|
"surfaceContainerLow" to colorScheme.surfaceContainer.toCssValue(),
|
||||||
|
"surfaceContainerLowest" to colorScheme.surfaceContainer.toCssValue(),
|
||||||
|
"filledTonalButtonContentColor" to colorScheme.onPrimaryContainer.toCssValue(),
|
||||||
|
"filledTonalButtonContainerColor" to colorScheme.secondaryContainer.toCssValue(),
|
||||||
|
"filledTonalButtonDisabledContentColor" to colorScheme.onSurfaceVariantSummary.toCssValue(),
|
||||||
|
"filledTonalButtonDisabledContainerColor" to colorScheme.surfaceVariant.toCssValue(),
|
||||||
|
"filledCardContentColor" to colorScheme.onPrimaryContainer.toCssValue(),
|
||||||
|
"filledCardContainerColor" to colorScheme.primaryContainer.toCssValue(),
|
||||||
|
"filledCardDisabledContentColor" to colorScheme.onSurfaceVariantSummary.toCssValue(),
|
||||||
|
"filledCardDisabledContainerColor" to colorScheme.surfaceVariant.toCssValue()
|
||||||
|
)
|
||||||
|
|
||||||
|
colorsCss.set(monetColors.toCssVars())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Map<String, String>.toCssVars(): String {
|
||||||
|
return buildString {
|
||||||
|
append(":root {\n")
|
||||||
|
for ((k, v) in this@toCssVars) {
|
||||||
|
append(" --$k: $v;\n")
|
||||||
|
}
|
||||||
|
append("}\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Color.toCssValue(): String {
|
||||||
|
fun Float.toHex(): String {
|
||||||
|
return (this * 255).toInt().coerceIn(0, 255).toString(16).padStart(2, '0')
|
||||||
|
}
|
||||||
|
return if (alpha == 1f) {
|
||||||
|
"#${red.toHex()}${green.toHex()}${blue.toHex()}"
|
||||||
|
} else {
|
||||||
|
"#${red.toHex()}${green.toHex()}${blue.toHex()}${alpha.toHex()}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -61,6 +61,7 @@ public final class SuFilePathHandler implements WebViewAssetLoader.PathHandler {
|
|||||||
|
|
||||||
private final Shell mShell;
|
private final Shell mShell;
|
||||||
private final InsetsSupplier mInsetsSupplier;
|
private final InsetsSupplier mInsetsSupplier;
|
||||||
|
private final Context mContext;
|
||||||
|
|
||||||
public interface InsetsSupplier {
|
public interface InsetsSupplier {
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -93,6 +94,7 @@ public final class SuFilePathHandler implements WebViewAssetLoader.PathHandler {
|
|||||||
*/
|
*/
|
||||||
public SuFilePathHandler(@NonNull Context context, @NonNull File directory, Shell rootShell, @NonNull InsetsSupplier insetsSupplier) {
|
public SuFilePathHandler(@NonNull Context context, @NonNull File directory, Shell rootShell, @NonNull InsetsSupplier insetsSupplier) {
|
||||||
try {
|
try {
|
||||||
|
mContext = context;
|
||||||
mInsetsSupplier = insetsSupplier;
|
mInsetsSupplier = insetsSupplier;
|
||||||
mDirectory = new File(getCanonicalDirPath(directory));
|
mDirectory = new File(getCanonicalDirPath(directory));
|
||||||
if (!isAllowedInternalStorageDir(context)) {
|
if (!isAllowedInternalStorageDir(context)) {
|
||||||
@@ -149,6 +151,18 @@ public final class SuFilePathHandler implements WebViewAssetLoader.PathHandler {
|
|||||||
new ByteArrayInputStream(css.getBytes(StandardCharsets.UTF_8))
|
new ByteArrayInputStream(css.getBytes(StandardCharsets.UTF_8))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if ("internal/colors.css".equals(path)) {
|
||||||
|
int colorMode = mContext.getSharedPreferences("settings", Context.MODE_PRIVATE).getInt("color_mode", 0);
|
||||||
|
String css = "";
|
||||||
|
if (colorMode >= 3 && colorMode <= 5) {
|
||||||
|
css = MonetColorsProvider.INSTANCE.getColorsCss();
|
||||||
|
}
|
||||||
|
return new WebResourceResponse(
|
||||||
|
"text/css",
|
||||||
|
"utf-8",
|
||||||
|
new ByteArrayInputStream(css.getBytes(StandardCharsets.UTF_8))
|
||||||
|
);
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
File file = getCanonicalFileIfChild(mDirectory, path);
|
File file = getCanonicalFileIfChild(mDirectory, path);
|
||||||
if (file != null) {
|
if (file != null) {
|
||||||
|
|||||||
Reference in New Issue
Block a user