manager: Support monet colors

Co-authored-by: YuKongA <70465933+YuKongA@users.noreply.github.com>
This commit is contained in:
ShirkNeko
2025-11-20 12:02:49 +08:00
parent d2a6fa4513
commit 8e7f1f1cc7
23 changed files with 229 additions and 397 deletions

View File

@@ -1,5 +1,7 @@
package com.sukisu.ultra.ui
import android.content.SharedPreferences
import androidx.compose.ui.graphics.Color
import android.os.Build
import android.os.Bundle
import androidx.activity.ComponentActivity
@@ -19,11 +21,14 @@ import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import com.sukisu.ultra.ui.util.getKpmVersion
@@ -69,7 +74,24 @@ class MainActivity : ComponentActivity() {
if (isManager && !Natives.requireNewKernel()) install()
setContent {
KernelSUTheme {
val context = LocalActivity.current ?: this
val prefs = context.getSharedPreferences("settings", MODE_PRIVATE)
var colorMode by remember { mutableIntStateOf(prefs.getInt("color_mode", 0)) }
var keyColorInt by remember { mutableIntStateOf(prefs.getInt("key_color", 0)) }
val keyColor = remember(keyColorInt) { if (keyColorInt == 0) null else Color(keyColorInt) }
DisposableEffect(prefs) {
val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
when (key) {
"color_mode" -> colorMode = prefs.getInt("color_mode", 0)
"key_color" -> keyColorInt = prefs.getInt("key_color", 0)
}
}
prefs.registerOnSharedPreferenceChangeListener(listener)
onDispose { prefs.unregisterOnSharedPreferenceChangeListener(listener) }
}
KernelSUTheme(colorMode = colorMode, keyColor = keyColor) {
val navController = rememberNavController()
Scaffold {

View File

@@ -11,6 +11,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import top.yukonga.miuix.kmp.basic.Text
import top.yukonga.miuix.kmp.extra.DropdownColors
import top.yukonga.miuix.kmp.extra.DropdownDefaults
import top.yukonga.miuix.kmp.theme.MiuixTheme
@Composable

View File

@@ -1,299 +0,0 @@
package com.sukisu.ultra.ui.component
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.widthIn
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.key
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.BlendModeColorFilter
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.hapticfeedback.HapticFeedback
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import top.yukonga.miuix.kmp.basic.BasicComponent
import top.yukonga.miuix.kmp.basic.BasicComponentColors
import top.yukonga.miuix.kmp.basic.BasicComponentDefaults
import top.yukonga.miuix.kmp.basic.ListPopup
import top.yukonga.miuix.kmp.basic.ListPopupColumn
import top.yukonga.miuix.kmp.basic.PopupPositionProvider
import top.yukonga.miuix.kmp.basic.Text
import top.yukonga.miuix.kmp.icon.MiuixIcons
import top.yukonga.miuix.kmp.icon.icons.basic.ArrowUpDownIntegrated
import top.yukonga.miuix.kmp.icon.icons.basic.Check
import top.yukonga.miuix.kmp.theme.MiuixTheme
/**
* A dropdown with a title and a summary.
*
* @param items The options of the [SuperDropdown].
* @param selectedIndex The index of the selected option.
* @param title The title of the [SuperDropdown].
* @param titleColor The color of the title.
* @param summary The summary of the [SuperDropdown].
* @param summaryColor The color of the summary.
* @param dropdownColors The [DropdownColors] of the [SuperDropdown].
* @param insideMargin The margin inside the [SuperDropdown].
* @param maxHeight The maximum height of the [ListPopup].
* @param enabled Whether the [SuperDropdown] is enabled.
* @param showValue Whether to show the selected value of the [SuperDropdown].
* @param onClick The callback when the [SuperDropdown] is clicked.
* @param onSelectedIndexChange The callback when the selected index of the [SuperDropdown] is changed.
*/
@Composable
fun SuperDropdown(
items: List<String>,
selectedIndex: Int,
title: String,
titleColor: BasicComponentColors = BasicComponentDefaults.titleColor(),
summary: String? = null,
summaryColor: BasicComponentColors = BasicComponentDefaults.summaryColor(),
dropdownColors: DropdownColors = DropdownDefaults.dropdownColors(),
leftAction: (@Composable (() -> Unit))? = null,
insideMargin: PaddingValues = BasicComponentDefaults.InsideMargin,
maxHeight: Dp? = null,
enabled: Boolean = true,
showValue: Boolean = true,
onClick: (() -> Unit)? = null,
onSelectedIndexChange: ((Int) -> Unit)?,
) {
val interactionSource = remember { MutableInteractionSource() }
val isDropdownExpanded = remember { mutableStateOf(false) }
val hapticFeedback = LocalHapticFeedback.current
val itemsNotEmpty = items.isNotEmpty()
val actualEnabled = enabled && itemsNotEmpty
val actionColor = if (actualEnabled) {
MiuixTheme.colorScheme.onSurfaceVariantActions
} else {
MiuixTheme.colorScheme.disabledOnSecondaryVariant
}
val handleClick: () -> Unit = {
if (actualEnabled) {
onClick?.invoke()
isDropdownExpanded.value = !isDropdownExpanded.value
if (isDropdownExpanded.value) {
hapticFeedback.performHapticFeedback(HapticFeedbackType.ContextClick)
}
}
}
BasicComponent(
interactionSource = interactionSource,
insideMargin = insideMargin,
title = title,
titleColor = titleColor,
summary = summary,
summaryColor = summaryColor,
leftAction = if (itemsNotEmpty) {
{
SuperDropdownPopup(
items = items,
selectedIndex = selectedIndex,
isDropdownExpanded = isDropdownExpanded,
maxHeight = maxHeight,
dropdownColors = dropdownColors,
hapticFeedback = hapticFeedback,
onSelectedIndexChange = onSelectedIndexChange
)
leftAction?.invoke()
}
} else null,
rightActions = {
SuperDropdownRightActions(
showValue = showValue,
itemsNotEmpty = itemsNotEmpty,
items = items,
selectedIndex = selectedIndex,
actionColor = actionColor
)
},
onClick = handleClick,
holdDownState = isDropdownExpanded.value,
enabled = actualEnabled
)
}
@Composable
private fun SuperDropdownPopup(
items: List<String>,
selectedIndex: Int,
isDropdownExpanded: MutableState<Boolean>,
maxHeight: Dp?,
dropdownColors: DropdownColors,
hapticFeedback: HapticFeedback,
onSelectedIndexChange: ((Int) -> Unit)?
) {
val onSelectState = rememberUpdatedState(onSelectedIndexChange)
ListPopup(
show = isDropdownExpanded,
alignment = PopupPositionProvider.Align.Right,
onDismissRequest = {
isDropdownExpanded.value = false
},
maxHeight = maxHeight
) {
ListPopupColumn {
items.forEachIndexed { index, string ->
key(index) {
DropdownImpl(
text = string,
optionSize = items.size,
isSelected = selectedIndex == index,
dropdownColors = dropdownColors,
onSelectedIndexChange = { selectedIdx ->
hapticFeedback.performHapticFeedback(HapticFeedbackType.Confirm)
onSelectState.value?.invoke(selectedIdx)
isDropdownExpanded.value = false
},
index = index
)
}
}
}
}
}
@Composable
private fun RowScope.SuperDropdownRightActions(
showValue: Boolean,
itemsNotEmpty: Boolean,
items: List<String>,
selectedIndex: Int,
actionColor: Color
) {
if (showValue && itemsNotEmpty) {
Text(
modifier = Modifier.widthIn(max = 130.dp),
text = items[selectedIndex],
fontSize = MiuixTheme.textStyles.body2.fontSize,
color = actionColor,
textAlign = TextAlign.End,
overflow = TextOverflow.Ellipsis,
maxLines = 2
)
}
Image(
modifier = Modifier
.padding(start = 8.dp)
.size(10.dp, 16.dp)
.align(Alignment.CenterVertically),
imageVector = MiuixIcons.Basic.ArrowUpDownIntegrated,
colorFilter = ColorFilter.tint(actionColor),
contentDescription = null
)
}
/**
* The implementation of the dropdown.
*
* @param text The text of the current option.
* @param optionSize The size of the options.
* @param isSelected Whether the option is selected.
* @param index The index of the current option in the options.
* @param onSelectedIndexChange The callback when the index is selected.
*/
@Composable
fun DropdownImpl(
text: String,
optionSize: Int,
isSelected: Boolean,
index: Int,
dropdownColors: DropdownColors = DropdownDefaults.dropdownColors(),
onSelectedIndexChange: (Int) -> Unit
) {
val additionalTopPadding = if (index == 0) 20.dp else 12.dp
val additionalBottomPadding = if (index == optionSize - 1) 20.dp else 12.dp
val (textColor, backgroundColor) = if (isSelected) {
dropdownColors.selectedContentColor to dropdownColors.selectedContainerColor
} else {
dropdownColors.contentColor to dropdownColors.containerColor
}
val checkColor = if (isSelected) {
dropdownColors.selectedContentColor
} else {
Color.Transparent
}
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier
.clickable { onSelectedIndexChange(index) }
.background(backgroundColor)
.padding(horizontal = 20.dp)
.padding(
top = additionalTopPadding,
bottom = additionalBottomPadding
)
) {
Text(
modifier = Modifier.widthIn(max = 200.dp),
text = text,
fontSize = MiuixTheme.textStyles.body1.fontSize,
fontWeight = FontWeight.Medium,
color = textColor,
)
Image(
modifier = Modifier
.padding(start = 12.dp)
.size(20.dp),
imageVector = MiuixIcons.Basic.Check,
colorFilter = BlendModeColorFilter(checkColor, BlendMode.SrcIn),
contentDescription = null,
)
}
}
@Immutable
class DropdownColors(
val contentColor: Color,
val containerColor: Color,
val selectedContentColor: Color,
val selectedContainerColor: Color
)
object DropdownDefaults {
@Composable
fun dropdownColors(
contentColor: Color = MiuixTheme.colorScheme.onSurface,
containerColor: Color = MiuixTheme.colorScheme.surface,
selectedContentColor: Color = MiuixTheme.colorScheme.onTertiaryContainer,
selectedContainerColor: Color = MiuixTheme.colorScheme.surface
): DropdownColors {
return DropdownColors(
contentColor = contentColor,
containerColor = containerColor,
selectedContentColor = selectedContentColor,
selectedContainerColor = selectedContainerColor
)
}
}

View File

@@ -30,13 +30,12 @@ import top.yukonga.miuix.kmp.extra.SuperDialog
@Composable
fun SuperEditArrow(
modifier: Modifier = Modifier,
title: String,
titleColor: BasicComponentColors = BasicComponentDefaults.titleColor(),
defaultValue: Int = -1,
summaryColor: BasicComponentColors = BasicComponentDefaults.summaryColor(),
leftAction: @Composable (() -> Unit)? = null,
rightActionColor: RightActionColors = SuperArrowDefaults.rightActionColors(),
modifier: Modifier = Modifier,
insideMargin: PaddingValues = BasicComponentDefaults.InsideMargin,
enabled: Boolean = true,
onValueChange: ((Int) -> Unit)? = null
@@ -50,7 +49,6 @@ fun SuperEditArrow(
summary = dialogTextFieldValue.intValue.toString(),
summaryColor = summaryColor,
leftAction = leftAction,
rightActionColor = rightActionColor,
modifier = modifier,
insideMargin = insideMargin,
onClick = {

View File

@@ -1,5 +1,6 @@
package com.sukisu.ultra.ui.component
import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.LinearOutSlowInEasing
@@ -73,7 +74,6 @@ import top.yukonga.miuix.kmp.icon.MiuixIcons
import top.yukonga.miuix.kmp.icon.icons.basic.Search
import top.yukonga.miuix.kmp.icon.icons.basic.SearchCleanup
import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme
import top.yukonga.miuix.kmp.utils.BackHandler
import top.yukonga.miuix.kmp.utils.overScrollVertical
// Search Status Class

View File

@@ -15,12 +15,12 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.sukisu.ultra.Natives
import com.sukisu.ultra.R
import com.sukisu.ultra.ui.component.SuperDropdown
import com.sukisu.ultra.ui.util.listAppProfileTemplates
import com.sukisu.ultra.ui.util.setSepolicy
import com.sukisu.ultra.ui.viewmodel.getTemplateInfoById
import top.yukonga.miuix.kmp.basic.Icon
import top.yukonga.miuix.kmp.extra.SuperArrow
import top.yukonga.miuix.kmp.extra.SuperDropdown
import top.yukonga.miuix.kmp.theme.MiuixTheme
/**

View File

@@ -67,8 +67,8 @@ fun AboutScreen(navigator: DestinationsNavigator) {
val scrollBehavior = MiuixScrollBehavior()
val hazeState = remember { HazeState() }
val hazeStyle = HazeStyle(
backgroundColor = colorScheme.background,
tint = HazeTint(colorScheme.background.copy(0.8f))
backgroundColor = colorScheme.surface,
tint = HazeTint(colorScheme.surface.copy(0.8f))
)
val htmlString = stringResource(

View File

@@ -62,7 +62,6 @@ import com.sukisu.ultra.Natives
import com.sukisu.ultra.R
import com.sukisu.ultra.ui.component.AppIconImage
import com.sukisu.ultra.ui.component.DropdownItem
import com.sukisu.ultra.ui.component.SuperDropdown
import com.sukisu.ultra.ui.component.profile.AppProfileConfig
import com.sukisu.ultra.ui.component.profile.RootProfileConfig
import com.sukisu.ultra.ui.component.profile.TemplateConfig
@@ -90,6 +89,7 @@ import top.yukonga.miuix.kmp.basic.ScrollBehavior
import top.yukonga.miuix.kmp.basic.SmallTitle
import top.yukonga.miuix.kmp.basic.Text
import top.yukonga.miuix.kmp.basic.TopAppBar
import top.yukonga.miuix.kmp.extra.SuperDropdown
import top.yukonga.miuix.kmp.extra.SuperSwitch
import top.yukonga.miuix.kmp.icon.MiuixIcons
import top.yukonga.miuix.kmp.icon.icons.useful.Back
@@ -113,8 +113,8 @@ fun AppProfileScreen(
val scrollBehavior = MiuixScrollBehavior()
val hazeState = remember { HazeState() }
val hazeStyle = HazeStyle(
backgroundColor = colorScheme.background,
tint = HazeTint(colorScheme.background.copy(0.8f))
backgroundColor = colorScheme.surface,
tint = HazeTint(colorScheme.surface.copy(0.8f))
)
val scope = rememberCoroutineScope()
val failToUpdateAppProfile = stringResource(R.string.failed_to_update_app_profile).format(appInfo.label).format(appInfo.uid)

View File

@@ -78,8 +78,8 @@ fun ExecuteModuleActionScreen(navigator: DestinationsNavigator, moduleId: String
var actionResult: Boolean
val hazeState = remember { HazeState() }
val hazeStyle = HazeStyle(
backgroundColor = colorScheme.background,
tint = HazeTint(colorScheme.background.copy(0.8f))
backgroundColor = colorScheme.surface,
tint = HazeTint(colorScheme.surface.copy(0.8f))
)
LaunchedEffect(Unit) {

View File

@@ -89,6 +89,7 @@ import top.yukonga.miuix.kmp.icon.MiuixIcons
import top.yukonga.miuix.kmp.icon.icons.useful.Save
import top.yukonga.miuix.kmp.theme.MiuixTheme
import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme
import top.yukonga.miuix.kmp.theme.MiuixTheme.isDynamicColor
import top.yukonga.miuix.kmp.utils.PressFeedbackType
import top.yukonga.miuix.kmp.utils.getWindowSize
import top.yukonga.miuix.kmp.utils.overScrollVertical
@@ -104,8 +105,8 @@ fun HomePager(
val scrollBehavior = MiuixScrollBehavior()
val hazeState = remember { HazeState() }
val hazeStyle = HazeStyle(
backgroundColor = colorScheme.background,
tint = HazeTint(colorScheme.background.copy(0.8f))
backgroundColor = colorScheme.surface,
tint = HazeTint(colorScheme.surface.copy(0.8f))
)
val context = LocalContext.current
@@ -334,7 +335,11 @@ private fun StatusCard(
.weight(1f)
.fillMaxHeight(),
colors = CardDefaults.defaultColors(
color = if (isSystemInDarkTheme()) Color(0xFF1A3825) else Color(0xFFDFFAE4)
color = when {
isDynamicColor -> colorScheme.secondaryContainer
isSystemInDarkTheme() -> Color(0xFF1A3825)
else -> Color(0xFFDFFAE4)
}
),
onClick = {
if (kernelVersion.isGKI()) onClickInstall()
@@ -354,7 +359,11 @@ private fun StatusCard(
Icon(
modifier = Modifier.size(170.dp),
imageVector = Icons.Rounded.CheckCircleOutline,
tint = Color(0xFF36D167),
tint = if (isDynamicColor) {
colorScheme.primary.copy(alpha = 0.8f)
} else {
Color(0xFF36D167)
},
contentDescription = null
)
}
@@ -367,16 +376,14 @@ private fun StatusCard(
modifier = Modifier.fillMaxWidth(),
text = workingText,
fontSize = 20.sp,
fontWeight = FontWeight.SemiBold,
color = if (isSystemInDarkTheme()) Color(0xFFB8E6C5) else Color(0xFF1A5A2E)
fontWeight = FontWeight.SemiBold
)
Spacer(Modifier.height(2.dp))
Text(
modifier = Modifier.fillMaxWidth(),
text = stringResource(R.string.home_working_version, ksuVersion),
fontSize = 14.sp,
fontWeight = FontWeight.Medium,
color = if (isSystemInDarkTheme()) Color(0xFF9DD4AC) else Color(0xFF2D7A4A)
fontWeight = FontWeight.Medium
)
}
}
@@ -503,15 +510,19 @@ private fun StatusCard(
@Composable
fun WarningCard(
message: String,
color: Color = if (isSystemInDarkTheme()) Color(0XFF310808) else Color(0xFFF8E2E2),
onClick: (() -> Unit)? = null
color: Color? = null,
onClick: (() -> Unit)? = null,
) {
Card(
onClick = {
onClick?.invoke()
},
colors = CardDefaults.defaultColors(
color = color
color = color ?: when {
isDynamicColor -> colorScheme.errorContainer
isSystemInDarkTheme() -> Color(0XFF310808)
else -> Color(0xFFF8E2E2)
}
),
showIndication = onClick != null,
pressFeedbackType = PressFeedbackType.Tilt
@@ -523,7 +534,7 @@ fun WarningCard(
) {
Text(
text = message,
color = Color(0xFFF72727),
color = if (isDynamicColor) colorScheme.onErrorContainer else Color(0xFFF72727),
fontSize = 14.sp
)
}

View File

@@ -68,7 +68,6 @@ import dev.chrisbanes.haze.hazeSource
import com.sukisu.ultra.R
import com.sukisu.ultra.getKernelVersion
import com.sukisu.ultra.ui.component.ChooseKmiDialog
import com.sukisu.ultra.ui.component.SuperDropdown
import com.sukisu.ultra.ui.component.rememberConfirmDialog
import com.sukisu.ultra.ui.kernelFlash.component.SlotSelectionDialog
import com.sukisu.ultra.ui.util.LkmSelection
@@ -90,6 +89,7 @@ import top.yukonga.miuix.kmp.basic.Text
import top.yukonga.miuix.kmp.basic.TopAppBar
import top.yukonga.miuix.kmp.extra.SuperArrow
import top.yukonga.miuix.kmp.extra.SuperCheckbox
import top.yukonga.miuix.kmp.extra.SuperDropdown
import top.yukonga.miuix.kmp.icon.MiuixIcons
import top.yukonga.miuix.kmp.icon.icons.useful.Back
import top.yukonga.miuix.kmp.icon.icons.useful.Edit
@@ -282,8 +282,8 @@ fun InstallScreen(
val scrollBehavior = MiuixScrollBehavior()
val hazeState = remember { HazeState() }
val hazeStyle = HazeStyle(
backgroundColor = colorScheme.background,
tint = HazeTint(colorScheme.background.copy(0.8f))
backgroundColor = colorScheme.surface,
tint = HazeTint(colorScheme.surface.copy(0.8f))
)
Scaffold(

View File

@@ -107,8 +107,8 @@ fun KpmScreen(
}
val hazeState = remember { HazeState() }
val hazeStyle = HazeStyle(
backgroundColor = colorScheme.background,
tint = HazeTint(colorScheme.background.copy(0.8f))
backgroundColor = colorScheme.surface,
tint = HazeTint(colorScheme.surface.copy(0.8f))
)
LaunchedEffect(searchStatus.searchText) {

View File

@@ -390,7 +390,7 @@ private fun LogControlPanel(
SuperArrow(
title = stringResource(R.string.log_viewer_settings),
onClick = { isExpanded = !isExpanded },
rightText = if (isExpanded)
summary = if (isExpanded)
stringResource(R.string.log_viewer_collapse)
else
stringResource(R.string.log_viewer_expand)

View File

@@ -106,7 +106,6 @@ import com.sukisu.ultra.Natives
import com.sukisu.ultra.R
import com.sukisu.ultra.ksuApp
import com.sukisu.ultra.ui.component.ConfirmResult
import com.sukisu.ultra.ui.component.DropdownImpl
import com.sukisu.ultra.ui.component.RebootListPopup
import com.sukisu.ultra.ui.component.SearchBox
import com.sukisu.ultra.ui.component.SearchPager
@@ -137,6 +136,7 @@ import top.yukonga.miuix.kmp.basic.Switch
import top.yukonga.miuix.kmp.basic.Text
import top.yukonga.miuix.kmp.basic.TopAppBar
import top.yukonga.miuix.kmp.basic.rememberPullToRefreshState
import top.yukonga.miuix.kmp.extra.DropdownImpl
import top.yukonga.miuix.kmp.icon.MiuixIcons
import top.yukonga.miuix.kmp.icon.icons.useful.ImmersionMore
import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme
@@ -387,8 +387,8 @@ fun ModulePager(
val hazeState = remember { HazeState() }
val hazeStyle = HazeStyle(
backgroundColor = colorScheme.background,
tint = HazeTint(colorScheme.background.copy(0.8f))
backgroundColor = colorScheme.surface,
tint = HazeTint(colorScheme.surface.copy(0.8f))
)
Scaffold(
@@ -916,11 +916,11 @@ fun ModuleItem(
val textDecoration by remember(module.remove) {
mutableStateOf(if (module.remove) TextDecoration.LineThrough else null)
}
val onSurface = colorScheme.onSurface
val colorScheme = colorScheme
val secondaryContainer = colorScheme.secondaryContainer.copy(alpha = 0.8f)
val actionIconTint = remember(isDark) { onSurface.copy(alpha = if (isDark) 0.7f else 0.9f) }
val updateBg = remember(isDark) { Color(if (isDark) 0xFF25354E else 0xFFEAF2FF) }
val updateTint = remember { Color(0xFF0D84FF) }
val actionIconTint = remember(isDark) { colorScheme.onSurface.copy(alpha = if (isDark) 0.7f else 0.9f) }
val updateBg = remember(colorScheme) { colorScheme.tertiaryContainer.copy(alpha = 0.6f) }
val updateTint = remember(colorScheme) { colorScheme.onTertiaryContainer.copy(alpha = 0.8f) }
Card(
modifier = Modifier

View File

@@ -2,6 +2,7 @@ package com.sukisu.ultra.ui.screen
import android.app.Activity
import android.content.Context
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.add
@@ -22,6 +23,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
@@ -36,7 +38,6 @@ import dev.chrisbanes.haze.HazeTint
import dev.chrisbanes.haze.hazeEffect
import dev.chrisbanes.haze.hazeSource
import com.sukisu.ultra.R
import com.sukisu.ultra.ui.component.SuperDropdown
import top.yukonga.miuix.kmp.basic.Card
import top.yukonga.miuix.kmp.basic.Icon
import top.yukonga.miuix.kmp.basic.IconButton
@@ -46,6 +47,7 @@ import top.yukonga.miuix.kmp.basic.TopAppBar
import top.yukonga.miuix.kmp.icon.MiuixIcons
import top.yukonga.miuix.kmp.icon.icons.useful.Back
import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme
import top.yukonga.miuix.kmp.extra.SuperDropdown
import top.yukonga.miuix.kmp.utils.getWindowSize
import top.yukonga.miuix.kmp.utils.overScrollVertical
import top.yukonga.miuix.kmp.utils.scrollEndHaptic
@@ -58,8 +60,8 @@ fun Personalization(
val scrollBehavior = MiuixScrollBehavior()
val hazeState = remember { HazeState() }
val hazeStyle = HazeStyle(
backgroundColor = colorScheme.background,
tint = HazeTint(colorScheme.background.copy(0.8f))
backgroundColor = colorScheme.surface,
tint = HazeTint(colorScheme.surface.copy(0.8f))
)
Scaffold(
@@ -91,7 +93,6 @@ fun Personalization(
contentWindowInsets = WindowInsets.systemBars.add(WindowInsets.displayCutout).only(WindowInsetsSides.Horizontal)
) { innerPadding ->
val context = LocalContext.current
val activity = context as? Activity
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
LazyColumn(
@@ -112,34 +113,81 @@ fun Personalization(
.fillMaxWidth(),
) {
val themeItems = listOf(
stringResource(id = R.string.theme_follow_system),
stringResource(id = R.string.theme_light),
stringResource(id = R.string.theme_dark),
stringResource(id = R.string.settings_theme_mode_system),
stringResource(id = R.string.settings_theme_mode_light),
stringResource(id = R.string.settings_theme_mode_dark),
stringResource(id = R.string.settings_theme_mode_monet_system),
stringResource(id = R.string.settings_theme_mode_monet_light),
stringResource(id = R.string.settings_theme_mode_monet_dark),
)
var themeMode by rememberSaveable {
mutableIntStateOf(prefs.getInt("theme_mode", 0))
mutableIntStateOf(prefs.getInt("color_mode", 0))
}
SuperDropdown(
title = stringResource(id = R.string.theme_mode),
summary = stringResource(id = R.string.theme_mode_summary),
title = stringResource(id = R.string.settings_theme),
summary = stringResource(id = R.string.settings_theme_summary),
items = themeItems,
leftAction = {
Icon(
Icons.Rounded.Palette,
modifier = Modifier.padding(end = 16.dp),
contentDescription = stringResource(id = R.string.theme_mode),
contentDescription = stringResource(id = R.string.settings_theme),
tint = colorScheme.onBackground
)
},
selectedIndex = themeMode,
onSelectedIndexChange = { index ->
prefs.edit {
putInt("theme_mode", index)
}
prefs.edit { putInt("color_mode", index) }
themeMode = index
activity?.recreate()
}
)
AnimatedVisibility(
visible = themeMode in 3..5
) {
val colorItems = listOf(
stringResource(id = R.string.settings_key_color_default),
stringResource(id = R.string.color_blue),
stringResource(id = R.string.color_red),
stringResource(id = R.string.color_green),
stringResource(id = R.string.color_purple),
stringResource(id = R.string.color_orange),
stringResource(id = R.string.color_teal),
stringResource(id = R.string.color_pink),
stringResource(id = R.string.color_brown),
)
val colorValues = listOf(
0,
Color(0xFF1A73E8).toArgb(),
Color(0xFFEA4335).toArgb(),
Color(0xFF34A853).toArgb(),
Color(0xFF9333EA).toArgb(),
Color(0xFFFB8C00).toArgb(),
Color(0xFF009688).toArgb(),
Color(0xFFE91E63).toArgb(),
Color(0xFF795548).toArgb(),
)
var keyColorIndex by rememberSaveable {
mutableIntStateOf(
colorValues.indexOf(prefs.getInt("key_color", 0)).coerceAtLeast(0)
)
}
SuperDropdown(
title = stringResource(id = R.string.settings_key_color),
summary = stringResource(id = R.string.settings_key_color_summary),
items = colorItems,
leftAction = {
Icon(
Icons.Rounded.Palette,
modifier = Modifier.padding(end = 16.dp),
contentDescription = stringResource(id = R.string.settings_key_color),
tint = colorScheme.onBackground
)
},
selectedIndex = keyColorIndex,
onSelectedIndexChange = { index ->
prefs.edit { putInt("key_color", colorValues[index]) }
keyColorIndex = index
}
)
}
@@ -147,3 +195,4 @@ fun Personalization(
}
}
}
}

View File

@@ -8,6 +8,7 @@ import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
@@ -19,6 +20,8 @@ import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.icons.rounded.Palette
import androidx.compose.ui.graphics.toArgb
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.CleaningServices
import androidx.compose.material.icons.filled.Groups
@@ -75,7 +78,6 @@ import com.sukisu.ultra.ui.component.ConfirmResult
import com.sukisu.ultra.ui.component.DynamicManagerCard
import com.sukisu.ultra.ui.component.KsuIsValid
import com.sukisu.ultra.ui.component.SendLogDialog
import com.sukisu.ultra.ui.component.SuperDropdown
import com.sukisu.ultra.ui.component.UninstallDialog
import com.sukisu.ultra.ui.component.rememberConfirmDialog
import com.sukisu.ultra.ui.component.rememberLoadingDialog
@@ -95,6 +97,7 @@ import top.yukonga.miuix.kmp.basic.MiuixScrollBehavior
import top.yukonga.miuix.kmp.basic.Scaffold
import top.yukonga.miuix.kmp.basic.TopAppBar
import top.yukonga.miuix.kmp.extra.SuperArrow
import top.yukonga.miuix.kmp.extra.SuperDropdown
import top.yukonga.miuix.kmp.extra.SuperSwitch
import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme
import top.yukonga.miuix.kmp.utils.getWindowSize
@@ -114,8 +117,8 @@ fun SettingPager(
val scrollBehavior = MiuixScrollBehavior()
val hazeState = remember { HazeState() }
val hazeStyle = HazeStyle(
backgroundColor = colorScheme.background,
tint = HazeTint(colorScheme.background.copy(0.8f))
backgroundColor = colorScheme.surface,
tint = HazeTint(colorScheme.surface.copy(0.8f))
)
Scaffold(

View File

@@ -133,8 +133,8 @@ fun SuperUserPager(
}
val hazeState = remember { HazeState() }
val hazeStyle = HazeStyle(
backgroundColor = colorScheme.background,
tint = HazeTint(colorScheme.background.copy(0.8f))
backgroundColor = colorScheme.surface,
tint = HazeTint(colorScheme.surface.copy(0.8f))
)
Scaffold(
@@ -469,19 +469,23 @@ private fun GroupItem(
) {
val isDark = isSystemInDarkTheme()
val colorScheme = colorScheme
val bg = remember { colorScheme.secondaryContainer.copy(alpha = 0.8f) }
val rootBg = remember { colorScheme.tertiaryContainer.copy(alpha = 0.6f) }
val unmountBg = remember(isDark) { if (isDark) Color.White.copy(alpha = 0.4f) else Color.Black.copy(alpha = 0.3f) }
val fg = remember { colorScheme.onSecondaryContainer }
val rootFg = remember { colorScheme.onTertiaryContainer.copy(alpha = 0.8f) }
val unmountFg = remember(isDark) { if (isDark) Color.Black.copy(alpha = 0.4f) else Color.White.copy(alpha = 0.8f) }
val bg = remember(colorScheme) { colorScheme.secondaryContainer.copy(alpha = 0.8f) }
val rootBg = remember(colorScheme) { colorScheme.tertiaryContainer.copy(alpha = 0.6f) }
val unmountBg = remember(isDark, colorScheme) {
if (isDark) Color.White.copy(alpha = 0.4f) else Color.Black.copy(alpha = 0.3f)
}
val fg = remember(colorScheme) { colorScheme.onSecondaryContainer }
val rootFg = remember(colorScheme) { colorScheme.onTertiaryContainer.copy(alpha = 0.8f) }
val unmountFg = remember(isDark, colorScheme) {
if (isDark) Color.Black.copy(alpha = 0.4f) else Color.White.copy(alpha = 0.8f)
}
val userId = group.uid / 100000
val packageInfo = group.primary.packageInfo
val applicationInfo = packageInfo.applicationInfo
val hasSharedUserId = !packageInfo.sharedUserId.isNullOrEmpty()
val isSystemApp = applicationInfo?.flags?.and(ApplicationInfo.FLAG_SYSTEM) != 0
|| applicationInfo.flags.and(ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0
val tags = remember(group.uid, group.anyAllowSu, group.anyCustom) {
val tags = remember(group.uid, group.anyAllowSu, group.anyCustom, colorScheme) {
buildList {
if (group.anyAllowSu) add(StatusMeta("ROOT", rootBg, rootFg))
if (Natives.uidShouldUmount(group.uid)) add(StatusMeta("UMOUNT", unmountBg, unmountFg))

View File

@@ -164,8 +164,8 @@ fun AppProfileTemplateScreen(
)
val hazeState = remember { HazeState() }
val hazeStyle = HazeStyle(
backgroundColor = colorScheme.background,
tint = HazeTint(colorScheme.background.copy(0.8f))
backgroundColor = colorScheme.surface,
tint = HazeTint(colorScheme.surface.copy(0.8f))
)
Scaffold(

View File

@@ -87,8 +87,8 @@ fun TemplateEditorScreen(
val scrollBehavior = MiuixScrollBehavior()
val hazeState = remember { HazeState() }
val hazeStyle = HazeStyle(
backgroundColor = colorScheme.background,
tint = HazeTint(colorScheme.background.copy(0.8f))
backgroundColor = colorScheme.surface,
tint = HazeTint(colorScheme.surface.copy(0.8f))
)
BackHandler {

View File

@@ -1,34 +1,42 @@
package com.sukisu.ultra.ui.theme
import android.content.Context
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.graphics.Color
import top.yukonga.miuix.kmp.theme.ColorSchemeMode
import top.yukonga.miuix.kmp.theme.MiuixTheme
import top.yukonga.miuix.kmp.theme.darkColorScheme
import top.yukonga.miuix.kmp.theme.lightColorScheme
import top.yukonga.miuix.kmp.theme.ThemeController
@Composable
fun KernelSUTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
colorMode: Int = 0,
keyColor: Color? = null,
content: @Composable () -> Unit
) {
val context = LocalContext.current
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
val themeMode = prefs.getInt("theme_mode", 0)
val isDark = isSystemInDarkTheme()
val controller = when (colorMode) {
1 -> ThemeController(ColorSchemeMode.Light)
2 -> ThemeController(ColorSchemeMode.Dark)
3 -> ThemeController(
ColorSchemeMode.MonetSystem,
keyColor = keyColor,
isDark = isDark
)
val useDarkTheme = when (themeMode) {
1 -> false // 浅色
2 -> true // 深色
else -> darkTheme // 跟随系统
}
4 -> ThemeController(
ColorSchemeMode.MonetLight,
keyColor = keyColor,
)
val colorScheme = when {
useDarkTheme -> darkColorScheme()
else -> lightColorScheme()
5 -> ThemeController(
ColorSchemeMode.MonetDark,
keyColor = keyColor,
)
else -> ThemeController(ColorSchemeMode.System)
}
MiuixTheme(
colors = colorScheme,
return MiuixTheme(
controller = controller,
content = content
)
}

View File

@@ -158,6 +158,26 @@
<string name="module_undo_uninstall_success">成功撤销卸载 %s</string>
<string name="module_undo_uninstall_failed">撤销卸载 %s 失败</string>
<string name="group_contains_apps">包含 %1$d 个应用</string>
<string name="settings_theme">主题</string>
<string name="settings_theme_summary">选择应用的主题模式。</string>
<string name="settings_theme_mode_system">跟随系统</string>
<string name="settings_theme_mode_light">浅色</string>
<string name="settings_theme_mode_dark">深色</string>
<string name="settings_theme_mode_monet_system">Monet跟随系统</string>
<string name="settings_theme_mode_monet_light">Monet浅色</string>
<string name="settings_theme_mode_monet_dark">Monet深色</string>
<string name="settings_key_color">强调色</string>
<string name="settings_key_color_summary">在使用 Monet 时自定义种子色。</string>
<string name="settings_key_color_default">默认</string>
<string name="color_blue">蓝色</string>
<string name="color_red">红色</string>
<string name="color_green">绿色</string>
<string name="color_purple">紫色</string>
<string name="color_orange">橙色</string>
<string name="color_teal">青色</string>
<string name="color_pink">粉色</string>
<string name="color_brown">棕色</string>
<!-- Customize -->
<string name="home_susfs_version">SuSFS 版本</string>
<string name="manual_hook">Manual Hook</string>
<string name="inline_hook">Inline Hook</string>
@@ -243,9 +263,6 @@
<string name="theme_settings">主题设置</string>
<string name="theme_mode">主题模式</string>
<string name="theme_mode_summary">选择应用的显示主题</string>
<string name="theme_light">浅色</string>
<string name="theme_dark">深色</string>
<string name="theme_follow_system">跟随系统</string>
<!-- Kernel Flash Progress Related -->
<string name="horizon_flash_complete">刷入完成</string>
<string name="horizon_preparing">准备中…</string>

View File

@@ -160,6 +160,26 @@
<string name="module_undo_uninstall_success">Successfully canceled uninstall of %s</string>
<string name="module_undo_uninstall_failed">Failed to undo uninstall: %s</string>
<string name="group_contains_apps">Contains %d apps</string>
<string name="settings_theme">Theme</string>
<string name="settings_theme_summary">Choose the app theme mode.</string>
<string name="settings_theme_mode_system">Follow system</string>
<string name="settings_theme_mode_light">Light</string>
<string name="settings_theme_mode_dark">Dark</string>
<string name="settings_theme_mode_monet_system">Monet (Follow system)</string>
<string name="settings_theme_mode_monet_light">Monet (Light)</string>
<string name="settings_theme_mode_monet_dark">Monet (Dark)</string>
<string name="settings_key_color">Key color</string>
<string name="settings_key_color_summary">Customize accent when using Monet.</string>
<string name="settings_key_color_default">Default</string>
<string name="color_blue">Blue</string>
<string name="color_red">Red</string>
<string name="color_green">Green</string>
<string name="color_purple">Purple</string>
<string name="color_orange">Orange</string>
<string name="color_teal">Teal</string>
<string name="color_pink">Pink</string>
<string name="color_brown">Brown</string>
<!-- Customize -->
<string name="home_susfs_version">SuSFS Version</string>
<string name="manual_hook">Manual Hook</string>
<string name="inline_hook">Inline Hook</string>
@@ -247,9 +267,6 @@
<string name="theme_settings">Theme Settings</string>
<string name="theme_mode">Theme Mode</string>
<string name="theme_mode_summary">Select the app\'s display theme</string>
<string name="theme_light">Light</string>
<string name="theme_dark">Dark</string>
<string name="theme_follow_system">Follow System</string>
<!-- Kernel Flash Progress Related -->
<string name="horizon_flash_complete">Flash Complete</string>
<string name="horizon_preparing">Preparing…</string>

View File

@@ -3,8 +3,8 @@ agp = "8.13.1"
gson = "2.13.2"
kotlin = "2.2.21"
ksp = "2.3.2"
compose-bom = "2025.11.00"
lifecycle = "2.9.4"
compose-bom = "2025.11.01"
lifecycle = "2.10.0"
navigation = "2.9.6"
activity-compose = "1.11.0"
kotlinx-coroutines = "1.10.2"
@@ -17,7 +17,7 @@ ndk = "29.0.13599879-beta2"
libsu = "6.0.0"
apksign = "1.4"
cmaker = "1.2"
miuix = "0.6.1"
miuix = "0.7.1"
haze = "1.7.0"
capsule = "2.1.1"
documentfile = "1.1.0"