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.ramcosta.composedestinations.annotation.RootGraph
|
||||||
import com.sukisu.ultra.Natives
|
import com.sukisu.ultra.Natives
|
||||||
import com.sukisu.ultra.R
|
import com.sukisu.ultra.R
|
||||||
import com.sukisu.ultra.ui.MainActivity
|
|
||||||
import com.sukisu.ultra.ui.component.ImageEditorDialog
|
import com.sukisu.ultra.ui.component.ImageEditorDialog
|
||||||
import com.sukisu.ultra.ui.component.KsuIsValid
|
import com.sukisu.ultra.ui.component.KsuIsValid
|
||||||
import com.sukisu.ultra.ui.theme.CardConfig.cardElevation
|
import com.sukisu.ultra.ui.theme.CardConfig.cardElevation
|
||||||
|
|||||||
@@ -304,9 +304,6 @@
|
|||||||
<string name="tools">工具</string>
|
<string name="tools">工具</string>
|
||||||
<!-- String resources used in SuperUser -->
|
<!-- String resources used in SuperUser -->
|
||||||
<string name="clear">清除</string>
|
<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="no_apps_found">未找到应用</string>
|
||||||
<string name="selinux_enabled_toast">SELinux 已设置为启用状态</string>
|
<string name="selinux_enabled_toast">SELinux 已设置为启用状态</string>
|
||||||
<string name="selinux_disabled_toast">SELinux 已设置为禁用状态</string>
|
<string name="selinux_disabled_toast">SELinux 已设置为禁用状态</string>
|
||||||
@@ -353,7 +350,39 @@
|
|||||||
<string name="language_follow_system">跟随系统</string>
|
<string name="language_follow_system">跟随系统</string>
|
||||||
<string name="language_changed">语言已更改,重启应用以应用更改</string>
|
<string name="language_changed">语言已更改,重启应用以应用更改</string>
|
||||||
<string name="settings_card_dim">卡片暗度调节</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_top">顶部</string>
|
||||||
<string name="scroll_to_bottom">底部</string>
|
<string name="scroll_to_bottom">底部</string>
|
||||||
<string name="scroll_to_top_description">滚动到顶部</string>
|
<string name="scroll_to_top_description">滚动到顶部</string>
|
||||||
@@ -363,11 +392,7 @@
|
|||||||
<string name="selected">已选择</string>
|
<string name="selected">已选择</string>
|
||||||
<string name="select">选择</string>
|
<string name="select">选择</string>
|
||||||
<string name="profile_umount_modules_disable">禁用自定义卸载模块</string>
|
<string name="profile_umount_modules_disable">禁用自定义卸载模块</string>
|
||||||
<!-- Flash related -->
|
<!-- BottomSheet相关 -->
|
||||||
<string name="error_code">错误代码</string>
|
<string name="menu_options">菜单选项</string>
|
||||||
<string name="check_log">请查看日志</string>
|
<string name="sort_options">排序方式</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>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -306,9 +306,6 @@
|
|||||||
<string name="tools">Tools</string>
|
<string name="tools">Tools</string>
|
||||||
<!-- String resources used in SuperUser -->
|
<!-- String resources used in SuperUser -->
|
||||||
<string name="clear">Removals</string>
|
<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="no_apps_found">Application not found</string>
|
||||||
<string name="selinux_enabled_toast">SELinux Enabled</string>
|
<string name="selinux_enabled_toast">SELinux Enabled</string>
|
||||||
<string name="selinux_disabled_toast">SELinux Disabled</string>
|
<string name="selinux_disabled_toast">SELinux Disabled</string>
|
||||||
@@ -355,7 +352,39 @@
|
|||||||
<string name="language_follow_system">Follow System</string>
|
<string name="language_follow_system">Follow System</string>
|
||||||
<string name="language_changed">Language changed, restarting to apply changes</string>
|
<string name="language_changed">Language changed, restarting to apply changes</string>
|
||||||
<string name="settings_card_dim">Card Darkness Adjustment</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_top">Top</string>
|
||||||
<string name="scroll_to_bottom">Bottom</string>
|
<string name="scroll_to_bottom">Bottom</string>
|
||||||
<string name="scroll_to_top_description">Scroll to top</string>
|
<string name="scroll_to_top_description">Scroll to top</string>
|
||||||
@@ -365,11 +394,7 @@
|
|||||||
<string name="selected">Selected</string>
|
<string name="selected">Selected</string>
|
||||||
<string name="select">option</string>
|
<string name="select">option</string>
|
||||||
<string name="profile_umount_modules_disable">Disable custom uninstallation module</string>
|
<string name="profile_umount_modules_disable">Disable custom uninstallation module</string>
|
||||||
<!-- Flash related -->
|
<!-- BottomSheet相关 -->
|
||||||
<string name="error_code">error code</string>
|
<string name="menu_options">Menu Options</string>
|
||||||
<string name="check_log">Please check the log</string>
|
<string name="sort_options">Sort by</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>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Reference in New Issue
Block a user