diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/RootProfileConfig.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/RootProfileConfig.kt index 9a2fbe74..387b932b 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/RootProfileConfig.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/component/profile/RootProfileConfig.kt @@ -2,9 +2,13 @@ package me.weishu.kernelsu.ui.component.profile import androidx.compose.foundation.layout.Column import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowDropDown +import androidx.compose.material.icons.filled.ArrowDropUp import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExposedDropdownMenuBox +import androidx.compose.material3.Icon import androidx.compose.material3.ListItem import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text @@ -37,7 +41,7 @@ fun RootProfileConfig( ) } - var namespaceBoxExpanded by remember { mutableStateOf(false) } + var expanded by remember { mutableStateOf(false) } val currentNamespace = when (profile.namespace) { RootProfile.Namespace.Inherited -> stringResource(R.string.profile_namespace_inherited) RootProfile.Namespace.Global -> stringResource(R.string.profile_namespace_global) @@ -45,8 +49,8 @@ fun RootProfileConfig( } ListItem(headlineContent = { ExposedDropdownMenuBox( - expanded = namespaceBoxExpanded, - onExpandedChange = { namespaceBoxExpanded = it } + expanded = expanded, + onExpandedChange = { expanded = !expanded } ) { OutlinedTextField( modifier = Modifier.menuAnchor(), @@ -54,30 +58,34 @@ fun RootProfileConfig( label = { Text(stringResource(R.string.profile_namespace)) }, value = currentNamespace, onValueChange = {}, + trailingIcon = { + if (expanded) Icon(Icons.Filled.ArrowDropUp, null) + else Icon(Icons.Filled.ArrowDropDown, null) + }, ) ExposedDropdownMenu( - expanded = namespaceBoxExpanded, - onDismissRequest = { namespaceBoxExpanded = false } + expanded = expanded, + onDismissRequest = { expanded = false } ) { DropdownMenuItem( text = { Text(stringResource(R.string.profile_namespace_inherited)) }, onClick = { onProfileChange(profile.copy(namespace = RootProfile.Namespace.Inherited)) - namespaceBoxExpanded = false + expanded = false }, ) DropdownMenuItem( text = { Text(stringResource(R.string.profile_namespace_global)) }, onClick = { onProfileChange(profile.copy(namespace = RootProfile.Namespace.Global)) - namespaceBoxExpanded = false + expanded = false }, ) DropdownMenuItem( text = { Text(stringResource(R.string.profile_namespace_individual)) }, onClick = { onProfileChange(profile.copy(namespace = RootProfile.Namespace.Individual)) - namespaceBoxExpanded = false + expanded = false }, ) } diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/AppProfile.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/AppProfile.kt index e07f3228..eb0a42cd 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/AppProfile.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/AppProfile.kt @@ -1,19 +1,25 @@ package me.weishu.kernelsu.ui.screen -import android.os.Parcelable -import androidx.compose.animation.AnimatedVisibility +import androidx.annotation.StringRes import androidx.compose.animation.Crossfade +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.AccountCircle import androidx.compose.material.icons.filled.Android import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.filled.ArrowDropDown +import androidx.compose.material.icons.filled.ArrowDropUp import androidx.compose.material.icons.filled.Security import androidx.compose.material3.Divider import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExposedDropdownMenuBox +import androidx.compose.material3.FilterChip import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.ListItem @@ -39,12 +45,10 @@ import coil.request.ImageRequest import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.navigation.DestinationsNavigator import kotlinx.coroutines.launch -import kotlinx.parcelize.Parcelize import me.weishu.kernelsu.Natives import me.weishu.kernelsu.R import me.weishu.kernelsu.profile.AppProfile import me.weishu.kernelsu.profile.RootProfile -import me.weishu.kernelsu.ui.component.RadioItem import me.weishu.kernelsu.ui.component.SwitchItem import me.weishu.kernelsu.ui.component.profile.AppProfileConfig import me.weishu.kernelsu.ui.component.profile.RootProfileConfig @@ -126,96 +130,69 @@ private fun AppProfileInner( onCheckedChange = onSwitchRootPermission, ) - Divider(thickness = Dp.Hairline) - Crossfade(targetState = isRootGranted, label = "") { current -> - if (current) { - var mode: Mode by rememberSaveable { mutableStateOf(Mode.Default()) } - var template by rememberSaveable { mutableStateOf("None") } - var profile by rememberSaveable { mutableStateOf(RootProfile("@$packageName")) } - - Column { - RadioItem( - title = stringResource(R.string.profile_default), - selected = mode is Mode.Default, - onClick = { mode = Mode.Default() } - ) - - RadioItem( - title = stringResource(R.string.profile_template), - selected = mode is Mode.Template, - onClick = { mode = Mode.Template("") } - ) - AnimatedVisibility(mode is Mode.Template) { - var expanded by remember { mutableStateOf(false) } - ListItem(headlineContent = { - ExposedDropdownMenuBox( - expanded = expanded, - onExpandedChange = { expanded = it }, - ) { - OutlinedTextField( - modifier = Modifier.menuAnchor(), - readOnly = true, - label = { Text(stringResource(R.string.profile_template)) }, - value = template, - onValueChange = {} - ) - // TODO: Template - } - }) + Column { + if (current) { + var mode by rememberSaveable { mutableStateOf(Mode.Default) } + ProfileBox(mode, true) { mode = it } + Crossfade(targetState = mode, label = "") { currentMode -> + if (currentMode == Mode.Template) { + var expanded by remember { mutableStateOf(false) } + var template by rememberSaveable { mutableStateOf("None") } + ListItem(headlineContent = { + ExposedDropdownMenuBox( + expanded = expanded, + onExpandedChange = { expanded = it }, + ) { + OutlinedTextField( + modifier = Modifier.menuAnchor(), + readOnly = true, + label = { Text(stringResource(R.string.profile_template)) }, + value = template, + onValueChange = {}, + trailingIcon = { + if (expanded) Icon(Icons.Filled.ArrowDropUp, null) + else Icon(Icons.Filled.ArrowDropDown, null) + }, + ) + // TODO: Template + } + }) + } else if (mode == Mode.Custom) { + var profile by rememberSaveable { mutableStateOf(RootProfile("@$packageName")) } + RootProfileConfig( + fixedName = true, + profile = profile, + onProfileChange = { profile = it } + ) + } } - - RadioItem( - title = stringResource(R.string.profile_custom), - selected = mode is Mode.Custom, - onClick = { mode = Mode.Custom(profile) } - ) - AnimatedVisibility(mode is Mode.Custom) { - RootProfileConfig( - fixedName = true, - profile = profile, - onProfileChange = { profile = it } - ) + } else { + var mode by rememberSaveable { mutableStateOf(Mode.Default) } + ProfileBox(mode, false) { mode = it } + Crossfade(targetState = mode, label = "") { currentMode -> + if (currentMode == Mode.Custom) { + var profile by rememberSaveable { mutableStateOf(AppProfile(packageName)) } + AppProfileConfig( + fixedName = true, + profile = profile, + onProfileChange = { profile = it } + ) + } } } - } else { - var mode: Mode by rememberSaveable { mutableStateOf(Mode.Default()) } - var profile by rememberSaveable { mutableStateOf(AppProfile("@$packageName")) } - - Column { - RadioItem( - title = stringResource(R.string.profile_default), - selected = mode is Mode.Default, - onClick = { mode = Mode.Default() } - ) - - RadioItem( - title = stringResource(R.string.profile_custom), - selected = mode is Mode.Custom, - onClick = { mode = Mode.Custom(profile) } - ) - AnimatedVisibility(mode is Mode.Custom) { - AppProfileConfig( - fixedName = true, - profile = profile, - onProfileChange = { profile = it } - ) - } - } - } } } } -@Parcelize -private sealed class Mode

: Parcelable { +private enum class Mode(@StringRes private val res: Int) { + Default(R.string.profile_default), + Template(R.string.profile_template), + Custom(R.string.profile_custom); - class Default

: Mode

() - - class Template

(val template: String) : Mode

() - - class Custom

(val profile: P) : Mode

() + val text: String + @Composable get() = stringResource(res) } @OptIn(ExperimentalMaterial3Api::class) @@ -233,6 +210,45 @@ private fun TopBar(onBack: () -> Unit) { ) } +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun ProfileBox( + mode: Mode, + hasTemplate: Boolean, + onModeChange: (Mode) -> Unit, +) { + ListItem( + headlineContent = { Text(stringResource(R.string.profile)) }, + supportingContent = { Text(mode.text) }, + leadingContent = { Icon(Icons.Filled.AccountCircle, null) }, + ) + Divider(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) }, + ) + } + FilterChip( + selected = mode == Mode.Custom, + label = { Text(stringResource(R.string.profile_custom)) }, + onClick = { onModeChange(Mode.Custom) }, + ) + } + }) +} + @Preview @Composable private fun AppProfilePreview() { diff --git a/manager/app/src/main/res/values/strings.xml b/manager/app/src/main/res/values/strings.xml index f7c87028..d15d3772 100644 --- a/manager/app/src/main/res/values/strings.xml +++ b/manager/app/src/main/res/values/strings.xml @@ -66,9 +66,9 @@ KernelSU is, and always will be, free, and open source. You can however show us that you care by making a donation. Join our %2$s channel]]> App profile - Use default profile - Use template profile - Use custom profile + Default + Template + Custom Profile name Mount namespace Inherited