Stand alone theme configuration for webuiX
- Add secondary color interface: isSecondaryPage (bool)
This commit is contained in:
@@ -546,9 +546,6 @@ fun Context.loadDynamicColorState() {
|
||||
ThemeConfig.useDynamicColor = enabled
|
||||
}
|
||||
|
||||
/**
|
||||
* webui X样式
|
||||
*/
|
||||
@Composable
|
||||
private fun SystemBarStyle(
|
||||
darkMode: Boolean,
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
package com.sukisu.ultra.ui.webui
|
||||
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.SystemBarStyle
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.staticCompositionLocalOf
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.paint
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.zIndex
|
||||
import android.os.Build
|
||||
import androidx.compose.animation.core.animateFloat
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.animation.core.updateTransition
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import coil.compose.AsyncImagePainter
|
||||
import coil.compose.rememberAsyncImagePainter
|
||||
import com.sukisu.ultra.ui.theme.ThemeConfig
|
||||
import com.sukisu.ultra.ui.theme.Typography
|
||||
import com.sukisu.ultra.ui.theme.loadCustomBackground
|
||||
|
||||
// 提供界面类型的本地组合
|
||||
val LocalIsSecondaryScreen = staticCompositionLocalOf { false }
|
||||
|
||||
/**
|
||||
* WebUI专用主题配置
|
||||
*/
|
||||
@Composable
|
||||
fun WebUIXTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
dynamicColor: Boolean = true,
|
||||
isSecondaryScreen: Boolean = false,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
if (!ThemeConfig.backgroundImageLoaded && !ThemeConfig.preventBackgroundRefresh) {
|
||||
context.loadCustomBackground()
|
||||
ThemeConfig.backgroundImageLoaded = false
|
||||
}
|
||||
}
|
||||
val colorScheme = when {
|
||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
if (darkTheme) {
|
||||
dynamicDarkColorScheme(context).let { scheme ->
|
||||
if (isSecondaryScreen) {
|
||||
scheme.copy(
|
||||
background = scheme.surfaceContainerHighest,
|
||||
surface = scheme.surfaceContainerHighest
|
||||
)
|
||||
} else {
|
||||
scheme.copy(
|
||||
background = Color.Transparent,
|
||||
surface = Color.Transparent
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dynamicLightColorScheme(context).let { scheme ->
|
||||
if (isSecondaryScreen) {
|
||||
scheme.copy(
|
||||
background = scheme.surfaceContainerHighest,
|
||||
surface = scheme.surfaceContainerHighest
|
||||
)
|
||||
} else {
|
||||
scheme.copy(
|
||||
background = Color.Transparent,
|
||||
surface = Color.Transparent
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
darkTheme -> {
|
||||
if (isSecondaryScreen) {
|
||||
darkColorScheme().copy(
|
||||
background = MaterialTheme.colorScheme.surfaceContainerHighest,
|
||||
surface = MaterialTheme.colorScheme.surfaceContainerHighest
|
||||
)
|
||||
} else {
|
||||
darkColorScheme().copy(
|
||||
background = Color.Transparent,
|
||||
surface = Color.Transparent
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
if (isSecondaryScreen) {
|
||||
lightColorScheme().copy(
|
||||
background = MaterialTheme.colorScheme.surfaceContainerHighest,
|
||||
surface = MaterialTheme.colorScheme.surfaceContainerHighest
|
||||
)
|
||||
} else {
|
||||
lightColorScheme().copy(
|
||||
background = Color.Transparent,
|
||||
surface = Color.Transparent
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ConfigureSystemBars(darkTheme)
|
||||
|
||||
val backgroundUri = remember { mutableStateOf(ThemeConfig.customBackgroundUri) }
|
||||
|
||||
LaunchedEffect(ThemeConfig.customBackgroundUri) {
|
||||
backgroundUri.value = ThemeConfig.customBackgroundUri
|
||||
}
|
||||
val bgImagePainter = backgroundUri.value?.let {
|
||||
rememberAsyncImagePainter(
|
||||
model = it,
|
||||
onError = {
|
||||
ThemeConfig.backgroundImageLoaded = false
|
||||
},
|
||||
onSuccess = {
|
||||
ThemeConfig.backgroundImageLoaded = true
|
||||
ThemeConfig.isThemeChanging = false
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// 背景透明度动画
|
||||
val transition = updateTransition(
|
||||
targetState = ThemeConfig.backgroundImageLoaded,
|
||||
label = "bgTransition"
|
||||
)
|
||||
|
||||
val bgAlpha by transition.animateFloat(
|
||||
label = "bgAlpha",
|
||||
transitionSpec = {
|
||||
spring(
|
||||
dampingRatio = 0.8f,
|
||||
stiffness = 300f
|
||||
)
|
||||
}
|
||||
) { loaded -> if (loaded) 1f else 0f }
|
||||
CompositionLocalProvider(LocalIsSecondaryScreen provides isSecondaryScreen) {
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = Typography,
|
||||
) {
|
||||
if (isSecondaryScreen) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(MaterialTheme.colorScheme.surfaceContainerHighest)
|
||||
) {
|
||||
content()
|
||||
}
|
||||
} else {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.zIndex(-2f)
|
||||
.background(if (darkTheme) Color.Black else Color.White)
|
||||
)
|
||||
|
||||
backgroundUri.value?.let { uri ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.zIndex(-1f)
|
||||
.alpha(bgAlpha)
|
||||
) {
|
||||
bgImagePainter?.let { painter ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.paint(
|
||||
painter = painter,
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
.graphicsLayer {
|
||||
alpha = (painter.state as? AsyncImagePainter.State.Success)?.let { 1f } ?: 0f
|
||||
}
|
||||
)
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
if (darkTheme) Color.Black.copy(alpha = 0.6f)
|
||||
else Color.White.copy(alpha = 0.1f)
|
||||
)
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(
|
||||
Brush.radialGradient(
|
||||
colors = listOf(
|
||||
Color.Transparent,
|
||||
if (darkTheme) Color.Black.copy(alpha = 0.5f)
|
||||
else Color.Black.copy(alpha = 0.2f)
|
||||
),
|
||||
radius = 1200f
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.zIndex(1f)
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前界面是否为二级界面
|
||||
*/
|
||||
@Composable
|
||||
fun isSecondaryScreen(): Boolean {
|
||||
return LocalIsSecondaryScreen.current
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置WebUI的系统栏样式
|
||||
*/
|
||||
@Composable
|
||||
private fun ConfigureSystemBars(
|
||||
darkMode: Boolean,
|
||||
statusBarScrim: Color = Color.Transparent,
|
||||
navigationBarScrim: Color = Color.Transparent
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val activity = context as ComponentActivity
|
||||
|
||||
SideEffect {
|
||||
activity.enableEdgeToEdge(
|
||||
statusBarStyle = SystemBarStyle.auto(
|
||||
statusBarScrim.toArgb(),
|
||||
statusBarScrim.toArgb()
|
||||
) { darkMode },
|
||||
navigationBarStyle = when {
|
||||
darkMode -> SystemBarStyle.dark(
|
||||
navigationBarScrim.toArgb()
|
||||
)
|
||||
else -> SystemBarStyle.light(
|
||||
navigationBarScrim.toArgb(),
|
||||
navigationBarScrim.toArgb()
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,6 @@ import com.dergoogler.mmrl.ui.component.Loading
|
||||
import com.dergoogler.mmrl.webui.screen.WebUIScreen
|
||||
import com.dergoogler.mmrl.webui.util.rememberWebUIOptions
|
||||
import com.sukisu.ultra.BuildConfig
|
||||
import com.sukisu.ultra.ui.theme.KernelSUTheme
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@@ -69,7 +68,7 @@ class WebUIXActivity : ComponentActivity() {
|
||||
val prefs = getSharedPreferences("settings", MODE_PRIVATE)
|
||||
|
||||
setContent {
|
||||
KernelSUTheme {
|
||||
WebUIXTheme {
|
||||
var isLoading by remember { mutableStateOf(true) }
|
||||
|
||||
LaunchedEffect(Platform.isAlive) {
|
||||
@@ -82,8 +81,7 @@ class WebUIXActivity : ComponentActivity() {
|
||||
|
||||
if (isLoading) {
|
||||
Loading()
|
||||
|
||||
return@KernelSUTheme
|
||||
return@WebUIXTheme
|
||||
}
|
||||
|
||||
val webDebugging = prefs.getBoolean("enable_web_debugging", false)
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.sukisu.ultra.ui.util.controlKpmModule
|
||||
import com.sukisu.ultra.ui.util.listKpmModules
|
||||
import java.io.File
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
class WebViewInterface(
|
||||
wxOptions: WXOptions,
|
||||
@@ -36,6 +37,12 @@ class WebViewInterface(
|
||||
|
||||
private val modDir get() = "/data/adb/modules/${modId.id}"
|
||||
|
||||
@Composable
|
||||
@JavascriptInterface
|
||||
fun isSecondaryPage(): Boolean {
|
||||
return isSecondaryScreen()
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun exec(cmd: String): String {
|
||||
return withNewRootShell(true) { ShellUtils.fastCmd(this, cmd) }
|
||||
|
||||
Reference in New Issue
Block a user