From d06f22dcd03637737e0696634f938f77553d4f9c Mon Sep 17 00:00:00 2001 From: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com> Date: Sun, 4 May 2025 14:19:29 +0800 Subject: [PATCH] manager: continue to improve the UI - Expose anykernel3 flashing as long as there is root. - Opt some styles --- .../java/com/sukisu/ultra/ui/MainActivity.kt | 42 +- .../sukisu/ultra/ui/component/SearchBar.kt | 2 +- .../com/sukisu/ultra/ui/screen/AppProfile.kt | 404 +++++++++--- .../java/com/sukisu/ultra/ui/screen/Home.kt | 100 +-- .../com/sukisu/ultra/ui/screen/Install.kt | 583 ++++++++++++------ .../java/com/sukisu/ultra/ui/screen/Kpm.kt | 8 +- .../java/com/sukisu/ultra/ui/screen/Module.kt | 10 +- .../sukisu/ultra/ui/screen/MoreSettings.kt | 28 +- .../com/sukisu/ultra/ui/screen/Settings.kt | 11 +- .../com/sukisu/ultra/ui/screen/SuperUser.kt | 10 +- .../com/sukisu/ultra/ui/screen/Template.kt | 17 + .../com/sukisu/ultra/ui/theme/CardManage.kt | 25 +- .../java/com/sukisu/ultra/ui/theme/Theme.kt | 72 ++- .../app/src/main/res/values-ja/strings.xml | 3 +- .../src/main/res/values-zh-rCN/strings.xml | 7 +- manager/app/src/main/res/values/strings.xml | 7 +- 16 files changed, 893 insertions(+), 436 deletions(-) diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/MainActivity.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/MainActivity.kt index 1e4ec582..ccddbd74 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/MainActivity.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/MainActivity.kt @@ -32,6 +32,8 @@ import com.sukisu.ultra.ui.screen.BottomBarDestination import com.sukisu.ultra.ui.theme.* import com.sukisu.ultra.ui.theme.CardConfig.cardAlpha import com.sukisu.ultra.ui.util.* +import androidx.core.content.edit +import com.sukisu.ultra.ui.theme.CardConfig.cardElevation class MainActivity : ComponentActivity() { private inner class ThemeChangeContentObserver( @@ -45,7 +47,6 @@ class MainActivity : ComponentActivity() { } override fun onCreate(savedInstanceState: Bundle?) { - // Enable edge to edge enableEdgeToEdge() @@ -55,8 +56,18 @@ class MainActivity : ComponentActivity() { super.onCreate(savedInstanceState) + val prefs = getSharedPreferences("settings", MODE_PRIVATE) + val isFirstRun = prefs.getBoolean("is_first_run", true) + + if (isFirstRun) { + ThemeConfig.preventBackgroundRefresh = false + getSharedPreferences("theme_prefs", MODE_PRIVATE).edit { + putBoolean("prevent_background_refresh", false) + } + prefs.edit { putBoolean("is_first_run", false) } + } + // 加载保存的背景设置 - loadCustomBackground() loadThemeMode() loadThemeColors() loadDynamicColorState() @@ -64,8 +75,10 @@ class MainActivity : ComponentActivity() { val contentObserver = ThemeChangeContentObserver(Handler(mainLooper)) { runOnUiThread { - ThemeConfig.backgroundImageLoaded = false - loadCustomBackground() + if (!ThemeConfig.preventBackgroundRefresh) { + ThemeConfig.backgroundImageLoaded = false + loadCustomBackground() + } } } @@ -115,6 +128,22 @@ class MainActivity : ComponentActivity() { } } + override fun onPause() { + super.onPause() + CardConfig.save(applicationContext) + getSharedPreferences("theme_prefs", MODE_PRIVATE).edit() { + putBoolean("prevent_background_refresh", true) + } + ThemeConfig.preventBackgroundRefresh = true + } + + override fun onResume() { + super.onResume() + if (!ThemeConfig.backgroundImageLoaded && !ThemeConfig.preventBackgroundRefresh) { + loadCustomBackground() + } + } + private val destroyListeners = mutableListOf<() -> Unit>() override fun onDestroy() { @@ -141,7 +170,7 @@ private fun BottomBar(navController: NavHostController) { containerColor = cardColor.copy(alpha = cardAlpha), scrolledContainerColor = containerColor.copy(alpha = cardAlpha) ).containerColor, - tonalElevation = 0.dp + tonalElevation = cardElevation ) { BottomBarDestination.entries.forEach { destination -> if (destination == BottomBarDestination.Kpm) { @@ -169,7 +198,6 @@ private fun BottomBar(navController: NavHostController) { destination.iconNotSelected }, contentDescription = stringResource(destination.label), - tint = if (isCurrentDestOnBackStack) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant ) }, label = { @@ -217,4 +245,4 @@ private fun BottomBar(navController: NavHostController) { } } } -} +} \ No newline at end of file diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/component/SearchBar.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/component/SearchBar.kt index 72b3e8b9..83ab9644 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/component/SearchBar.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/component/SearchBar.kt @@ -63,7 +63,7 @@ fun SearchAppBar( var onSearch by remember { mutableStateOf(false) } // 获取卡片颜色和透明度 - val cardColor = MaterialTheme.colorScheme.secondaryContainer + val cardColor = MaterialTheme.colorScheme.surfaceVariant val cardAlpha = CardConfig.cardAlpha if (onSearch) { diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/AppProfile.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/AppProfile.kt index 37c2c360..e28a4cea 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/AppProfile.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/AppProfile.kt @@ -1,7 +1,13 @@ package com.sukisu.ultra.ui.screen +import android.annotation.SuppressLint import androidx.annotation.StringRes +import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.Crossfade +import androidx.compose.animation.expandVertically +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.shrinkVertically import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.BoxWithConstraints @@ -25,16 +31,21 @@ import androidx.compose.material.icons.filled.Android import androidx.compose.material.icons.filled.Security import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.ElevatedCard import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FilterChip import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.IconButtonDefaults import androidx.compose.material3.ListItem +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarColors import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.runtime.Composable @@ -44,7 +55,10 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha +import androidx.compose.ui.draw.shadow import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.pointer.pointerInput @@ -63,13 +77,13 @@ import com.ramcosta.composedestinations.annotation.RootGraph import com.ramcosta.composedestinations.generated.destinations.AppProfileTemplateScreenDestination import com.ramcosta.composedestinations.generated.destinations.TemplateEditorScreenDestination import com.ramcosta.composedestinations.navigation.DestinationsNavigator -import kotlinx.coroutines.launch import com.sukisu.ultra.Natives import com.sukisu.ultra.R import com.sukisu.ultra.ui.component.SwitchItem import com.sukisu.ultra.ui.component.profile.AppProfileConfig import com.sukisu.ultra.ui.component.profile.RootProfileConfig import com.sukisu.ultra.ui.component.profile.TemplateConfig +import com.sukisu.ultra.ui.theme.CardConfig import com.sukisu.ultra.ui.util.LocalSnackbarHost import com.sukisu.ultra.ui.util.forceStopApp import com.sukisu.ultra.ui.util.getSepolicy @@ -78,6 +92,7 @@ import com.sukisu.ultra.ui.util.restartApp import com.sukisu.ultra.ui.util.setSepolicy import com.sukisu.ultra.ui.viewmodel.SuperUserViewModel import com.sukisu.ultra.ui.viewmodel.getTemplateInfoById +import kotlinx.coroutines.launch /** * @author weishu @@ -107,9 +122,18 @@ fun AppProfileScreen( mutableStateOf(initialProfile) } + val cardColor = MaterialTheme.colorScheme.surfaceVariant + val cardAlpha = CardConfig.cardAlpha + Scaffold( topBar = { TopBar( + title = appInfo.label, + packageName = packageName, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = cardColor.copy(alpha = cardAlpha), + scrolledContainerColor = cardColor.copy(alpha = cardAlpha) + ), onBack = dropUnlessResumed { navigator.popBackStack() }, scrollBehavior = scrollBehavior ) @@ -181,22 +205,50 @@ private fun AppProfileInner( val isRootGranted = profile.allowSu Column(modifier = modifier) { - AppMenuBox(packageName) { - ListItem( - headlineContent = { Text(appLabel) }, - supportingContent = { Text(packageName) }, - leadingContent = appIcon, + ElevatedCard( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp), + shape = MaterialTheme.shapes.medium + ) { + AppMenuBox(packageName) { + ListItem( + headlineContent = { + Text( + text = appLabel, + style = MaterialTheme.typography.titleMedium + ) + }, + supportingContent = { + Text( + text = packageName, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + }, + leadingContent = appIcon, + ) + } + } + + ElevatedCard( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp), + shape = MaterialTheme.shapes.medium + ) { + SwitchItem( + icon = Icons.Filled.Security, + title = stringResource(id = R.string.superuser), + checked = isRootGranted, + onCheckedChange = { onProfileChange(profile.copy(allowSu = it)) }, ) } - SwitchItem( - icon = Icons.Filled.Security, - title = stringResource(id = R.string.superuser), - checked = isRootGranted, - onCheckedChange = { onProfileChange(profile.copy(allowSu = it)) }, - ) - - Crossfade(targetState = isRootGranted, label = "") { current -> + Crossfade( + targetState = isRootGranted, + label = "RootAccess" + ) { current -> Column( modifier = Modifier.padding(bottom = 6.dp + 48.dp + 6.dp /* SnackBar height */) ) { @@ -211,42 +263,91 @@ private fun AppProfileInner( var mode by rememberSaveable { mutableStateOf(initialMode) } - ProfileBox(mode, true) { - // template mode shouldn't change profile here! - if (it == Mode.Default || it == Mode.Custom) { - onProfileChange(profile.copy(rootUseDefault = it == Mode.Default)) + + ElevatedCard( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp), + shape = MaterialTheme.shapes.medium + ) { + ProfileBox(mode, true) { + // template mode shouldn't change profile here! + if (it == Mode.Default || it == Mode.Custom) { + onProfileChange(profile.copy(rootUseDefault = it == Mode.Default)) + } + mode = it } - mode = it } - Crossfade(targetState = mode, label = "") { currentMode -> - if (currentMode == Mode.Template) { - TemplateConfig( - profile = profile, - onViewTemplate = onViewTemplate, - onManageTemplate = onManageTemplate, - onProfileChange = onProfileChange - ) - } else if (mode == Mode.Custom) { - RootProfileConfig( - fixedName = true, - profile = profile, - onProfileChange = onProfileChange - ) + + AnimatedVisibility( + visible = mode != Mode.Default, + enter = fadeIn() + expandVertically(), + exit = fadeOut() + shrinkVertically() + ) { + ElevatedCard( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp), + shape = MaterialTheme.shapes.medium + ) { + Column(modifier = Modifier.padding(vertical = 8.dp)) { + Crossfade(targetState = mode, label = "ProfileMode") { currentMode -> + when (currentMode) { + Mode.Template -> { + TemplateConfig( + profile = profile, + onViewTemplate = onViewTemplate, + onManageTemplate = onManageTemplate, + onProfileChange = onProfileChange + ) + } + Mode.Custom -> { + RootProfileConfig( + fixedName = true, + profile = profile, + onProfileChange = onProfileChange + ) + } + else -> {} + } + } + } } } } else { val mode = if (profile.nonRootUseDefault) Mode.Default else Mode.Custom - ProfileBox(mode, false) { - onProfileChange(profile.copy(nonRootUseDefault = (it == Mode.Default))) + + ElevatedCard( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp), + shape = MaterialTheme.shapes.medium + ) { + ProfileBox(mode, false) { + onProfileChange(profile.copy(nonRootUseDefault = (it == Mode.Default))) + } } - Crossfade(targetState = mode, label = "") { currentMode -> - val modifyEnabled = currentMode == Mode.Custom - AppProfileConfig( - fixedName = true, - profile = profile, - enabled = modifyEnabled, - onProfileChange = onProfileChange - ) + + AnimatedVisibility( + visible = mode == Mode.Custom, + enter = fadeIn() + expandVertically(), + exit = fadeOut() + shrinkVertically() + ) { + ElevatedCard( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp), + shape = MaterialTheme.shapes.medium + ) { + Column(modifier = Modifier.padding(vertical = 8.dp)) { + AppProfileConfig( + fixedName = true, + profile = profile, + enabled = mode == Mode.Custom, + onProfileChange = onProfileChange + ) + } + } } } } @@ -264,20 +365,51 @@ private enum class Mode(@StringRes private val res: Int) { @OptIn(ExperimentalMaterial3Api::class) @Composable private fun TopBar( + title: String, + packageName: String, onBack: () -> Unit, + colors: TopAppBarColors, scrollBehavior: TopAppBarScrollBehavior? = null ) { TopAppBar( title = { - Text(stringResource(R.string.profile)) + Column { + Text( + text = title, + style = MaterialTheme.typography.titleMedium, + color = MaterialTheme.colorScheme.onSurface + ) + Text( + text = packageName, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.alpha(0.8f) + ) + } }, + colors = colors, navigationIcon = { IconButton( - onClick = onBack - ) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) } + onClick = onBack, + colors = IconButtonDefaults.iconButtonColors( + contentColor = MaterialTheme.colorScheme.onSurface + ) + ) { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = stringResource(R.string.back) + ) + } }, - windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal), - scrollBehavior = scrollBehavior + windowInsets = WindowInsets.safeDrawing.only( + WindowInsetsSides.Top + WindowInsetsSides.Horizontal + ), + scrollBehavior = scrollBehavior, + modifier = Modifier.shadow( + elevation = if ((scrollBehavior?.state?.overlappedFraction ?: 0f) > 0.01f) + 4.dp else 0.dp, + spotColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.1f) + ) ) } @@ -287,40 +419,88 @@ private fun ProfileBox( hasTemplate: Boolean, onModeChange: (Mode) -> Unit, ) { - ListItem( - headlineContent = { Text(stringResource(R.string.profile)) }, - supportingContent = { Text(mode.text) }, - leadingContent = { Icon(Icons.Filled.AccountCircle, null) }, - ) - HorizontalDivider(thickness = Dp.Hairline) - ListItem(headlineContent = { - Row( - modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceEvenly - ) { - FilterChip( - selected = mode == Mode.Default, - label = { Text(stringResource(R.string.profile_default)) }, - onClick = { onModeChange(Mode.Default) }, - ) - if (hasTemplate) { - FilterChip( - selected = mode == Mode.Template, - label = { Text(stringResource(R.string.profile_template)) }, - onClick = { onModeChange(Mode.Template) }, + Column(modifier = Modifier.padding(vertical = 8.dp)) { + ListItem( + headlineContent = { + Text( + text = stringResource(R.string.profile), + style = MaterialTheme.typography.titleMedium ) + }, + supportingContent = { + Text( + text = mode.text, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + }, + leadingContent = { + Icon( + imageVector = Icons.Filled.AccountCircle, + contentDescription = null, + ) + }, + ) + + HorizontalDivider( + thickness = Dp.Hairline, + color = MaterialTheme.colorScheme.outlineVariant + ) + + ListItem( + headlineContent = { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterHorizontally) + ) { + FilterChip( + selected = mode == Mode.Default, + onClick = { onModeChange(Mode.Default) }, + label = { + Text( + text = stringResource(R.string.profile_default), + style = MaterialTheme.typography.bodyMedium + ) + }, + shape = MaterialTheme.shapes.small + ) + + if (hasTemplate) { + FilterChip( + selected = mode == Mode.Template, + onClick = { onModeChange(Mode.Template) }, + label = { + Text( + text = stringResource(R.string.profile_template), + style = MaterialTheme.typography.bodyMedium + ) + }, + shape = MaterialTheme.shapes.small + ) + } + + FilterChip( + selected = mode == Mode.Custom, + onClick = { onModeChange(Mode.Custom) }, + label = { + Text( + text = stringResource(R.string.profile_custom), + style = MaterialTheme.typography.bodyMedium + ) + }, + shape = MaterialTheme.shapes.small + ) + } } - FilterChip( - selected = mode == Mode.Custom, - label = { Text(stringResource(R.string.profile_custom)) }, - onClick = { onModeChange(Mode.Custom) }, - ) - } - }) + ) + } } +@SuppressLint("UnusedBoxWithConstraintsScope") @Composable private fun AppMenuBox(packageName: String, content: @Composable () -> Unit) { - var expanded by remember { mutableStateOf(false) } var touchPoint: Offset by remember { mutableStateOf(Offset.Zero) } val density = LocalDensity.current @@ -329,13 +509,14 @@ private fun AppMenuBox(packageName: String, content: @Composable () -> Unit) { Modifier .fillMaxSize() .pointerInput(Unit) { - detectTapGestures { - touchPoint = it - expanded = true - } + detectTapGestures( + onLongPress = { + touchPoint = it + expanded = true + } + ) } ) { - content() val (offsetX, offsetY) = with(density) { @@ -349,43 +530,64 @@ private fun AppMenuBox(packageName: String, content: @Composable () -> Unit) { expanded = false }, ) { - DropdownMenuItem( - text = { Text(stringResource(id = R.string.launch_app)) }, + AppMenuOption( + text = stringResource(id = R.string.launch_app), onClick = { expanded = false launchApp(packageName) - }, + } ) - DropdownMenuItem( - text = { Text(stringResource(id = R.string.force_stop_app)) }, + + AppMenuOption( + text = stringResource(id = R.string.force_stop_app), onClick = { expanded = false forceStopApp(packageName) - }, + } ) - DropdownMenuItem( - text = { Text(stringResource(id = R.string.restart_app)) }, + + AppMenuOption( + text = stringResource(id = R.string.restart_app), onClick = { expanded = false restartApp(packageName) - }, + } ) } } } +@Composable +private fun AppMenuOption(text: String, onClick: () -> Unit) { + DropdownMenuItem( + text = { + Text( + text = text, + style = MaterialTheme.typography.bodyMedium + ) + }, + onClick = onClick + ) +} + @Preview @Composable private fun AppProfilePreview() { var profile by remember { mutableStateOf(Natives.Profile("")) } - AppProfileInner( - packageName = "icu.nullptr.test", - appLabel = "Test", - appIcon = { Icon(Icons.Filled.Android, null) }, - profile = profile, - onProfileChange = { - profile = it - }, - ) -} - + Surface { + AppProfileInner( + packageName = "icu.nullptr.test", + appLabel = "Test", + appIcon = { + Icon( + imageVector = Icons.Filled.Android, + contentDescription = null, + ) + }, + profile = profile, + onProfileChange = { + profile = it + }, + ) + } +} \ No newline at end of file diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Home.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Home.kt index 53d234e1..39bea950 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Home.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Home.kt @@ -34,12 +34,10 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Android import androidx.compose.material.icons.filled.Archive import androidx.compose.material.icons.filled.Code -import androidx.compose.material.icons.filled.Favorite import androidx.compose.material.icons.filled.Info import androidx.compose.material.icons.filled.Memory import androidx.compose.material.icons.filled.PhoneAndroid import androidx.compose.material.icons.filled.Refresh -import androidx.compose.material.icons.filled.School import androidx.compose.material.icons.filled.Security import androidx.compose.material.icons.filled.Settings import androidx.compose.material.icons.filled.Storage @@ -99,6 +97,7 @@ import com.sukisu.ultra.getKernelVersion import com.sukisu.ultra.ksuApp import com.sukisu.ultra.ui.component.rememberConfirmDialog import com.sukisu.ultra.ui.theme.CardConfig +import com.sukisu.ultra.ui.theme.CardConfig.cardElevation import com.sukisu.ultra.ui.theme.getCardColors import com.sukisu.ultra.ui.util.checkNewVersion import com.sukisu.ultra.ui.util.getKpmModuleCount @@ -245,11 +244,11 @@ fun HomeScreen(navigator: DestinationsNavigator) { ) { ElevatedCard( colors = getCardColors(MaterialTheme.colorScheme.secondaryContainer), - elevation = CardDefaults.cardElevation(defaultElevation = 0.dp), + elevation = CardDefaults.cardElevation(defaultElevation = cardElevation), modifier = Modifier .clip(MaterialTheme.shapes.medium) .shadow( - elevation = 0.dp, + elevation = cardElevation, shape = MaterialTheme.shapes.medium, spotColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.1f) ) @@ -267,7 +266,6 @@ fun HomeScreen(navigator: DestinationsNavigator) { Icon( imageVector = Icons.Outlined.Info, contentDescription = null, - tint = MaterialTheme.colorScheme.primary, modifier = Modifier.padding(end = 12.dp) ) Text( @@ -283,10 +281,10 @@ fun HomeScreen(navigator: DestinationsNavigator) { InfoCard() if (!isSimpleMode) { - if (!isHideLinkCard) { - ContributionCard() - DonateCard() - LearnMoreCard() + if (!isHideLinkCard) { + ContributionCard() + DonateCard() + LearnMoreCard() } } Spacer(Modifier.height(16.dp)) @@ -365,7 +363,6 @@ fun RebootDropdownItem(@StringRes id: Int, reason: String = "") { Icon( imageVector = Icons.Filled.Refresh, contentDescription = null, - tint = MaterialTheme.colorScheme.primary ) } ) @@ -393,12 +390,11 @@ private fun TopBar( scrolledContainerColor = cardColor.copy(alpha = cardAlpha) ), actions = { - if (kernelVersion.isGKI()) { + if (rootAvailable() || kernelVersion.isGKI()) { IconButton(onClick = onInstallClick) { Icon( Icons.Filled.Archive, contentDescription = stringResource(R.string.install), - tint = MaterialTheme.colorScheme.primary ) } } @@ -409,7 +405,6 @@ private fun TopBar( Icon( Icons.Filled.Refresh, contentDescription = stringResource(R.string.reboot), - tint = MaterialTheme.colorScheme.primary ) DropdownMenu( @@ -444,22 +439,22 @@ private fun StatusCard( onClickInstall: () -> Unit = {} ) { ElevatedCard( - colors = getCardColors(MaterialTheme.colorScheme.surfaceContainerHigh), - elevation = CardDefaults.cardElevation(defaultElevation = 0.dp), + colors = getCardColors(MaterialTheme.colorScheme.surfaceVariant), + elevation = CardDefaults.cardElevation(defaultElevation = cardElevation), modifier = Modifier .fillMaxWidth() .clip(MaterialTheme.shapes.large) .shadow( - elevation = 0.dp, + elevation = cardElevation, shape = MaterialTheme.shapes.large, - spotColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.1f) + spotColor = MaterialTheme.colorScheme.surface.copy(alpha = 0.1f) ) ) { Row( modifier = Modifier .fillMaxWidth() - .clickable(enabled = kernelVersion.isGKI()) { - if (kernelVersion.isGKI()) { + .clickable { + if (rootAvailable() || kernelVersion.isGKI()) { onClickInstall() } } @@ -620,12 +615,12 @@ fun WarningCard( ) { ElevatedCard( colors = getCardColors(color), - elevation = CardDefaults.cardElevation(defaultElevation = 0.dp), + elevation = CardDefaults.cardElevation(defaultElevation = cardElevation), modifier = Modifier .fillMaxWidth() .clip(MaterialTheme.shapes.large) .shadow( - elevation = 0.dp, + elevation = cardElevation, shape = MaterialTheme.shapes.large, spotColor = MaterialTheme.colorScheme.error.copy(alpha = 0.1f) ) @@ -660,14 +655,14 @@ fun ContributionCard() { val links = listOf("https://github.com/zako", "https://github.com/udochina") ElevatedCard( - colors = getCardColors(MaterialTheme.colorScheme.tertiaryContainer), - elevation = CardDefaults.cardElevation(defaultElevation = 0.dp), + colors = getCardColors(MaterialTheme.colorScheme.surfaceContainerHigh), + elevation = CardDefaults.cardElevation(defaultElevation = cardElevation), modifier = Modifier .fillMaxWidth() .wrapContentHeight() .clip(MaterialTheme.shapes.large) .shadow( - elevation = 0.dp, + elevation = cardElevation, shape = MaterialTheme.shapes.large, spotColor = MaterialTheme.colorScheme.tertiary.copy(alpha = 0.1f) ) @@ -682,27 +677,18 @@ fun ContributionCard() { .padding(24.dp), verticalAlignment = Alignment.CenterVertically ) { - Icon( - imageVector = Icons.Filled.Code, - contentDescription = null, - tint = MaterialTheme.colorScheme.onTertiaryContainer, - modifier = Modifier - .padding(end = 16.dp) - .size(24.dp) - ) - Column { Text( text = stringResource(R.string.home_ContributionCard_kernelsu), style = MaterialTheme.typography.titleSmall, - color = MaterialTheme.colorScheme.onTertiaryContainer + color = MaterialTheme.colorScheme.onSurfaceVariant ) Spacer(Modifier.height(4.dp)) Text( text = stringResource(R.string.home_click_to_ContributionCard_kernelsu), style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onTertiaryContainer.copy(alpha = 0.8f) + color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.8f) ) } } @@ -715,13 +701,13 @@ fun LearnMoreCard() { val url = stringResource(R.string.home_learn_kernelsu_url) ElevatedCard( - colors = getCardColors(MaterialTheme.colorScheme.primaryContainer), - elevation = CardDefaults.cardElevation(defaultElevation = 0.dp), + colors = getCardColors(MaterialTheme.colorScheme.surfaceContainerHigh), + elevation = CardDefaults.cardElevation(defaultElevation = cardElevation), modifier = Modifier .fillMaxWidth() .clip(MaterialTheme.shapes.large) .shadow( - elevation = 0.dp, + elevation = cardElevation, shape = MaterialTheme.shapes.large, spotColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.1f) ) @@ -735,27 +721,18 @@ fun LearnMoreCard() { .padding(24.dp), verticalAlignment = Alignment.CenterVertically ) { - Icon( - imageVector = Icons.Filled.School, - contentDescription = null, - tint = MaterialTheme.colorScheme.onPrimaryContainer, - modifier = Modifier - .padding(end = 16.dp) - .size(24.dp) - ) - Column { Text( text = stringResource(R.string.home_learn_kernelsu), style = MaterialTheme.typography.titleSmall, - color = MaterialTheme.colorScheme.onPrimaryContainer + color = MaterialTheme.colorScheme.onSurfaceVariant ) Spacer(Modifier.height(4.dp)) Text( text = stringResource(R.string.home_click_to_learn_kernelsu), style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onPrimaryContainer.copy(alpha = 0.8f) + color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.8f) ) } } @@ -767,13 +744,13 @@ fun DonateCard() { val uriHandler = LocalUriHandler.current ElevatedCard( - colors = getCardColors(MaterialTheme.colorScheme.secondaryContainer), - elevation = CardDefaults.cardElevation(defaultElevation = 0.dp), + colors = getCardColors(MaterialTheme.colorScheme.surfaceContainerHigh), + elevation = CardDefaults.cardElevation(defaultElevation = cardElevation), modifier = Modifier .fillMaxWidth() .clip(MaterialTheme.shapes.large) .shadow( - elevation = 0.dp, + elevation = cardElevation, shape = MaterialTheme.shapes.large, spotColor = MaterialTheme.colorScheme.secondary.copy(alpha = 0.1f) ) @@ -787,27 +764,18 @@ fun DonateCard() { .padding(24.dp), verticalAlignment = Alignment.CenterVertically ) { - Icon( - imageVector = Icons.Filled.Favorite, - contentDescription = null, - tint = MaterialTheme.colorScheme.onSecondaryContainer, - modifier = Modifier - .padding(end = 16.dp) - .size(24.dp) - ) - Column { Text( text = stringResource(R.string.home_support_title), style = MaterialTheme.typography.titleSmall, - color = MaterialTheme.colorScheme.onSecondaryContainer + color = MaterialTheme.colorScheme.onSurfaceVariant ) Spacer(Modifier.height(4.dp)) Text( text = stringResource(R.string.home_support_content), style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSecondaryContainer.copy(alpha = 0.8f) + color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.8f) ) } } @@ -823,12 +791,12 @@ private fun InfoCard() { ElevatedCard( colors = getCardColors(MaterialTheme.colorScheme.surfaceContainerHighest), - elevation = CardDefaults.cardElevation(defaultElevation = 0.dp), + elevation = CardDefaults.cardElevation(defaultElevation = cardElevation), modifier = Modifier .fillMaxWidth() .clip(MaterialTheme.shapes.large) .shadow( - elevation = 0.dp, + elevation = cardElevation, shape = MaterialTheme.shapes.large, spotColor = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.05f) ) @@ -858,7 +826,7 @@ private fun InfoCard() { imageVector = icon, contentDescription = label, modifier = Modifier.size(24.dp), - tint = MaterialTheme.colorScheme.primary + tint = MaterialTheme.colorScheme.primary.copy(alpha = 0.7f), ) Spacer(modifier = Modifier.width(16.dp)) Column( diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Install.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Install.kt index ad9f644d..d3b5a5de 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Install.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Install.kt @@ -15,7 +15,14 @@ import androidx.compose.animation.shrinkVertically import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.WindowInsetsSides +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.only +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawing import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.selection.toggleable import androidx.compose.foundation.verticalScroll @@ -23,34 +30,71 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.filled.AutoFixHigh import androidx.compose.material.icons.filled.FileUpload -import androidx.compose.material3.* -import androidx.compose.runtime.* +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.ElevatedCard +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.ListItem +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.RadioButton +import androidx.compose.material3.RadioButtonDefaults +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.TopAppBarScrollBehavior +import androidx.compose.material3.rememberTopAppBarState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.produceState +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.shadow import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import com.maxkeppeker.sheets.core.models.base.Header +import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState +import com.maxkeppeler.sheets.list.ListDialog import com.maxkeppeler.sheets.list.models.ListOption +import com.maxkeppeler.sheets.list.models.ListSelection import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.RootGraph import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator import com.sukisu.ultra.R +import com.sukisu.ultra.flash.HorizonKernelFlashProgress +import com.sukisu.ultra.flash.HorizonKernelState +import com.sukisu.ultra.flash.HorizonKernelWorker import com.sukisu.ultra.ui.component.DialogHandle import com.sukisu.ultra.ui.component.SlotSelectionDialog import com.sukisu.ultra.ui.component.rememberConfirmDialog import com.sukisu.ultra.ui.component.rememberCustomDialog -import com.sukisu.ultra.flash.HorizonKernelFlashProgress -import com.sukisu.ultra.flash.HorizonKernelState -import com.sukisu.ultra.flash.HorizonKernelWorker -import com.sukisu.ultra.ui.theme.CardConfig -import com.sukisu.ultra.ui.theme.ThemeConfig +import com.sukisu.ultra.ui.theme.CardConfig.cardAlpha +import com.sukisu.ultra.ui.theme.CardConfig.cardElevation import com.sukisu.ultra.ui.theme.getCardColors -import com.sukisu.ultra.ui.util.* +import com.sukisu.ultra.ui.util.LkmSelection +import com.sukisu.ultra.ui.util.getCurrentKmi +import com.sukisu.ultra.ui.util.getSupportedKmis +import com.sukisu.ultra.ui.util.isAbDevice +import com.sukisu.ultra.ui.util.isInitBoot +import com.sukisu.ultra.ui.util.rootAvailable +import com.sukisu.ultra.getKernelVersion /** * @author weishu @@ -69,6 +113,8 @@ fun InstallScreen(navigator: DestinationsNavigator) { val horizonKernelState = remember { HorizonKernelState() } val flashState by horizonKernelState.state.collectAsState() val summary = stringResource(R.string.horizon_kernel_summary) + val kernelVersion = getKernelVersion() + val isGKI = kernelVersion.isGKI() val onFlashComplete = { showRebootDialog = true @@ -133,7 +179,6 @@ fun InstallScreen(navigator: DestinationsNavigator) { summary = summary ) installMethod = horizonMethod - onInstall() } ) @@ -149,7 +194,7 @@ fun InstallScreen(navigator: DestinationsNavigator) { } val onClickNext = { - if (lkmSelection == LkmSelection.KmiNone && currentKmi.isBlank()) { + if (isGKI && lkmSelection == LkmSelection.KmiNone && currentKmi.isBlank()) { selectKmiDialog.show() } else { onInstall() @@ -191,8 +236,10 @@ fun InstallScreen(navigator: DestinationsNavigator) { .padding(innerPadding) .nestedScroll(scrollBehavior.nestedScrollConnection) .verticalScroll(rememberScrollState()) + .padding(top = 12.dp) ) { SelectInstallMethod( + isGKI = isGKI, onSelected = { method -> if (method is InstallMethod.HorizonKernel && method.uri != null && method.slot == null) { tempKernelUri = method.uri @@ -218,32 +265,73 @@ fun InstallScreen(navigator: DestinationsNavigator) { .padding(16.dp) ) { (lkmSelection as? LkmSelection.LkmUri)?.let { - Text( - stringResource( - id = R.string.selected_lkm, - it.uri.lastPathSegment ?: "(file)" - ) - ) - } - (installMethod as? InstallMethod.HorizonKernel)?.let { method -> - if (method.slot != null) { - Text( - stringResource( - id = R.string.selected_slot, - if (method.slot == "a") stringResource(id = R.string.slot_a) - else stringResource(id = R.string.slot_b) + ElevatedCard( + colors = getCardColors(MaterialTheme.colorScheme.surfaceVariant), + elevation = CardDefaults.cardElevation(defaultElevation = cardElevation), + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 12.dp) + .clip(MaterialTheme.shapes.medium) + .shadow( + elevation = cardElevation, + shape = MaterialTheme.shapes.medium, + spotColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.1f) ) + ) { + Text( + text = stringResource( + id = R.string.selected_lkm, + it.uri.lastPathSegment ?: "(file)" + ), + style = MaterialTheme.typography.bodyMedium, + modifier = Modifier.padding(16.dp) ) } } + + (installMethod as? InstallMethod.HorizonKernel)?.let { method -> + if (method.slot != null) { + ElevatedCard( + colors = getCardColors(MaterialTheme.colorScheme.surfaceVariant), + elevation = CardDefaults.cardElevation(defaultElevation = cardElevation), + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 12.dp) + .clip(MaterialTheme.shapes.medium) + .shadow( + elevation = cardElevation, + shape = MaterialTheme.shapes.medium, + spotColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.1f) + ) + ) { + Text( + text = stringResource( + id = R.string.selected_slot, + if (method.slot == "a") stringResource(id = R.string.slot_a) + else stringResource(id = R.string.slot_b) + ), + style = MaterialTheme.typography.bodyMedium, + modifier = Modifier.padding(16.dp) + ) + } + } + } + Button( modifier = Modifier.fillMaxWidth(), enabled = installMethod != null && !flashState.isFlashing, - onClick = onClickNext + onClick = onClickNext, + shape = MaterialTheme.shapes.medium, + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.primary, + contentColor = MaterialTheme.colorScheme.onPrimary, + disabledContainerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.6f), + disabledContentColor = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.6f) + ) ) { Text( stringResource(id = R.string.install_next), - fontSize = MaterialTheme.typography.bodyMedium.fontSize + style = MaterialTheme.typography.bodyMedium ) } } @@ -305,7 +393,10 @@ sealed class InstallMethod { } @Composable -private fun SelectInstallMethod(onSelected: (InstallMethod) -> Unit = {}) { +private fun SelectInstallMethod( + isGKI: Boolean = false, + onSelected: (InstallMethod) -> Unit = {} +) { val rootAvailable = rootAvailable() val isAbDevice = isAbDevice() val horizonKernelSummary = stringResource(R.string.horizon_kernel_summary) @@ -393,114 +484,256 @@ private fun SelectInstallMethod(onSelected: (InstallMethod) -> Unit = {}) { var LKMExpanded by remember { mutableStateOf(false) } var GKIExpanded by remember { mutableStateOf(false) } - Column { - ListItem( - leadingContent = { Icon(Icons.Filled.AutoFixHigh, null) }, - headlineContent = { Text(stringResource(R.string.Lkm_install_methods)) }, - modifier = Modifier.clickable { - LKMExpanded = !LKMExpanded - } - ) - radioOptions.take(3).forEach { option -> - AnimatedVisibility( - visible = LKMExpanded, - modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp) + Column( + modifier = Modifier.padding(horizontal = 16.dp) + ) { + // LKM 安装/修补 + if (isGKI && rootAvailable) { + ElevatedCard( + colors = getCardColors(MaterialTheme.colorScheme.surfaceVariant), + elevation = CardDefaults.cardElevation(defaultElevation = cardElevation), + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 12.dp) + .clip(MaterialTheme.shapes.large) + .shadow( + elevation = cardElevation, + shape = MaterialTheme.shapes.large, + spotColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.1f) + ) ) { - Column { - val interactionSource = remember { MutableInteractionSource() } - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier - .fillMaxWidth() - .toggleable( - value = option.javaClass == selectedOption?.javaClass, - onValueChange = { onClick(option) }, - role = Role.RadioButton, - indication = LocalIndication.current, - interactionSource = interactionSource - ) - ) { - RadioButton( - selected = option.javaClass == selectedOption?.javaClass, - onClick = { onClick(option) }, - interactionSource = interactionSource + ListItem( + leadingContent = { + Icon( + Icons.Filled.AutoFixHigh, + contentDescription = null, + tint = MaterialTheme.colorScheme.primary ) - Column( - modifier = Modifier.padding(vertical = 12.dp) - ) { - Text( - text = stringResource(id = option.label), - fontSize = MaterialTheme.typography.titleMedium.fontSize, - fontFamily = MaterialTheme.typography.titleMedium.fontFamily, - fontStyle = MaterialTheme.typography.titleMedium.fontStyle - ) - option.summary?.let { - Text( - text = it, - fontSize = MaterialTheme.typography.bodySmall.fontSize, - fontFamily = MaterialTheme.typography.bodySmall.fontFamily, - fontStyle = MaterialTheme.typography.bodySmall.fontStyle - ) + }, + headlineContent = { + Text( + stringResource(R.string.Lkm_install_methods), + style = MaterialTheme.typography.titleMedium + ) + }, + modifier = Modifier.clickable { + LKMExpanded = !LKMExpanded + } + ) + + AnimatedVisibility( + visible = LKMExpanded, + enter = fadeIn() + expandVertically(), + exit = shrinkVertically() + fadeOut() + ) { + Column( + modifier = Modifier.padding( + start = 16.dp, + end = 16.dp, + bottom = 16.dp + ) + ) { + radioOptions.take(3).forEach { option -> + val interactionSource = remember { MutableInteractionSource() } + Surface( + color = if (option.javaClass == selectedOption?.javaClass) + MaterialTheme.colorScheme.secondaryContainer.copy(alpha = cardAlpha) + else + MaterialTheme.colorScheme.surfaceContainerHighest.copy(alpha = cardAlpha), + shape = MaterialTheme.shapes.medium, + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 4.dp) + .clip(MaterialTheme.shapes.medium) + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .toggleable( + value = option.javaClass == selectedOption?.javaClass, + onValueChange = { onClick(option) }, + role = Role.RadioButton, + indication = LocalIndication.current, + interactionSource = interactionSource + ) + .padding(vertical = 8.dp, horizontal = 12.dp) + ) { + RadioButton( + selected = option.javaClass == selectedOption?.javaClass, + onClick = null, + interactionSource = interactionSource, + colors = RadioButtonDefaults.colors( + selectedColor = MaterialTheme.colorScheme.primary, + unselectedColor = MaterialTheme.colorScheme.onSurfaceVariant + ) + ) + Column( + modifier = Modifier + .padding(start = 10.dp) + .weight(1f) + ) { + Text( + text = stringResource(id = option.label), + style = MaterialTheme.typography.bodyLarge + ) + option.summary?.let { + Text( + text = it, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + } + } } } } } - Spacer(modifier = Modifier.height(8.dp)) } } - } - Column { - ListItem( - leadingContent = { Icon(Icons.Filled.FileUpload, null) }, - headlineContent = { Text(stringResource(R.string.GKI_install_methods)) }, - modifier = Modifier.clickable { - GKIExpanded = !GKIExpanded - } - ) - AnimatedVisibility( - visible = GKIExpanded, - modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp) - ) { - Column { - radioOptions.drop(3).forEach { option -> - val interactionSource = remember { MutableInteractionSource() } - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier - .fillMaxWidth() - .toggleable( - value = option.javaClass == selectedOption?.javaClass, - onValueChange = { onClick(option) }, - role = Role.RadioButton, - indication = LocalIndication.current, - interactionSource = interactionSource - ) - ) { - RadioButton( - selected = option.javaClass == selectedOption?.javaClass, - onClick = { onClick(option) }, - interactionSource = interactionSource + + // anykernel3 刷写 + if (rootAvailable) { + ElevatedCard( + colors = getCardColors(MaterialTheme.colorScheme.surfaceVariant), + elevation = CardDefaults.cardElevation(defaultElevation = cardElevation), + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 12.dp) + .clip(MaterialTheme.shapes.large) + .shadow( + elevation = cardElevation, + shape = MaterialTheme.shapes.large, + spotColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.1f) + ) + ) { + ListItem( + leadingContent = { + Icon( + Icons.Filled.FileUpload, + contentDescription = null, + tint = MaterialTheme.colorScheme.primary ) - Column( - modifier = Modifier.padding(vertical = 12.dp) - ) { - Text( - text = stringResource(id = option.label), - fontSize = MaterialTheme.typography.titleMedium.fontSize, - fontFamily = MaterialTheme.typography.titleMedium.fontFamily, - fontStyle = MaterialTheme.typography.titleMedium.fontStyle - ) - option.summary?.let { - Text( - text = it, - fontSize = MaterialTheme.typography.bodySmall.fontSize, - fontFamily = MaterialTheme.typography.bodySmall.fontFamily, - fontStyle = MaterialTheme.typography.bodySmall.fontStyle - ) + }, + headlineContent = { + Text( + stringResource(R.string.GKI_install_methods), + style = MaterialTheme.typography.titleMedium + ) + }, + modifier = Modifier.clickable { + GKIExpanded = !GKIExpanded + } + ) + + AnimatedVisibility( + visible = GKIExpanded, + enter = fadeIn() + expandVertically(), + exit = shrinkVertically() + fadeOut() + ) { + Column( + modifier = Modifier.padding( + start = 16.dp, + end = 16.dp, + bottom = 16.dp + ) + ) { + if (radioOptions.size > 3) { + radioOptions.drop(3).forEach { option -> + val interactionSource = remember { MutableInteractionSource() } + Surface( + color = if (option.javaClass == selectedOption?.javaClass) + MaterialTheme.colorScheme.secondaryContainer.copy(alpha = cardAlpha) + else + MaterialTheme.colorScheme.surfaceContainerHighest.copy(alpha = cardAlpha), + shape = MaterialTheme.shapes.medium, + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 4.dp) + .clip(MaterialTheme.shapes.medium) + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .toggleable( + value = option.javaClass == selectedOption?.javaClass, + onValueChange = { onClick(option) }, + role = Role.RadioButton, + indication = LocalIndication.current, + interactionSource = interactionSource + ) + .padding(vertical = 8.dp, horizontal = 12.dp) + ) { + RadioButton( + selected = option.javaClass == selectedOption?.javaClass, + onClick = null, + interactionSource = interactionSource, + colors = RadioButtonDefaults.colors( + selectedColor = MaterialTheme.colorScheme.primary, + unselectedColor = MaterialTheme.colorScheme.onSurfaceVariant + ) + ) + Column( + modifier = Modifier + .padding(start = 10.dp) + .weight(1f) + ) { + Text( + text = stringResource(id = option.label), + style = MaterialTheme.typography.bodyLarge + ) + option.summary?.let { + Text( + text = it, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + } + } + } } } } - Spacer(modifier = Modifier.height(8.dp)) + } + } + } + + // 没有root + if (!rootAvailable) { + ElevatedCard( + colors = getCardColors(MaterialTheme.colorScheme.errorContainer), + elevation = CardDefaults.cardElevation(defaultElevation = cardElevation), + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 12.dp) + .clip(MaterialTheme.shapes.large) + .shadow( + elevation = cardElevation, + shape = MaterialTheme.shapes.large, + spotColor = MaterialTheme.colorScheme.error.copy(alpha = 0.1f) + ) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(24.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Filled.AutoFixHigh, + contentDescription = null, + tint = MaterialTheme.colorScheme.onErrorContainer, + modifier = Modifier + .padding(end = 16.dp) + ) + Text( + text = stringResource(R.string.root_require_for_install), + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onErrorContainer + ) } } } @@ -514,77 +747,33 @@ fun rememberSelectKmiDialog(onSelected: (String?) -> Unit): DialogHandle { val supportedKmi by produceState(initialValue = emptyList()) { value = getSupportedKmis() } - val listOptions = supportedKmi.map { value -> + val options = supportedKmi.map { value -> ListOption( - titleText = value, - subtitleText = null, - icon = null + titleText = value ) } - var selection: String? = null - val cardColor = if (!ThemeConfig.useDynamicColor) { - ThemeConfig.currentTheme.ButtonContrast - } else { - MaterialTheme.colorScheme.secondaryContainer - } + var selection by remember { mutableStateOf(null) } + val backgroundColor = MaterialTheme.colorScheme.surfaceContainerHighest - AlertDialog( - onDismissRequest = { + MaterialTheme( + colorScheme = MaterialTheme.colorScheme.copy( + surface = backgroundColor + ) + ) { + ListDialog(state = rememberUseCaseState(visible = true, onFinishedRequest = { + onSelected(selection) + }, onCloseRequest = { dismiss() - }, - title = { - Text(text = stringResource(R.string.select_kmi)) - }, - text = { - Column { - listOptions.forEachIndexed { index, option -> - Row( - modifier = Modifier - .clickable { - selection = supportedKmi[index] - } - .padding(vertical = 8.dp) - ) { - Column { - Text(text = option.titleText) - option.subtitleText?.let { - Text( - text = it, - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) - } - } - } - } - } - }, - confirmButton = { - TextButton( - onClick = { - if (selection != null) { - onSelected(selection) - } - dismiss() - } - ) { - Text(text = stringResource(android.R.string.ok)) - } - }, - dismissButton = { - TextButton( - onClick = { - dismiss() - } - ) { - Text(text = stringResource(android.R.string.cancel)) - } - }, - containerColor = getCardColors(cardColor.copy(alpha = 0.9f)).containerColor.copy(alpha = 0.9f), - shape = MaterialTheme.shapes.medium, - tonalElevation = 0.dp - ) + }), header = Header.Default( + title = stringResource(R.string.select_kmi), + ), selection = ListSelection.Single( + showRadioButtons = true, + options = options, + ) { _, option -> + selection = option.titleText + }) + } } } @@ -595,18 +784,26 @@ private fun TopBar( onLkmUpload: () -> Unit = {}, scrollBehavior: TopAppBarScrollBehavior? = null ) { - val cardColor = MaterialTheme.colorScheme.secondaryContainer - val cardAlpha = CardConfig.cardAlpha + val cardColor = MaterialTheme.colorScheme.surfaceVariant + val cardAlpha = cardAlpha TopAppBar( - title = { Text(stringResource(R.string.install)) }, + title = { + Text( + stringResource(R.string.install), + style = MaterialTheme.typography.titleLarge + ) + }, colors = TopAppBarDefaults.topAppBarColors( containerColor = cardColor.copy(alpha = cardAlpha), scrolledContainerColor = cardColor.copy(alpha = cardAlpha) ), navigationIcon = { IconButton(onClick = onBack) { - Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) + Icon( + Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = stringResource(R.string.back) + ) } }, windowInsets = WindowInsets.safeDrawing.only( diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Kpm.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Kpm.kt index a23451c1..b7d28ee7 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Kpm.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Kpm.kt @@ -38,6 +38,7 @@ import java.io.FileInputStream import java.io.InputStreamReader import java.net.* import android.app.Activity +import com.sukisu.ultra.ui.theme.CardConfig.cardElevation /** * KPM 管理界面 @@ -290,7 +291,6 @@ fun KpmScreen( Icon( imageVector = Icons.Filled.Refresh, contentDescription = stringResource(R.string.refresh), - tint = MaterialTheme.colorScheme.primary ) } } @@ -309,7 +309,6 @@ fun KpmScreen( Icon( imageVector = Icons.Filled.Add, contentDescription = stringResource(R.string.kpm_install), - tint = MaterialTheme.colorScheme.onPrimaryContainer ) }, text = { @@ -346,7 +345,6 @@ fun KpmScreen( Icon( imageVector = Icons.Filled.Info, contentDescription = null, - tint = MaterialTheme.colorScheme.onSecondaryContainer, modifier = Modifier .padding(end = 16.dp) .size(24.dp) @@ -654,12 +652,12 @@ private fun KpmModuleItem( Card( colors = getCardColors(MaterialTheme.colorScheme.surfaceContainerHigh), - elevation = CardDefaults.cardElevation(defaultElevation = 0.dp), + elevation = CardDefaults.cardElevation(defaultElevation = cardElevation), modifier = Modifier .fillMaxWidth() .clip(MaterialTheme.shapes.large) .shadow( - elevation = 0.dp, + elevation = cardElevation, shape = MaterialTheme.shapes.large, spotColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.1f) ) diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Module.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Module.kt index e5d553a8..c5d85f56 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Module.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Module.kt @@ -71,6 +71,7 @@ import androidx.core.content.edit import androidx.core.net.toUri import com.sukisu.ultra.ui.theme.ThemeConfig import com.sukisu.ultra.R +import com.sukisu.ultra.ui.theme.CardConfig.cardElevation @OptIn(ExperimentalMaterial3Api::class) @@ -233,7 +234,6 @@ fun ModuleScreen(navigator: DestinationsNavigator) { Icon( imageVector = Icons.Filled.MoreVert, contentDescription = stringResource(id = R.string.settings), - tint = MaterialTheme.colorScheme.primary ) DropdownMenu( @@ -294,7 +294,6 @@ fun ModuleScreen(navigator: DestinationsNavigator) { Icon( imageVector = Icons.Outlined.Download, contentDescription = stringResource(R.string.backup), - tint = MaterialTheme.colorScheme.primary ) }, onClick = { @@ -308,7 +307,6 @@ fun ModuleScreen(navigator: DestinationsNavigator) { Icon( imageVector = Icons.Outlined.Refresh, contentDescription = stringResource(R.string.restore), - tint = MaterialTheme.colorScheme.primary ) }, onClick = { @@ -343,7 +341,6 @@ fun ModuleScreen(navigator: DestinationsNavigator) { Icon( imageVector = Icons.Filled.Add, contentDescription = moduleInstall, - tint = MaterialTheme.colorScheme.onPrimaryContainer ) }, text = { @@ -690,12 +687,12 @@ fun ModuleItem( ) { ElevatedCard( colors = getCardColors(MaterialTheme.colorScheme.surfaceContainerHigh), - elevation = CardDefaults.cardElevation(defaultElevation = 0.dp), + elevation = CardDefaults.cardElevation(defaultElevation = cardElevation), modifier = Modifier .fillMaxWidth() .clip(MaterialTheme.shapes.large) .shadow( - elevation = 0.dp, + elevation = cardElevation, shape = MaterialTheme.shapes.large, spotColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.1f) ) @@ -919,7 +916,6 @@ fun ModuleItem( modifier = Modifier.size(20.dp), imageVector = Icons.Outlined.Delete, contentDescription = null, - tint = MaterialTheme.colorScheme.onErrorContainer ) } else { Icon( diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/MoreSettings.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/MoreSettings.kt index fbc840a3..9a70d541 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/MoreSettings.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/MoreSettings.kt @@ -307,11 +307,17 @@ fun MoreSettingsScreen(navigator: DestinationsNavigator) { ) } + val cardColor = MaterialTheme.colorScheme.surfaceVariant + val cardAlphaUse = CardConfig.cardAlpha + Scaffold( modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = { TopAppBar( title = { Text(stringResource(R.string.more_settings)) }, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = cardColor.copy(alpha = cardAlphaUse), + scrolledContainerColor = cardColor.copy(alpha = cardAlphaUse)), navigationIcon = { IconButton(onClick = { navigator.popBackStack() }) { Icon(Icons.AutoMirrored.Filled.ArrowBack, @@ -395,7 +401,7 @@ fun MoreSettingsScreen(navigator: DestinationsNavigator) { // 只在未启用动态颜色时显示主题色选择 AnimatedVisibility( - visible = !useDynamicColor, + visible = Build.VERSION.SDK_INT < Build.VERSION_CODES.S || !useDynamicColor, enter = fadeIn() + expandVertically(), exit = fadeOut() + shrinkVertically() ) { @@ -465,17 +471,25 @@ fun MoreSettingsScreen(navigator: DestinationsNavigator) { } else { context.saveCustomBackground(null) isCustomBackgroundEnabled = false - CardConfig.cardElevation = CardConfig.defaultElevation - CardConfig.cardAlpha = 0.80f + CardConfig.cardElevation + CardConfig.cardAlpha = 1f CardConfig.isCustomAlphaSet = false CardConfig.isCustomBackgroundEnabled = false saveCardConfig(context) - cardAlpha = 0.80f + cardAlpha = 1f // 重置其他相关设置 ThemeConfig.needsResetOnThemeChange = true + ThemeConfig.preventBackgroundRefresh = false + + context.getSharedPreferences("theme_prefs", Context.MODE_PRIVATE) + .edit { + putBoolean( + "prevent_background_refresh", + false + ) + } - // 显示成功提示 Toast.makeText( context, context.getString(R.string.background_removed), @@ -784,7 +798,7 @@ fun MoreSettingsScreen(navigator: DestinationsNavigator) { CardConfig.isUserDarkModeEnabled = true CardConfig.isUserLightModeEnabled = false if (!CardConfig.isCustomAlphaSet) { - CardConfig.cardAlpha = 0.35f + CardConfig.cardAlpha = 1f } CardConfig.save(context) } @@ -793,7 +807,7 @@ fun MoreSettingsScreen(navigator: DestinationsNavigator) { CardConfig.isUserLightModeEnabled = true CardConfig.isUserDarkModeEnabled = false if (!CardConfig.isCustomAlphaSet) { - CardConfig.cardAlpha = 0.80f + CardConfig.cardAlpha = 1f } CardConfig.save(context) } diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Settings.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Settings.kt index 61f01dd0..04e9ba4b 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Settings.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Settings.kt @@ -52,6 +52,7 @@ import com.sukisu.ultra.* import com.sukisu.ultra.ui.component.* import com.sukisu.ultra.ui.theme.* import com.sukisu.ultra.ui.theme.CardConfig.cardAlpha +import com.sukisu.ultra.ui.theme.CardConfig.cardElevation import com.sukisu.ultra.ui.util.LocalSnackbarHost import com.sukisu.ultra.ui.util.getBugreportFile import java.time.LocalDateTime @@ -117,7 +118,7 @@ fun SettingScreen(navigator: DestinationsNavigator) { colors = CardDefaults.cardColors( containerColor = MaterialTheme.colorScheme.surfaceContainerLow.copy(alpha = cardAlpha) ), - elevation = CardDefaults.cardElevation(defaultElevation = 0.dp) + elevation = CardDefaults.cardElevation(defaultElevation = cardElevation) ) { Column(modifier = Modifier.padding(vertical = 8.dp)) { Text( @@ -190,7 +191,7 @@ fun SettingScreen(navigator: DestinationsNavigator) { colors = CardDefaults.cardColors( containerColor = MaterialTheme.colorScheme.surfaceContainerLow.copy(alpha = cardAlpha) ), - elevation = CardDefaults.cardElevation(defaultElevation = 0.dp) + elevation = CardDefaults.cardElevation(defaultElevation = cardElevation) ) { Column(modifier = Modifier.padding(vertical = 8.dp)) { Text( @@ -258,7 +259,7 @@ fun SettingScreen(navigator: DestinationsNavigator) { colors = CardDefaults.cardColors( containerColor = MaterialTheme.colorScheme.surfaceContainerLow.copy(alpha = cardAlpha) ), - elevation = CardDefaults.cardElevation(defaultElevation = 0.dp) + elevation = CardDefaults.cardElevation(defaultElevation = cardElevation) ) { Column(modifier = Modifier.padding(vertical = 8.dp)) { Text( @@ -357,7 +358,7 @@ fun SettingScreen(navigator: DestinationsNavigator) { colors = CardDefaults.cardColors( containerColor = MaterialTheme.colorScheme.surfaceContainerLow.copy(alpha = cardAlpha) ), - elevation = CardDefaults.cardElevation(defaultElevation = 0.dp) + elevation = CardDefaults.cardElevation(defaultElevation = cardElevation) ) { Column(modifier = Modifier.padding(vertical = 8.dp)) { Text( @@ -701,7 +702,7 @@ private fun TopBar( val cardAlpha = if (ThemeConfig.customBackgroundUri != null) { cardAlpha } else { - if (systemIsDark) 0.35f else 0.80f + if (systemIsDark) 0.8f else 1f } TopAppBar( diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/SuperUser.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/SuperUser.kt index 4bea9df0..5970f0ba 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/SuperUser.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/SuperUser.kt @@ -40,6 +40,7 @@ import com.ramcosta.composedestinations.navigation.DestinationsNavigator import kotlinx.coroutines.launch import com.sukisu.ultra.Natives import com.sukisu.ultra.ui.component.SearchAppBar +import com.sukisu.ultra.ui.theme.CardConfig.cardElevation import com.sukisu.ultra.ui.util.ModuleModify import com.sukisu.ultra.ui.viewmodel.SuperUserViewModel @@ -87,7 +88,6 @@ fun SuperUserScreen(navigator: DestinationsNavigator) { Icon( imageVector = Icons.Filled.MoreVert, contentDescription = stringResource(id = R.string.settings), - tint = MaterialTheme.colorScheme.primary ) DropdownMenu(expanded = showDropdown, onDismissRequest = { @@ -99,7 +99,6 @@ fun SuperUserScreen(navigator: DestinationsNavigator) { Icon( imageVector = Icons.Filled.Refresh, contentDescription = null, - tint = MaterialTheme.colorScheme.primary ) }, onClick = { @@ -124,7 +123,6 @@ fun SuperUserScreen(navigator: DestinationsNavigator) { imageVector = if (viewModel.showSystemApps) Icons.Filled.VisibilityOff else Icons.Filled.Visibility, contentDescription = null, - tint = MaterialTheme.colorScheme.primary ) }, onClick = { @@ -139,7 +137,6 @@ fun SuperUserScreen(navigator: DestinationsNavigator) { Icon( imageVector = Icons.Filled.Save, contentDescription = null, - tint = MaterialTheme.colorScheme.primary ) }, onClick = { @@ -153,7 +150,6 @@ fun SuperUserScreen(navigator: DestinationsNavigator) { Icon( imageVector = Icons.Filled.RestoreFromTrash, contentDescription = null, - tint = MaterialTheme.colorScheme.primary ) }, onClick = { @@ -178,8 +174,8 @@ fun SuperUserScreen(navigator: DestinationsNavigator) { ) { Surface( color = MaterialTheme.colorScheme.surfaceContainerHighest, - tonalElevation = 0.dp, - shadowElevation = 0.dp + tonalElevation = cardElevation, + shadowElevation = cardElevation ) { Row( modifier = Modifier diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Template.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Template.kt index a2dc3513..38976757 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Template.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Template.kt @@ -33,6 +33,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarColors import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.material3.pulltorefresh.PullToRefreshBox @@ -61,6 +62,7 @@ import com.ramcosta.composedestinations.result.getOr import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import com.sukisu.ultra.R +import com.sukisu.ultra.ui.theme.CardConfig import com.sukisu.ultra.ui.theme.ThemeConfig import com.sukisu.ultra.ui.viewmodel.TemplateViewModel @@ -98,6 +100,9 @@ fun AppProfileTemplateScreen( } } + val cardColorUse = MaterialTheme.colorScheme.surfaceVariant + val cardAlpha = CardConfig.cardAlpha + Scaffold( topBar = { val context = LocalContext.current @@ -109,6 +114,10 @@ fun AppProfileTemplateScreen( } TopBar( onBack = dropUnlessResumed { navigator.popBackStack() }, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = cardColorUse.copy(alpha = cardAlpha), + scrolledContainerColor = cardColorUse.copy(alpha = cardAlpha) + ), onSync = { scope.launch { viewModel.fetchTemplates(true) } }, @@ -226,12 +235,20 @@ private fun TopBar( onSync: () -> Unit = {}, onImport: () -> Unit = {}, onExport: () -> Unit = {}, + colors: TopAppBarColors, scrollBehavior: TopAppBarScrollBehavior? = null ) { + val cardColor = MaterialTheme.colorScheme.surfaceVariant + val cardAlpha = CardConfig.cardAlpha + TopAppBar( title = { Text(stringResource(R.string.settings_profile_template)) }, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = cardColor.copy(alpha = cardAlpha), + scrolledContainerColor = cardColor.copy(alpha = cardAlpha) + ), navigationIcon = { IconButton( onClick = onBack diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/theme/CardManage.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/theme/CardManage.kt index 701ea61a..431ed164 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/theme/CardManage.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/theme/CardManage.kt @@ -13,10 +13,11 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp object CardConfig { - val defaultElevation: Dp = 1.dp + val settingElevation: Dp = 4.dp + val customBackgroundElevation: Dp = 0.dp - var cardAlpha by mutableStateOf(0.80f) - var cardElevation by mutableStateOf(defaultElevation) + var cardAlpha by mutableStateOf(1f) + var cardElevation by mutableStateOf(settingElevation) var isShadowEnabled by mutableStateOf(true) var isCustomAlphaSet by mutableStateOf(false) var isUserDarkModeEnabled by mutableStateOf(false) @@ -44,13 +45,13 @@ object CardConfig { */ fun load(context: Context) { val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE) - cardAlpha = prefs.getFloat("card_alpha", 0.80f) + cardAlpha = prefs.getFloat("card_alpha", 1f) isCustomBackgroundEnabled = prefs.getBoolean("custom_background_enabled", false) isShadowEnabled = prefs.getBoolean("is_shadow_enabled", true) - cardElevation = if (isShadowEnabled) defaultElevation else 0.dp isCustomAlphaSet = prefs.getBoolean("is_custom_alpha_set", false) isUserDarkModeEnabled = prefs.getBoolean("is_user_dark_mode_enabled", false) isUserLightModeEnabled = prefs.getBoolean("is_user_light_mode_enabled", false) + updateShadowEnabled(isShadowEnabled) } /** @@ -58,7 +59,13 @@ object CardConfig { */ fun updateShadowEnabled(enabled: Boolean) { isShadowEnabled = enabled - cardElevation = if (enabled) defaultElevation else 0.dp + cardElevation = if (isCustomBackgroundEnabled && cardAlpha != 1f) { + customBackgroundElevation + } else if (enabled) { + settingElevation + } else { + customBackgroundElevation + } } /** @@ -66,11 +73,9 @@ object CardConfig { */ fun setDarkModeDefaults() { if (!isCustomAlphaSet) { - cardAlpha = 0.50f - } - if (!isShadowEnabled) { - cardElevation = 0.dp + cardAlpha = 1f } + updateShadowEnabled(isShadowEnabled) } } diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/theme/Theme.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/theme/Theme.kt index e24c7e18..707b6109 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/theme/Theme.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/theme/Theme.kt @@ -56,6 +56,8 @@ object ThemeConfig { var backgroundImageLoaded by mutableStateOf(false) var needsResetOnThemeChange by mutableStateOf(false) var isThemeChanging by mutableStateOf(false) + var preventBackgroundRefresh by mutableStateOf(false) + private var lastDarkModeState: Boolean? = null fun detectThemeChange(currentDarkMode: Boolean): Boolean { val isChanged = lastDarkModeState != null && lastDarkModeState != currentDarkMode @@ -64,7 +66,9 @@ object ThemeConfig { } fun resetBackgroundState() { - backgroundImageLoaded = false + if (!preventBackgroundRefresh) { + backgroundImageLoaded = false + } isThemeChanging = true } } @@ -92,14 +96,14 @@ fun KernelSUTheme( Log.d("ThemeSystem", "系统主题变化检测: 从 ${!systemIsDark} 变为 $systemIsDark") ThemeConfig.resetBackgroundState() - // 强制重新加载自定义背景 - context.loadCustomBackground() + if (!ThemeConfig.preventBackgroundRefresh) { + context.loadCustomBackground() + } - // 调整卡片样式以适应新主题 CardConfig.apply { load(context) if (!isCustomAlphaSet) { - cardAlpha = if (systemIsDark) 0.35f else 0.80f + cardAlpha = if (systemIsDark) 0.50f else 1f } save(context) } @@ -108,14 +112,18 @@ fun KernelSUTheme( // 初始加载配置 LaunchedEffect(Unit) { - context.loadCustomBackground() + context.loadThemeMode() context.loadThemeColors() context.loadDynamicColorState() - context.loadThemeMode() CardConfig.load(context) - // 立即将加载状态设为false,确保首次会触发加载动画 - ThemeConfig.backgroundImageLoaded = false + if (!ThemeConfig.backgroundImageLoaded && !ThemeConfig.preventBackgroundRefresh) { + context.loadCustomBackground() + ThemeConfig.backgroundImageLoaded = false + } + + ThemeConfig.preventBackgroundRefresh = context.getSharedPreferences("theme_prefs", Context.MODE_PRIVATE) + .getBoolean("prevent_background_refresh", true) } // 创建颜色方案 @@ -134,15 +142,12 @@ fun KernelSUTheme( } CardConfig.updateShadowEnabled(!isDarkModeWithCustomBackground) - // 使用rememberSaveable保留背景URI状态,防止在主题切换时丢失 val backgroundUri = rememberSaveable { mutableStateOf(ThemeConfig.customBackgroundUri) } - // 确保状态同步 LaunchedEffect(ThemeConfig.customBackgroundUri) { backgroundUri.value = ThemeConfig.customBackgroundUri } - // 背景图加载器 - 使用保存的URI状态 val bgImagePainter = backgroundUri.value?.let { rememberAsyncImagePainter( model = it, @@ -155,11 +160,14 @@ fun KernelSUTheme( Log.d("ThemeSystem", "背景图加载成功") ThemeConfig.backgroundImageLoaded = true ThemeConfig.isThemeChanging = false + + ThemeConfig.preventBackgroundRefresh = true + context.getSharedPreferences("theme_prefs", Context.MODE_PRIVATE) + .edit { putBoolean("prevent_background_refresh", true) } } ) } - // 背景透明度动画 - 使用更强健的动画配置 val transition = updateTransition( targetState = ThemeConfig.backgroundImageLoaded, label = "bgTransition" @@ -174,7 +182,6 @@ fun KernelSUTheme( } ) { loaded -> if (loaded) 1f else 0f } - // 清理函数,确保主题切换完成后重置状态 DisposableEffect(systemIsDark) { onDispose { if (ThemeConfig.isThemeChanging) { @@ -188,7 +195,6 @@ fun KernelSUTheme( typography = Typography ) { Box(modifier = Modifier.fillMaxSize()) { - // 底色层 - 确保有底色 Box( modifier = Modifier .fillMaxSize() @@ -346,15 +352,18 @@ private fun createLightColorScheme() = lightColorScheme( ) /** - * 复制图片到应用内部存储 + * 复制图片到应用内部存储并提升持久性 */ private fun Context.copyImageToInternalStorage(uri: Uri): Uri? { return try { val contentResolver: ContentResolver = contentResolver val inputStream: InputStream = contentResolver.openInputStream(uri) ?: return null + val fileName = "custom_background.jpg" val file = File(filesDir, fileName) - val outputStream = FileOutputStream(file) + + val backupFile = File(filesDir, "${fileName}.backup") + val outputStream = FileOutputStream(backupFile) val buffer = ByteArray(4 * 1024) var read: Int @@ -366,6 +375,11 @@ private fun Context.copyImageToInternalStorage(uri: Uri): Uri? { outputStream.close() inputStream.close() + if (file.exists()) { + file.delete() + } + backupFile.renameTo(file) + Uri.fromFile(file) } catch (e: Exception) { Log.e("ImageCopy", "复制图片失败: ${e.message}") @@ -383,13 +397,17 @@ fun Context.saveAndApplyCustomBackground(uri: Uri, transformation: BackgroundTra copyImageToInternalStorage(uri) } + // 保存到配置文件 getSharedPreferences("theme_prefs", Context.MODE_PRIVATE) .edit { putString("custom_background", finalUri?.toString()) + // 设置阻止刷新标志为false,允许新设置的背景加载一次 + putBoolean("prevent_background_refresh", false) } ThemeConfig.customBackgroundUri = finalUri ThemeConfig.backgroundImageLoaded = false + ThemeConfig.preventBackgroundRefresh = false CardConfig.cardElevation = 0.dp CardConfig.isCustomBackgroundEnabled = true } @@ -399,13 +417,23 @@ fun Context.saveAndApplyCustomBackground(uri: Uri, transformation: BackgroundTra */ fun Context.saveCustomBackground(uri: Uri?) { val newUri = uri?.let { copyImageToInternalStorage(it) } + + // 保存到配置文件 getSharedPreferences("theme_prefs", Context.MODE_PRIVATE) .edit { putString("custom_background", newUri?.toString()) + if (uri == null) { + // 如果清除背景,也重置阻止刷新标志 + putBoolean("prevent_background_refresh", false) + } else { + // 设置阻止刷新标志为false,允许新设置的背景加载一次 + putBoolean("prevent_background_refresh", false) + } } ThemeConfig.customBackgroundUri = newUri ThemeConfig.backgroundImageLoaded = false + ThemeConfig.preventBackgroundRefresh = false if (uri != null) { CardConfig.cardElevation = 0.dp @@ -420,10 +448,14 @@ fun Context.loadCustomBackground() { val uriString = getSharedPreferences("theme_prefs", Context.MODE_PRIVATE) .getString("custom_background", null) - // 判断是否有实际变化,避免无谓的重新加载 val newUri = uriString?.toUri() - if (ThemeConfig.customBackgroundUri?.toString() != newUri?.toString()) { - Log.d("ThemeSystem", "加载自定义背景: $uriString") + val preventRefresh = getSharedPreferences("theme_prefs", Context.MODE_PRIVATE) + .getBoolean("prevent_background_refresh", false) + + ThemeConfig.preventBackgroundRefresh = preventRefresh + + if (!preventRefresh || ThemeConfig.customBackgroundUri?.toString() != newUri?.toString()) { + Log.d("ThemeSystem", "加载自定义背景: $uriString, 阻止刷新: $preventRefresh") ThemeConfig.customBackgroundUri = newUri ThemeConfig.backgroundImageLoaded = false } diff --git a/manager/app/src/main/res/values-ja/strings.xml b/manager/app/src/main/res/values-ja/strings.xml index b8c2d2be..ef1197f0 100644 --- a/manager/app/src/main/res/values-ja/strings.xml +++ b/manager/app/src/main/res/values-ja/strings.xml @@ -299,7 +299,7 @@ フラッシュに失敗しました LKM の修復またはインストール - GKI のインストール + GKI/non-GKI のインストール カーネルのバージョン: %1$s パッチ適用ツールの使用: %1$s 設定 @@ -324,4 +324,5 @@ SuSFS 無効 背景の設定が成功しました カスタム背景を削除しました + root 権限が必要 diff --git a/manager/app/src/main/res/values-zh-rCN/strings.xml b/manager/app/src/main/res/values-zh-rCN/strings.xml index 6e695bc1..05920126 100644 --- a/manager/app/src/main/res/values-zh-rCN/strings.xml +++ b/manager/app/src/main/res/values-zh-rCN/strings.xml @@ -222,8 +222,8 @@ 重启失败 - 批量授权 - 批量取消授权 + 授权 + 撤销 黄色 内核模块 内核模块 @@ -295,7 +295,7 @@ 刷写失败 LKM修补/安装 - GKI安装 + GKI/non-GKI安装 内核版本:%1$s 使用修补工具:%1$s 配置 @@ -320,4 +320,5 @@ SuSFS 已禁用 背景设置成功 已移除自定义背景 + 需要 root 权限 diff --git a/manager/app/src/main/res/values/strings.xml b/manager/app/src/main/res/values/strings.xml index c4db7c83..d8caed08 100644 --- a/manager/app/src/main/res/values/strings.xml +++ b/manager/app/src/main/res/values/strings.xml @@ -222,8 +222,8 @@ Yes No Reboot Failed - Bulk license - Batch cancel authorization + authorizations + withdraw Backup Yellow Kernel Module @@ -299,7 +299,7 @@ Flash failed LKM repair/installation - GKI installation + GKI/non-GKI installation Kernel version:%1$s Using the patching tool:%1$s Configure @@ -324,4 +324,5 @@ SuSFS disabled Background set successfully Removed custom backgrounds + Requires root privileges