From 72361ab8bfc3f2e01fb49d7378a76df77474b3c6 Mon Sep 17 00:00:00 2001
From: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
Date: Fri, 16 May 2025 16:00:51 +0800
Subject: [PATCH] manager: Modify the batch selection ui on the superuser page
- Add more convenient buttons for it
---
.../com/sukisu/ultra/ui/screen/SuperUser.kt | 457 ++++++++++++++----
.../com/sukisu/ultra/ui/screen/Template.kt | 1 +
.../ultra/ui/viewmodel/SuperUserViewModel.kt | 37 ++
.../src/main/res/values-zh-rCN/strings.xml | 10 +
manager/app/src/main/res/values/strings.xml | 10 +
5 files changed, 431 insertions(+), 84 deletions(-)
diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/SuperUser.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/SuperUser.kt
index 540e418e..ff35254b 100644
--- a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/SuperUser.kt
+++ b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/SuperUser.kt
@@ -1,13 +1,15 @@
-package com.sukisu.ultra.ui.screen
-
import androidx.compose.animation.*
+import androidx.compose.animation.core.*
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.gestures.detectTapGestures
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.icons.Icons
@@ -40,7 +42,6 @@ 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
@@ -68,7 +69,8 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
LaunchedEffect(viewModel.search) {
if (viewModel.search.isEmpty()) {
- listState.scrollToItem(0)
+ // 取消自动滚动到顶部的行为
+ // listState.scrollToItem(0)
}
}
@@ -165,77 +167,309 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
},
snackbarHost = { SnackbarHost(snackBarHostState) },
contentWindowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal),
- bottomBar = {
- AnimatedVisibility(
- visible = viewModel.showBatchActions && viewModel.selectedApps.isNotEmpty(),
- enter = slideInVertically(initialOffsetY = { it }),
- exit = slideOutVertically(targetOffsetY = { it })
+ floatingActionButton = {
+ // 侧边悬浮按钮集合
+ Column(
+ verticalArrangement = Arrangement.spacedBy(8.dp),
+ horizontalAlignment = Alignment.End
) {
- Surface(
- color = MaterialTheme.colorScheme.surfaceContainerHighest,
- tonalElevation = cardElevation,
- shadowElevation = cardElevation
- ) {
- Row(
- modifier = Modifier
- .fillMaxWidth()
- .padding(16.dp),
- horizontalArrangement = Arrangement.SpaceEvenly
+ // 批量操作相关按钮
+ // 只有在批量模式且有选中应用时才显示批量操作按钮
+ if (viewModel.showBatchActions && viewModel.selectedApps.isNotEmpty()) {
+ // 取消按钮
+ val cancelInteractionSource = remember { MutableInteractionSource() }
+ val isCancelPressed by cancelInteractionSource.collectIsPressedAsState()
+
+ FloatingActionButton(
+ onClick = {
+ viewModel.selectedApps = emptySet()
+ viewModel.showBatchActions = false
+ },
+ modifier = Modifier.size(if (isCancelPressed) 56.dp else 40.dp),
+ containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
+ contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
+ shape = CircleShape,
+ interactionSource = cancelInteractionSource,
+ elevation = FloatingActionButtonDefaults.elevation(4.dp, 6.dp)
) {
- OutlinedButton(
- onClick = {
- viewModel.selectedApps = emptySet()
- viewModel.showBatchActions = false
- },
- colors = ButtonDefaults.outlinedButtonColors(
- contentColor = MaterialTheme.colorScheme.primary
- )
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Center
) {
Icon(
imageVector = Icons.Filled.Close,
- contentDescription = null,
- modifier = Modifier.size(16.dp)
+ contentDescription = stringResource(android.R.string.cancel),
+ modifier = Modifier.size(24.dp)
)
- Spacer(modifier = Modifier.width(6.dp))
- Text(stringResource(android.R.string.cancel))
+ AnimatedVisibility(
+ visible = isCancelPressed,
+ enter = expandHorizontally() + fadeIn(),
+ exit = shrinkHorizontally() + fadeOut()
+ ) {
+ Text(
+ stringResource(android.R.string.cancel),
+ modifier = Modifier.padding(end = 4.dp),
+ style = MaterialTheme.typography.labelMedium
+ )
+ }
}
+ }
- Button(
- onClick = {
- scope.launch {
- viewModel.updateBatchPermissions(true)
- }
- },
- colors = ButtonDefaults.buttonColors(
- containerColor = MaterialTheme.colorScheme.primary
- )
- ) {
- Icon(
- imageVector = Icons.Filled.Check,
- contentDescription = null,
- modifier = Modifier.size(16.dp)
- )
- Spacer(modifier = Modifier.width(6.dp))
- Text(stringResource(R.string.batch_authorization))
- }
+ // 取消授权按钮
+ val unauthorizeInteractionSource = remember { MutableInteractionSource() }
+ val isUnauthorizePressed by unauthorizeInteractionSource.collectIsPressedAsState()
- Button(
- onClick = {
- scope.launch {
- viewModel.updateBatchPermissions(false)
- }
- },
- colors = ButtonDefaults.buttonColors(
- containerColor = MaterialTheme.colorScheme.error
- )
+ FloatingActionButton(
+ onClick = {
+ scope.launch {
+ viewModel.updateBatchPermissions(false)
+ }
+ },
+ modifier = Modifier.size(if (isUnauthorizePressed) 56.dp else 40.dp),
+ containerColor = MaterialTheme.colorScheme.errorContainer,
+ contentColor = MaterialTheme.colorScheme.onErrorContainer,
+ shape = CircleShape,
+ interactionSource = unauthorizeInteractionSource,
+ elevation = FloatingActionButtonDefaults.elevation(4.dp, 6.dp)
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Center
) {
Icon(
imageVector = Icons.Filled.Block,
- contentDescription = null,
- modifier = Modifier.size(16.dp)
+ contentDescription = stringResource(R.string.batch_cancel_authorization),
+ modifier = Modifier.size(24.dp)
+ )
+ AnimatedVisibility(
+ visible = isUnauthorizePressed,
+ enter = expandHorizontally() + fadeIn(),
+ exit = shrinkHorizontally() + fadeOut()
+ ) {
+ Text(
+ stringResource(R.string.batch_cancel_authorization),
+ modifier = Modifier.padding(end = 4.dp),
+ style = MaterialTheme.typography.labelMedium
+ )
+ }
+ }
+ }
+
+ // 授权按钮
+ val authorizeInteractionSource = remember { MutableInteractionSource() }
+ val isAuthorizePressed by authorizeInteractionSource.collectIsPressedAsState()
+
+ FloatingActionButton(
+ onClick = {
+ scope.launch {
+ viewModel.updateBatchPermissions(true)
+ }
+ },
+ modifier = Modifier.size(if (isAuthorizePressed) 56.dp else 40.dp),
+ containerColor = MaterialTheme.colorScheme.primaryContainer,
+ contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
+ shape = CircleShape,
+ interactionSource = authorizeInteractionSource,
+ elevation = FloatingActionButtonDefaults.elevation(4.dp, 6.dp)
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Center
+ ) {
+ Icon(
+ imageVector = Icons.Filled.Check,
+ contentDescription = stringResource(R.string.batch_authorization),
+ modifier = Modifier.size(24.dp)
+ )
+ AnimatedVisibility(
+ visible = isAuthorizePressed,
+ enter = expandHorizontally() + fadeIn(),
+ exit = shrinkHorizontally() + fadeOut()
+ ) {
+ Text(
+ stringResource(R.string.batch_authorization),
+ modifier = Modifier.padding(end = 4.dp),
+ style = MaterialTheme.typography.labelMedium
+ )
+ }
+ }
+ }
+
+ // 添加分隔
+ Spacer(modifier = Modifier.height(8.dp))
+ }
+
+ if (viewModel.showBatchActions && viewModel.selectedApps.isNotEmpty()) {
+
+ // 在批量操作按钮组中添加卸载模块的按钮
+ // 卸载模块启用按钮
+ val umountEnableInteractionSource = remember { MutableInteractionSource() }
+ val isUmountEnablePressed by umountEnableInteractionSource.collectIsPressedAsState()
+
+ FloatingActionButton(
+ onClick = {
+ scope.launch {
+ viewModel.updateBatchPermissions(
+ allowSu = false, // 不改变ROOT权限状态
+ umountModules = true // 启用卸载模块
+ )
+ }
+ },
+ modifier = Modifier.size(if (isUmountEnablePressed) 56.dp else 40.dp),
+ containerColor = MaterialTheme.colorScheme.tertiaryContainer,
+ contentColor = MaterialTheme.colorScheme.onTertiaryContainer,
+ shape = CircleShape,
+ interactionSource = umountEnableInteractionSource,
+ elevation = FloatingActionButtonDefaults.elevation(4.dp, 6.dp)
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Center
+ ) {
+ Icon(
+ imageVector = Icons.Filled.FolderOff,
+ contentDescription = stringResource(R.string.profile_umount_modules),
+ modifier = Modifier.size(24.dp)
+ )
+ AnimatedVisibility(
+ visible = isUmountEnablePressed,
+ enter = expandHorizontally() + fadeIn(),
+ exit = shrinkHorizontally() + fadeOut()
+ ) {
+ Text(
+ stringResource(R.string.profile_umount_modules),
+ modifier = Modifier.padding(end = 4.dp),
+ style = MaterialTheme.typography.labelMedium
+ )
+ }
+ }
+ }
+
+ // 卸载模块禁用按钮
+ val umountDisableInteractionSource = remember { MutableInteractionSource() }
+ val isUmountDisablePressed by umountDisableInteractionSource.collectIsPressedAsState()
+
+ FloatingActionButton(
+ onClick = {
+ scope.launch {
+ viewModel.updateBatchPermissions(
+ allowSu = false, // 不改变ROOT权限状态
+ umountModules = false // 禁用卸载模块
+ )
+ }
+ },
+ modifier = Modifier.size(if (isUmountDisablePressed) 56.dp else 40.dp),
+ containerColor = MaterialTheme.colorScheme.tertiaryContainer,
+ contentColor = MaterialTheme.colorScheme.onTertiaryContainer,
+ shape = CircleShape,
+ interactionSource = umountDisableInteractionSource,
+ elevation = FloatingActionButtonDefaults.elevation(4.dp, 6.dp)
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Center
+ ) {
+ Icon(
+ imageVector = Icons.Filled.Folder,
+ contentDescription = stringResource(R.string.profile_umount_modules_disable),
+ modifier = Modifier.size(24.dp)
+ )
+ AnimatedVisibility(
+ visible = isUmountDisablePressed,
+ enter = expandHorizontally() + fadeIn(),
+ exit = shrinkHorizontally() + fadeOut()
+ ) {
+ Text(
+ stringResource(R.string.profile_umount_modules_disable),
+ modifier = Modifier.padding(end = 4.dp),
+ style = MaterialTheme.typography.labelMedium
+ )
+ }
+ }
+ // 添加分隔
+ Spacer(modifier = Modifier.height(8.dp))
+ }
+ }
+
+ // 向上导航按钮
+ val topBtnInteractionSource = remember { MutableInteractionSource() }
+ val isTopBtnPressed by topBtnInteractionSource.collectIsPressedAsState()
+
+ FloatingActionButton(
+ onClick = {
+ scope.launch {
+ listState.animateScrollToItem(0)
+ }
+ },
+ modifier = Modifier.size(if (isTopBtnPressed) 56.dp else 40.dp),
+ containerColor = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 1f),
+ contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
+ shape = CircleShape,
+ interactionSource = topBtnInteractionSource,
+ elevation = FloatingActionButtonDefaults.elevation(4.dp, 6.dp)
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Center
+ ) {
+ Icon(
+ imageVector = Icons.Filled.KeyboardArrowUp,
+ contentDescription = stringResource(R.string.scroll_to_top_description),
+ modifier = Modifier.size(24.dp)
+ )
+ AnimatedVisibility(
+ visible = isTopBtnPressed,
+ enter = expandHorizontally() + fadeIn(),
+ exit = shrinkHorizontally() + fadeOut()
+ ) {
+ Text(
+ stringResource(R.string.scroll_to_top),
+ modifier = Modifier.padding(end = 4.dp),
+ style = MaterialTheme.typography.labelMedium
+ )
+ }
+ }
+ }
+
+ // 向下导航按钮
+ val bottomBtnInteractionSource = remember { MutableInteractionSource() }
+ val isBottomBtnPressed by bottomBtnInteractionSource.collectIsPressedAsState()
+
+ FloatingActionButton(
+ onClick = {
+ scope.launch {
+ val lastIndex = viewModel.appList.size - 1
+ if (lastIndex >= 0) {
+ listState.animateScrollToItem(lastIndex)
+ }
+ }
+ },
+ modifier = Modifier.size(if (isBottomBtnPressed) 56.dp else 40.dp),
+ containerColor = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 1f),
+ contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
+ shape = CircleShape,
+ interactionSource = bottomBtnInteractionSource,
+ elevation = FloatingActionButtonDefaults.elevation(4.dp, 6.dp)
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Center
+ ) {
+ Icon(
+ imageVector = Icons.Filled.KeyboardArrowDown,
+ contentDescription = stringResource(R.string.scroll_to_bottom_description),
+ modifier = Modifier.size(24.dp)
+ )
+ AnimatedVisibility(
+ visible = isBottomBtnPressed,
+ enter = expandHorizontally() + fadeIn(),
+ exit = shrinkHorizontally() + fadeOut()
+ ) {
+ Text(
+ stringResource(R.string.scroll_to_bottom),
+ modifier = Modifier.padding(end = 4.dp),
+ style = MaterialTheme.typography.labelMedium
)
- Spacer(modifier = Modifier.width(6.dp))
- Text(stringResource(R.string.batch_cancel_authorization))
}
}
}
@@ -256,7 +490,7 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
.nestedScroll(scrollBehavior.nestedScrollConnection),
contentPadding = PaddingValues(
top = 8.dp,
- bottom = if (viewModel.showBatchActions && viewModel.selectedApps.isNotEmpty()) 88.dp else 16.dp
+ bottom = 16.dp
)
) {
// 获取分组后的应用列表
@@ -280,7 +514,10 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
val profile = Natives.getAppProfile(app.packageName, app.uid)
val updatedProfile = profile.copy(allowSu = allowSu)
if (Natives.setAppProfile(updatedProfile)) {
- viewModel.fetchAppList()
+ // 不重新获取应用列表,避免滚动位置重置
+ // viewModel.fetchAppList()
+ // 仅更新当前应用的配置
+ viewModel.updateAppProfileLocally(app.packageName, updatedProfile)
}
}
},
@@ -319,7 +556,10 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
val profile = Natives.getAppProfile(app.packageName, app.uid)
val updatedProfile = profile.copy(allowSu = allowSu)
if (Natives.setAppProfile(updatedProfile)) {
- viewModel.fetchAppList()
+ // 不重新获取应用列表,避免滚动位置重置
+ // viewModel.fetchAppList()
+ // 仅更新当前应用的配置
+ viewModel.updateAppProfileLocally(app.packageName, updatedProfile)
}
}
},
@@ -358,7 +598,10 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
val profile = Natives.getAppProfile(app.packageName, app.uid)
val updatedProfile = profile.copy(allowSu = allowSu)
if (Natives.setAppProfile(updatedProfile)) {
- viewModel.fetchAppList()
+ // 不重新获取应用列表,避免滚动位置重置
+ // viewModel.fetchAppList()
+ // 仅更新当前应用的配置
+ viewModel.updateAppProfileLocally(app.packageName, updatedProfile)
}
}
},
@@ -539,27 +782,73 @@ private fun AppItem(
}
if (!viewModel.showBatchActions) {
- Switch(
- checked = app.allowSu,
- onCheckedChange = onSwitchChange,
- colors = SwitchDefaults.colors(
- checkedThumbColor = MaterialTheme.colorScheme.onPrimary,
- checkedTrackColor = MaterialTheme.colorScheme.primary,
- checkedIconColor = MaterialTheme.colorScheme.primary,
- uncheckedThumbColor = MaterialTheme.colorScheme.outline,
- uncheckedTrackColor = MaterialTheme.colorScheme.surfaceVariant,
- uncheckedIconColor = MaterialTheme.colorScheme.surfaceVariant
+ // 开关交互源
+ val switchInteractionSource = remember { MutableInteractionSource() }
+ val isSwitchPressed by switchInteractionSource.collectIsPressedAsState()
+
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.End
+ ) {
+ AnimatedVisibility(
+ visible = isSwitchPressed,
+ enter = expandHorizontally() + fadeIn(),
+ exit = shrinkHorizontally() + fadeOut()
+ ) {
+ Text(
+ text = if (app.allowSu) stringResource(R.string.authorized) else stringResource(R.string.unauthorized),
+ style = MaterialTheme.typography.labelMedium,
+ color = if (app.allowSu) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.outline,
+ modifier = Modifier.padding(end = 4.dp)
+ )
+ }
+
+ Switch(
+ checked = app.allowSu,
+ onCheckedChange = onSwitchChange,
+ interactionSource = switchInteractionSource,
+ colors = SwitchDefaults.colors(
+ checkedThumbColor = MaterialTheme.colorScheme.onPrimary,
+ checkedTrackColor = MaterialTheme.colorScheme.primary,
+ checkedIconColor = MaterialTheme.colorScheme.primary,
+ uncheckedThumbColor = MaterialTheme.colorScheme.outline,
+ uncheckedTrackColor = MaterialTheme.colorScheme.surfaceVariant,
+ uncheckedIconColor = MaterialTheme.colorScheme.surfaceVariant
+ )
)
- )
+ }
} else {
- Checkbox(
- checked = isSelected,
- onCheckedChange = { onToggleSelection() },
- colors = CheckboxDefaults.colors(
- checkedColor = MaterialTheme.colorScheme.primary,
- uncheckedColor = MaterialTheme.colorScheme.outline
+ // 复选框交互源
+ val checkboxInteractionSource = remember { MutableInteractionSource() }
+ val isCheckboxPressed by checkboxInteractionSource.collectIsPressedAsState()
+
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.End
+ ) {
+ AnimatedVisibility(
+ visible = isCheckboxPressed,
+ enter = expandHorizontally() + fadeIn(),
+ exit = shrinkHorizontally() + fadeOut()
+ ) {
+ Text(
+ text = if (isSelected) stringResource(R.string.selected) else stringResource(R.string.select),
+ style = MaterialTheme.typography.labelMedium,
+ color = if (isSelected) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.outline,
+ modifier = Modifier.padding(end = 4.dp)
+ )
+ }
+
+ Checkbox(
+ checked = isSelected,
+ onCheckedChange = { onToggleSelection() },
+ interactionSource = checkboxInteractionSource,
+ colors = CheckboxDefaults.colors(
+ checkedColor = MaterialTheme.colorScheme.primary,
+ uncheckedColor = MaterialTheme.colorScheme.outline
+ )
)
- )
+ }
}
}
}
diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Template.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Template.kt
index 0c89211d..2fcec85f 100644
--- a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Template.kt
+++ b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Template.kt
@@ -1,5 +1,6 @@
package com.sukisu.ultra.ui.screen
+import LabelText
import android.content.ClipData
import android.content.ClipboardManager
import android.widget.Toast
diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/viewmodel/SuperUserViewModel.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/viewmodel/SuperUserViewModel.kt
index f1750b9e..df3a2e5b 100644
--- a/manager/app/src/main/java/com/sukisu/ultra/ui/viewmodel/SuperUserViewModel.kt
+++ b/manager/app/src/main/java/com/sukisu/ultra/ui/viewmodel/SuperUserViewModel.kt
@@ -139,6 +139,43 @@ class SuperUserViewModel : ViewModel() {
fetchAppList() // 刷新列表以显示最新状态
}
+ // 批量更新权限和umount模块设置
+ suspend fun updateBatchPermissions(allowSu: Boolean, umountModules: Boolean? = null) {
+ selectedApps.forEach { packageName ->
+ val app = apps.find { it.packageName == packageName }
+ app?.let {
+ val profile = Natives.getAppProfile(packageName, it.uid)
+ val updatedProfile = profile.copy(
+ allowSu = allowSu,
+ umountModules = umountModules ?: profile.umountModules,
+ nonRootUseDefault = false
+ )
+ if (Natives.setAppProfile(updatedProfile)) {
+ apps = apps.map { app ->
+ if (app.packageName == packageName) {
+ app.copy(profile = updatedProfile)
+ } else {
+ app
+ }
+ }
+ }
+ }
+ }
+ clearSelection()
+ showBatchActions = false // 批量操作完成后退出批量模式
+ fetchAppList() // 刷新列表以显示最新状态
+ }
+
+ // 仅更新本地应用配置,避免重新获取整个列表导致滚动位置重置
+ fun updateAppProfileLocally(packageName: String, updatedProfile: Natives.Profile) {
+ apps = apps.map { app ->
+ if (app.packageName == packageName) {
+ app.copy(profile = updatedProfile)
+ } else {
+ app
+ }
+ }
+ }
suspend fun fetchAppList() {
isRefreshing = true
diff --git a/manager/app/src/main/res/values-zh-rCN/strings.xml b/manager/app/src/main/res/values-zh-rCN/strings.xml
index 312ca0ed..05cd0bc6 100644
--- a/manager/app/src/main/res/values-zh-rCN/strings.xml
+++ b/manager/app/src/main/res/values-zh-rCN/strings.xml
@@ -347,4 +347,14 @@
跟随系统
语言已更改,重启应用以应用更改
卡片暗度调节
+
+ 顶部
+ 底部
+ 滚动到顶部
+ 滚动到底部
+ 已授权
+ 未授权
+ 已选择
+ 选择
+ 禁用自定义卸载模块
diff --git a/manager/app/src/main/res/values/strings.xml b/manager/app/src/main/res/values/strings.xml
index 3d3f7ab6..9cbc19fc 100644
--- a/manager/app/src/main/res/values/strings.xml
+++ b/manager/app/src/main/res/values/strings.xml
@@ -351,4 +351,14 @@
Follow System
Language changed, restarting to apply changes
Card Darkness Adjustment
+
+ Top
+ Bottom
+ Scroll to top
+ Scroll to the bottom
+ authorized
+ unauthorized
+ Selected
+ option
+ Disable custom uninstallation module