manager: Updated superuser interface, added sidebar categories
This commit is contained in:
@@ -0,0 +1,277 @@
|
||||
package com.sukisu.ultra.ui.component
|
||||
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.draw.scale
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import com.sukisu.ultra.R
|
||||
|
||||
// 菜单项数据类
|
||||
data class FabMenuItem(
|
||||
val icon: ImageVector,
|
||||
val labelRes: Int,
|
||||
val color: Color = Color.Unspecified,
|
||||
val onClick: () -> Unit
|
||||
)
|
||||
|
||||
// 动画配置
|
||||
object FabAnimationConfig {
|
||||
const val ANIMATION_DURATION = 300
|
||||
const val STAGGER_DELAY = 50
|
||||
val BUTTON_SPACING = 72.dp
|
||||
val BUTTON_SIZE = 56.dp
|
||||
val SMALL_BUTTON_SIZE = 48.dp
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun VerticalExpandableFab(
|
||||
menuItems: List<FabMenuItem>,
|
||||
modifier: Modifier = Modifier,
|
||||
buttonSize: Dp = FabAnimationConfig.BUTTON_SIZE,
|
||||
smallButtonSize: Dp = FabAnimationConfig.SMALL_BUTTON_SIZE,
|
||||
buttonSpacing: Dp = FabAnimationConfig.BUTTON_SPACING,
|
||||
animationDurationMs: Int = FabAnimationConfig.ANIMATION_DURATION,
|
||||
staggerDelayMs: Int = FabAnimationConfig.STAGGER_DELAY,
|
||||
mainButtonIcon: ImageVector = Icons.Filled.Add,
|
||||
mainButtonExpandedIcon: ImageVector = Icons.Filled.Close,
|
||||
onMainButtonClick: (() -> Unit)? = null,
|
||||
) {
|
||||
var isExpanded by remember { mutableStateOf(false) }
|
||||
|
||||
// 主按钮旋转动画
|
||||
val rotationAngle by animateFloatAsState(
|
||||
targetValue = if (isExpanded) 45f else 0f,
|
||||
animationSpec = tween(
|
||||
durationMillis = animationDurationMs,
|
||||
easing = FastOutSlowInEasing
|
||||
),
|
||||
label = "mainButtonRotation"
|
||||
)
|
||||
|
||||
// 主按钮缩放动画
|
||||
val mainButtonScale by animateFloatAsState(
|
||||
targetValue = if (isExpanded) 1.1f else 1f,
|
||||
animationSpec = tween(
|
||||
durationMillis = animationDurationMs,
|
||||
easing = FastOutSlowInEasing
|
||||
),
|
||||
label = "mainButtonScale"
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = modifier.wrapContentSize(),
|
||||
contentAlignment = Alignment.BottomEnd
|
||||
) {
|
||||
// 子菜单按钮
|
||||
menuItems.forEachIndexed { index, menuItem ->
|
||||
val animatedOffsetY by animateFloatAsState(
|
||||
targetValue = if (isExpanded) {
|
||||
-(buttonSpacing.value * (index + 1))
|
||||
} else {
|
||||
0f
|
||||
},
|
||||
animationSpec = tween(
|
||||
durationMillis = animationDurationMs,
|
||||
delayMillis = if (isExpanded) {
|
||||
index * staggerDelayMs
|
||||
} else {
|
||||
(menuItems.size - index - 1) * staggerDelayMs
|
||||
},
|
||||
easing = FastOutSlowInEasing
|
||||
),
|
||||
label = "fabOffset$index"
|
||||
)
|
||||
|
||||
val animatedScale by animateFloatAsState(
|
||||
targetValue = if (isExpanded) 1f else 0f,
|
||||
animationSpec = tween(
|
||||
durationMillis = animationDurationMs,
|
||||
delayMillis = if (isExpanded) {
|
||||
index * staggerDelayMs + 100
|
||||
} else {
|
||||
(menuItems.size - index - 1) * staggerDelayMs
|
||||
},
|
||||
easing = FastOutSlowInEasing
|
||||
),
|
||||
label = "fabScale$index"
|
||||
)
|
||||
|
||||
val animatedAlpha by animateFloatAsState(
|
||||
targetValue = if (isExpanded) 1f else 0f,
|
||||
animationSpec = tween(
|
||||
durationMillis = animationDurationMs,
|
||||
delayMillis = if (isExpanded) {
|
||||
index * staggerDelayMs + 150
|
||||
} else {
|
||||
(menuItems.size - index - 1) * staggerDelayMs
|
||||
},
|
||||
easing = FastOutSlowInEasing
|
||||
),
|
||||
label = "fabAlpha$index"
|
||||
)
|
||||
|
||||
// 子按钮容器(包含标签)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.offset(y = animatedOffsetY.dp)
|
||||
.scale(animatedScale)
|
||||
.alpha(animatedAlpha),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.End
|
||||
) {
|
||||
// 标签
|
||||
AnimatedVisibility(
|
||||
visible = isExpanded && animatedScale > 0.5f,
|
||||
enter = slideInHorizontally(
|
||||
initialOffsetX = { it / 2 },
|
||||
animationSpec = tween(200)
|
||||
) + fadeIn(animationSpec = tween(200)),
|
||||
exit = slideOutHorizontally(
|
||||
targetOffsetX = { it / 2 },
|
||||
animationSpec = tween(150)
|
||||
) + fadeOut(animationSpec = tween(150))
|
||||
) {
|
||||
Surface(
|
||||
modifier = Modifier.padding(end = 16.dp),
|
||||
shape = MaterialTheme.shapes.small,
|
||||
color = MaterialTheme.colorScheme.inverseSurface,
|
||||
tonalElevation = 6.dp
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(menuItem.labelRes),
|
||||
modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.inverseOnSurface
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 子按钮
|
||||
SmallFloatingActionButton(
|
||||
onClick = {
|
||||
menuItem.onClick()
|
||||
isExpanded = false
|
||||
},
|
||||
modifier = Modifier.size(smallButtonSize),
|
||||
containerColor = if (menuItem.color != Color.Unspecified) {
|
||||
menuItem.color
|
||||
} else {
|
||||
MaterialTheme.colorScheme.secondary
|
||||
},
|
||||
contentColor = if (menuItem.color != Color.Unspecified) {
|
||||
if (menuItem.color == Color.Gray) Color.White
|
||||
else MaterialTheme.colorScheme.onSecondary
|
||||
} else {
|
||||
MaterialTheme.colorScheme.onSecondary
|
||||
},
|
||||
elevation = FloatingActionButtonDefaults.elevation(
|
||||
defaultElevation = 4.dp,
|
||||
pressedElevation = 6.dp
|
||||
)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = menuItem.icon,
|
||||
contentDescription = stringResource(menuItem.labelRes),
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 主按钮
|
||||
FloatingActionButton(
|
||||
onClick = {
|
||||
onMainButtonClick?.invoke()
|
||||
isExpanded = !isExpanded
|
||||
},
|
||||
modifier = Modifier
|
||||
.size(buttonSize)
|
||||
.scale(mainButtonScale),
|
||||
elevation = FloatingActionButtonDefaults.elevation(
|
||||
defaultElevation = 6.dp,
|
||||
pressedElevation = 8.dp,
|
||||
hoveredElevation = 8.dp
|
||||
)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = if (isExpanded) mainButtonExpandedIcon else mainButtonIcon,
|
||||
contentDescription = stringResource(
|
||||
if (isExpanded) R.string.collapse_menu else R.string.expand_menu
|
||||
),
|
||||
modifier = Modifier
|
||||
.size(24.dp)
|
||||
.rotate(if (mainButtonIcon == Icons.Filled.Add) rotationAngle else 0f)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 预设菜单项
|
||||
object FabMenuPresets {
|
||||
fun getScrollMenuItems(
|
||||
onScrollToTop: () -> Unit,
|
||||
onScrollToBottom: () -> Unit
|
||||
) = listOf(
|
||||
FabMenuItem(
|
||||
icon = Icons.Filled.KeyboardArrowUp,
|
||||
labelRes = R.string.scroll_to_top,
|
||||
onClick = onScrollToTop
|
||||
),
|
||||
FabMenuItem(
|
||||
icon = Icons.Filled.KeyboardArrowDown,
|
||||
labelRes = R.string.scroll_to_bottom,
|
||||
onClick = onScrollToBottom
|
||||
)
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun getBatchActionMenuItems(
|
||||
onCancel: () -> Unit,
|
||||
onDeny: () -> Unit,
|
||||
onAllow: () -> Unit,
|
||||
onUnmountModules: () -> Unit,
|
||||
onDisableUnmount: () -> Unit
|
||||
) = listOf(
|
||||
FabMenuItem(
|
||||
icon = Icons.Filled.Close,
|
||||
labelRes = R.string.cancel,
|
||||
color = Color.Gray,
|
||||
onClick = onCancel
|
||||
),
|
||||
FabMenuItem(
|
||||
icon = Icons.Filled.Block,
|
||||
labelRes = R.string.deny_authorization,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
onClick = onDeny
|
||||
),
|
||||
FabMenuItem(
|
||||
icon = Icons.Filled.Check,
|
||||
labelRes = R.string.grant_authorization,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
onClick = onAllow
|
||||
),
|
||||
FabMenuItem(
|
||||
icon = Icons.Filled.FolderOff,
|
||||
labelRes = R.string.unmount_modules,
|
||||
onClick = onUnmountModules
|
||||
),
|
||||
FabMenuItem(
|
||||
icon = Icons.Filled.Folder,
|
||||
labelRes = R.string.disable_unmount,
|
||||
onClick = onDisableUnmount
|
||||
)
|
||||
)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -61,7 +61,6 @@ import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.sukisu.ultra.Natives
|
||||
import com.sukisu.ultra.R
|
||||
import com.sukisu.ultra.ui.MainActivity
|
||||
import com.sukisu.ultra.ui.component.ImageEditorDialog
|
||||
import com.sukisu.ultra.ui.component.KsuIsValid
|
||||
import com.sukisu.ultra.ui.theme.CardConfig.cardElevation
|
||||
|
||||
@@ -304,9 +304,6 @@
|
||||
<string name="tools">工具</string>
|
||||
<!-- String resources used in SuperUser -->
|
||||
<string name="clear">清除</string>
|
||||
<string name="apps_with_root">Root 权限应用</string>
|
||||
<string name="apps_with_custom_profile">自定义配置应用</string>
|
||||
<string name="other_apps">默认配置应用</string>
|
||||
<string name="no_apps_found">未找到应用</string>
|
||||
<string name="selinux_enabled_toast">SELinux 已设置为启用状态</string>
|
||||
<string name="selinux_disabled_toast">SELinux 已设置为禁用状态</string>
|
||||
@@ -353,7 +350,39 @@
|
||||
<string name="language_follow_system">跟随系统</string>
|
||||
<string name="language_changed">语言已更改,重启应用以应用更改</string>
|
||||
<string name="settings_card_dim">卡片暗度调节</string>
|
||||
<!-- Super User Related -->
|
||||
<!-- Flash related -->
|
||||
<string name="error_code">错误代码</string>
|
||||
<string name="check_log">请查看日志</string>
|
||||
<string name="installing_module">正在安装模块 %1$d/%2$d</string>
|
||||
<string name="module_failed_count">%d 个模块安装失败</string>
|
||||
<string name="module_download_error">模块下载失败</string>
|
||||
<string name="kernel_flashing">内核刷写</string>
|
||||
<!-- 分类相关 -->
|
||||
<string name="category_all_apps">全部</string>
|
||||
<string name="category_root_apps">Root</string>
|
||||
<string name="category_custom_apps">自定义</string>
|
||||
<string name="category_default_apps">默认</string>
|
||||
<!-- 排序相关 -->
|
||||
<string name="sort_name_asc">名称升序</string>
|
||||
<string name="sort_name_desc">名称降序</string>
|
||||
<string name="sort_install_time_new">安装时间(新)</string>
|
||||
<string name="sort_install_time_old">安装时间(旧)</string>
|
||||
<string name="sort_size_desc">大小降序</string>
|
||||
<string name="sort_size_asc">大小升序</string>
|
||||
<string name="sort_usage_freq">使用频率</string>
|
||||
<!-- 状态相关 -->
|
||||
<string name="no_apps_in_category">此分类中没有应用</string>
|
||||
<!-- 标签相关 -->
|
||||
<string name="label_root">Root</string>
|
||||
<string name="label_unmount">卸载</string>
|
||||
<string name="label_custom">自定义</string>
|
||||
<!-- FAB菜单相关 -->
|
||||
<string name="deny_authorization">取消授权</string>
|
||||
<string name="grant_authorization">授权</string>
|
||||
<string name="unmount_modules">卸载模块挂载</string>
|
||||
<string name="disable_unmount">禁用卸载模块挂载</string>
|
||||
<string name="expand_menu">展开菜单</string>
|
||||
<string name="collapse_menu">收起菜单</string>
|
||||
<string name="scroll_to_top">顶部</string>
|
||||
<string name="scroll_to_bottom">底部</string>
|
||||
<string name="scroll_to_top_description">滚动到顶部</string>
|
||||
@@ -363,11 +392,7 @@
|
||||
<string name="selected">已选择</string>
|
||||
<string name="select">选择</string>
|
||||
<string name="profile_umount_modules_disable">禁用自定义卸载模块</string>
|
||||
<!-- Flash related -->
|
||||
<string name="error_code">错误代码</string>
|
||||
<string name="check_log">请查看日志</string>
|
||||
<string name="installing_module">正在安装模块 %1$d/%2$d</string>
|
||||
<string name="module_failed_count">%d 个模块安装失败</string>
|
||||
<string name="module_download_error">模块下载失败</string>
|
||||
<string name="kernel_flashing">内核刷写</string>
|
||||
<!-- BottomSheet相关 -->
|
||||
<string name="menu_options">菜单选项</string>
|
||||
<string name="sort_options">排序方式</string>
|
||||
</resources>
|
||||
|
||||
@@ -306,9 +306,6 @@
|
||||
<string name="tools">Tools</string>
|
||||
<!-- String resources used in SuperUser -->
|
||||
<string name="clear">Removals</string>
|
||||
<string name="apps_with_root">Applications with root privileges</string>
|
||||
<string name="apps_with_custom_profile">Applications with customized configurations</string>
|
||||
<string name="other_apps">Applications with unchanged defaults</string>
|
||||
<string name="no_apps_found">Application not found</string>
|
||||
<string name="selinux_enabled_toast">SELinux Enabled</string>
|
||||
<string name="selinux_disabled_toast">SELinux Disabled</string>
|
||||
@@ -355,7 +352,39 @@
|
||||
<string name="language_follow_system">Follow System</string>
|
||||
<string name="language_changed">Language changed, restarting to apply changes</string>
|
||||
<string name="settings_card_dim">Card Darkness Adjustment</string>
|
||||
<!-- Super User Related -->
|
||||
<!-- Flash related -->
|
||||
<string name="error_code">error code</string>
|
||||
<string name="check_log">Please check the log</string>
|
||||
<string name="installing_module">Module being installed %1$d/%2$d</string>
|
||||
<string name="module_failed_count">%d Failed to install a new module</string>
|
||||
<string name="module_download_error">Module download failed</string>
|
||||
<string name="kernel_flashing">Kernel Flashing</string>
|
||||
<!-- 分类相关 -->
|
||||
<string name="category_all_apps">All</string>
|
||||
<string name="category_root_apps">Root</string>
|
||||
<string name="category_custom_apps">Custom</string>
|
||||
<string name="category_default_apps">Default</string>
|
||||
<!-- 排序相关 -->
|
||||
<string name="sort_name_asc">Ascending order of name</string>
|
||||
<string name="sort_name_desc">Name descending</string>
|
||||
<string name="sort_install_time_new">Installation time (new)</string>
|
||||
<string name="sort_install_time_old">Installation time (old)</string>
|
||||
<string name="sort_size_desc">descending order of size</string>
|
||||
<string name="sort_size_asc">ascending order of size</string>
|
||||
<string name="sort_usage_freq">frequency of use</string>
|
||||
<!-- 状态相关 -->
|
||||
<string name="no_apps_in_category">No application in this category</string>
|
||||
<!-- 标签相关 -->
|
||||
<string name="label_root">Root</string>
|
||||
<string name="label_unmount">Unmount</string>
|
||||
<string name="label_custom">Custom</string>
|
||||
<!-- FAB菜单相关 -->
|
||||
<string name="deny_authorization">Delegation of authority</string>
|
||||
<string name="grant_authorization">Authorizations</string>
|
||||
<string name="unmount_modules">Unmounting Module Mounts</string>
|
||||
<string name="disable_unmount">Disable uninstall module mounting</string>
|
||||
<string name="expand_menu">Expand menu</string>
|
||||
<string name="collapse_menu">Put away the menu</string>
|
||||
<string name="scroll_to_top">Top</string>
|
||||
<string name="scroll_to_bottom">Bottom</string>
|
||||
<string name="scroll_to_top_description">Scroll to top</string>
|
||||
@@ -365,11 +394,7 @@
|
||||
<string name="selected">Selected</string>
|
||||
<string name="select">option</string>
|
||||
<string name="profile_umount_modules_disable">Disable custom uninstallation module</string>
|
||||
<!-- Flash related -->
|
||||
<string name="error_code">error code</string>
|
||||
<string name="check_log">Please check the log</string>
|
||||
<string name="installing_module">Module being installed %1$d/%2$d</string>
|
||||
<string name="module_failed_count">%d Failed to install a new module</string>
|
||||
<string name="module_download_error">Module download failed</string>
|
||||
<string name="kernel_flashing">Kernel Flashing</string>
|
||||
<!-- BottomSheet相关 -->
|
||||
<string name="menu_options">Menu Options</string>
|
||||
<string name="sort_options">Sort by</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user