manager: continue to improve the UI
- Expose anykernel3 flashing as long as there is root. - Opt some styles
This commit is contained in:
@@ -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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
@@ -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<String>()) {
|
||||
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<String?>(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(
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user