manager: several UI improvements (#515)
This commit is contained in:
@@ -2,9 +2,13 @@ package me.weishu.kernelsu.ui.component.profile
|
|||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
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.DropdownMenuItem
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.ExposedDropdownMenuBox
|
import androidx.compose.material3.ExposedDropdownMenuBox
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.ListItem
|
import androidx.compose.material3.ListItem
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Text
|
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) {
|
val currentNamespace = when (profile.namespace) {
|
||||||
RootProfile.Namespace.Inherited -> stringResource(R.string.profile_namespace_inherited)
|
RootProfile.Namespace.Inherited -> stringResource(R.string.profile_namespace_inherited)
|
||||||
RootProfile.Namespace.Global -> stringResource(R.string.profile_namespace_global)
|
RootProfile.Namespace.Global -> stringResource(R.string.profile_namespace_global)
|
||||||
@@ -45,8 +49,8 @@ fun RootProfileConfig(
|
|||||||
}
|
}
|
||||||
ListItem(headlineContent = {
|
ListItem(headlineContent = {
|
||||||
ExposedDropdownMenuBox(
|
ExposedDropdownMenuBox(
|
||||||
expanded = namespaceBoxExpanded,
|
expanded = expanded,
|
||||||
onExpandedChange = { namespaceBoxExpanded = it }
|
onExpandedChange = { expanded = !expanded }
|
||||||
) {
|
) {
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
modifier = Modifier.menuAnchor(),
|
modifier = Modifier.menuAnchor(),
|
||||||
@@ -54,30 +58,34 @@ fun RootProfileConfig(
|
|||||||
label = { Text(stringResource(R.string.profile_namespace)) },
|
label = { Text(stringResource(R.string.profile_namespace)) },
|
||||||
value = currentNamespace,
|
value = currentNamespace,
|
||||||
onValueChange = {},
|
onValueChange = {},
|
||||||
|
trailingIcon = {
|
||||||
|
if (expanded) Icon(Icons.Filled.ArrowDropUp, null)
|
||||||
|
else Icon(Icons.Filled.ArrowDropDown, null)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
ExposedDropdownMenu(
|
ExposedDropdownMenu(
|
||||||
expanded = namespaceBoxExpanded,
|
expanded = expanded,
|
||||||
onDismissRequest = { namespaceBoxExpanded = false }
|
onDismissRequest = { expanded = false }
|
||||||
) {
|
) {
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text(stringResource(R.string.profile_namespace_inherited)) },
|
text = { Text(stringResource(R.string.profile_namespace_inherited)) },
|
||||||
onClick = {
|
onClick = {
|
||||||
onProfileChange(profile.copy(namespace = RootProfile.Namespace.Inherited))
|
onProfileChange(profile.copy(namespace = RootProfile.Namespace.Inherited))
|
||||||
namespaceBoxExpanded = false
|
expanded = false
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text(stringResource(R.string.profile_namespace_global)) },
|
text = { Text(stringResource(R.string.profile_namespace_global)) },
|
||||||
onClick = {
|
onClick = {
|
||||||
onProfileChange(profile.copy(namespace = RootProfile.Namespace.Global))
|
onProfileChange(profile.copy(namespace = RootProfile.Namespace.Global))
|
||||||
namespaceBoxExpanded = false
|
expanded = false
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = { Text(stringResource(R.string.profile_namespace_individual)) },
|
text = { Text(stringResource(R.string.profile_namespace_individual)) },
|
||||||
onClick = {
|
onClick = {
|
||||||
onProfileChange(profile.copy(namespace = RootProfile.Namespace.Individual))
|
onProfileChange(profile.copy(namespace = RootProfile.Namespace.Individual))
|
||||||
namespaceBoxExpanded = false
|
expanded = false
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,25 @@
|
|||||||
package me.weishu.kernelsu.ui.screen
|
package me.weishu.kernelsu.ui.screen
|
||||||
|
|
||||||
import android.os.Parcelable
|
import androidx.annotation.StringRes
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
|
||||||
import androidx.compose.animation.Crossfade
|
import androidx.compose.animation.Crossfade
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
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.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.material.icons.Icons
|
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.Android
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
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.material.icons.filled.Security
|
||||||
import androidx.compose.material3.Divider
|
import androidx.compose.material3.Divider
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.ExposedDropdownMenuBox
|
import androidx.compose.material3.ExposedDropdownMenuBox
|
||||||
|
import androidx.compose.material3.FilterChip
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.ListItem
|
import androidx.compose.material3.ListItem
|
||||||
@@ -39,12 +45,10 @@ import coil.request.ImageRequest
|
|||||||
import com.ramcosta.composedestinations.annotation.Destination
|
import com.ramcosta.composedestinations.annotation.Destination
|
||||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.parcelize.Parcelize
|
|
||||||
import me.weishu.kernelsu.Natives
|
import me.weishu.kernelsu.Natives
|
||||||
import me.weishu.kernelsu.R
|
import me.weishu.kernelsu.R
|
||||||
import me.weishu.kernelsu.profile.AppProfile
|
import me.weishu.kernelsu.profile.AppProfile
|
||||||
import me.weishu.kernelsu.profile.RootProfile
|
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.SwitchItem
|
||||||
import me.weishu.kernelsu.ui.component.profile.AppProfileConfig
|
import me.weishu.kernelsu.ui.component.profile.AppProfileConfig
|
||||||
import me.weishu.kernelsu.ui.component.profile.RootProfileConfig
|
import me.weishu.kernelsu.ui.component.profile.RootProfileConfig
|
||||||
@@ -126,96 +130,69 @@ private fun AppProfileInner(
|
|||||||
onCheckedChange = onSwitchRootPermission,
|
onCheckedChange = onSwitchRootPermission,
|
||||||
)
|
)
|
||||||
|
|
||||||
Divider(thickness = Dp.Hairline)
|
|
||||||
|
|
||||||
Crossfade(targetState = isRootGranted, label = "") { current ->
|
Crossfade(targetState = isRootGranted, label = "") { current ->
|
||||||
if (current) {
|
Column {
|
||||||
var mode: Mode<RootProfile> by rememberSaveable { mutableStateOf(Mode.Default()) }
|
if (current) {
|
||||||
var template by rememberSaveable { mutableStateOf("None") }
|
var mode by rememberSaveable { mutableStateOf(Mode.Default) }
|
||||||
var profile by rememberSaveable { mutableStateOf(RootProfile("@$packageName")) }
|
ProfileBox(mode, true) { mode = it }
|
||||||
|
Crossfade(targetState = mode, label = "") { currentMode ->
|
||||||
Column {
|
if (currentMode == Mode.Template) {
|
||||||
RadioItem(
|
var expanded by remember { mutableStateOf(false) }
|
||||||
title = stringResource(R.string.profile_default),
|
var template by rememberSaveable { mutableStateOf("None") }
|
||||||
selected = mode is Mode.Default,
|
ListItem(headlineContent = {
|
||||||
onClick = { mode = Mode.Default() }
|
ExposedDropdownMenuBox(
|
||||||
)
|
expanded = expanded,
|
||||||
|
onExpandedChange = { expanded = it },
|
||||||
RadioItem(
|
) {
|
||||||
title = stringResource(R.string.profile_template),
|
OutlinedTextField(
|
||||||
selected = mode is Mode.Template,
|
modifier = Modifier.menuAnchor(),
|
||||||
onClick = { mode = Mode.Template("") }
|
readOnly = true,
|
||||||
)
|
label = { Text(stringResource(R.string.profile_template)) },
|
||||||
AnimatedVisibility(mode is Mode.Template) {
|
value = template,
|
||||||
var expanded by remember { mutableStateOf(false) }
|
onValueChange = {},
|
||||||
ListItem(headlineContent = {
|
trailingIcon = {
|
||||||
ExposedDropdownMenuBox(
|
if (expanded) Icon(Icons.Filled.ArrowDropUp, null)
|
||||||
expanded = expanded,
|
else Icon(Icons.Filled.ArrowDropDown, null)
|
||||||
onExpandedChange = { expanded = it },
|
},
|
||||||
) {
|
)
|
||||||
OutlinedTextField(
|
// TODO: Template
|
||||||
modifier = Modifier.menuAnchor(),
|
}
|
||||||
readOnly = true,
|
})
|
||||||
label = { Text(stringResource(R.string.profile_template)) },
|
} else if (mode == Mode.Custom) {
|
||||||
value = template,
|
var profile by rememberSaveable { mutableStateOf(RootProfile("@$packageName")) }
|
||||||
onValueChange = {}
|
RootProfileConfig(
|
||||||
)
|
fixedName = true,
|
||||||
// TODO: Template
|
profile = profile,
|
||||||
}
|
onProfileChange = { profile = it }
|
||||||
})
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
RadioItem(
|
var mode by rememberSaveable { mutableStateOf(Mode.Default) }
|
||||||
title = stringResource(R.string.profile_custom),
|
ProfileBox(mode, false) { mode = it }
|
||||||
selected = mode is Mode.Custom,
|
Crossfade(targetState = mode, label = "") { currentMode ->
|
||||||
onClick = { mode = Mode.Custom(profile) }
|
if (currentMode == Mode.Custom) {
|
||||||
)
|
var profile by rememberSaveable { mutableStateOf(AppProfile(packageName)) }
|
||||||
AnimatedVisibility(mode is Mode.Custom) {
|
AppProfileConfig(
|
||||||
RootProfileConfig(
|
fixedName = true,
|
||||||
fixedName = true,
|
profile = profile,
|
||||||
profile = profile,
|
onProfileChange = { profile = it }
|
||||||
onProfileChange = { profile = it }
|
)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
var mode: Mode<AppProfile> 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 enum class Mode(@StringRes private val res: Int) {
|
||||||
private sealed class Mode<P : Parcelable> : Parcelable {
|
Default(R.string.profile_default),
|
||||||
|
Template(R.string.profile_template),
|
||||||
|
Custom(R.string.profile_custom);
|
||||||
|
|
||||||
class Default<P : Parcelable> : Mode<P>()
|
val text: String
|
||||||
|
@Composable get() = stringResource(res)
|
||||||
class Template<P : Parcelable>(val template: String) : Mode<P>()
|
|
||||||
|
|
||||||
class Custom<P : Parcelable>(val profile: P) : Mode<P>()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@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
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
private fun AppProfilePreview() {
|
private fun AppProfilePreview() {
|
||||||
|
|||||||
@@ -66,9 +66,9 @@
|
|||||||
<string name="home_support_content">KernelSU is, and always will be, free, and open source. You can however show us that you care by making a donation.</string>
|
<string name="home_support_content">KernelSU is, and always will be, free, and open source. You can however show us that you care by making a donation.</string>
|
||||||
<string name="about_source_code"><![CDATA[View source code at %1$s<br/>Join our %2$s channel]]></string>
|
<string name="about_source_code"><![CDATA[View source code at %1$s<br/>Join our %2$s channel]]></string>
|
||||||
<string name="profile">App profile</string>
|
<string name="profile">App profile</string>
|
||||||
<string name="profile_default">Use default profile</string>
|
<string name="profile_default">Default</string>
|
||||||
<string name="profile_template">Use template profile</string>
|
<string name="profile_template">Template</string>
|
||||||
<string name="profile_custom">Use custom profile</string>
|
<string name="profile_custom">Custom</string>
|
||||||
<string name="profile_name">Profile name</string>
|
<string name="profile_name">Profile name</string>
|
||||||
<string name="profile_namespace">Mount namespace</string>
|
<string name="profile_namespace">Mount namespace</string>
|
||||||
<string name="profile_namespace_inherited">Inherited</string>
|
<string name="profile_namespace_inherited">Inherited</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user