From 1671316a8f4bd3e3091248ccb8c6c977ba3feff6 Mon Sep 17 00:00:00 2001 From: Michelle Rodriguez <29279979+lamprose@users.noreply.github.com> Date: Wed, 25 Dec 2024 19:48:28 +0800 Subject: [PATCH] manager: Add sort options on module's appbar (#2308) module list will be sorted by options when these are enabled: ![83d5cd3d23b8f6c36b52e731f5e21a97](https://github.com/user-attachments/assets/eaadc1a3-21c6-4b73-a55f-206b6faa7d2d) It will be very friendly and convenient when manager has many modules. --- .../me/weishu/kernelsu/ui/screen/Module.kt | 120 +++++++++++++----- .../kernelsu/ui/viewmodel/ModuleViewModel.kt | 12 +- .../src/main/res/values-zh-rCN/strings.xml | 2 + manager/app/src/main/res/values/strings.xml | 2 + 4 files changed, 102 insertions(+), 34 deletions(-) diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Module.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Module.kt index 64001fc7d7e9..8fa84666efc7 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Module.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Module.kt @@ -32,17 +32,22 @@ import androidx.compose.foundation.selection.toggleable import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.outlined.Wysiwyg import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.outlined.PlayArrow import androidx.compose.material.icons.outlined.Download import androidx.compose.material.icons.outlined.Delete import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Checkbox +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ElevatedCard import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExtendedFloatingActionButton import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarDuration @@ -110,9 +115,13 @@ fun ModuleScreen(navigator: DestinationsNavigator) { val viewModel = viewModel() val context = LocalContext.current val snackBarHost = LocalSnackbarHost.current + val scope = rememberCoroutineScope() + val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE) LaunchedEffect(Unit) { if (viewModel.moduleList.isEmpty() || viewModel.isNeedRefresh) { + viewModel.sortEnabledFirst = prefs.getBoolean("module_sort_enabled_first", false) + viewModel.sortActionFirst = prefs.getBoolean("module_sort_action_first", false) viewModel.fetchModuleList() } } @@ -127,6 +136,57 @@ fun ModuleScreen(navigator: DestinationsNavigator) { Scaffold( topBar = { TopAppBar( + actions = { + var showDropdown by remember { mutableStateOf(false) } + + IconButton( + onClick = { showDropdown = true }, + ) { + Icon( + imageVector = Icons.Filled.MoreVert, + contentDescription = stringResource(id = R.string.settings) + ) + + DropdownMenu(expanded = showDropdown, onDismissRequest = { + showDropdown = false + }) { + DropdownMenuItem(text = { + Text(stringResource(R.string.module_sort_action_first)) + }, trailingIcon = { + Checkbox(viewModel.sortActionFirst, null) + }, onClick = { + viewModel.sortActionFirst = + !viewModel.sortActionFirst + prefs.edit() + .putBoolean( + "module_sort_action_first", + viewModel.sortActionFirst + ) + .apply() + scope.launch { + viewModel.fetchModuleList() + } + }) + DropdownMenuItem(text = { + Text(stringResource(R.string.module_sort_enabled_first)) + }, trailingIcon = { + Checkbox(viewModel.sortEnabledFirst, null) + }, onClick = { + viewModel.sortEnabledFirst = + !viewModel.sortEnabledFirst + prefs.edit() + .putBoolean( + "module_sort_enabled_first", + viewModel.sortEnabledFirst + ) + .apply() + scope.launch { + viewModel.fetchModuleList() + } + }) + } + } + }, scrollBehavior = scrollBehavior, title = { Text(stringResource(R.string.module)) }, windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) @@ -493,8 +553,8 @@ fun ModuleItem( onValueChange = { onClick(module) } ) } else { - this - } + this + } } .padding(22.dp, 18.dp, 22.dp, 12.dp) ) { @@ -577,24 +637,24 @@ fun ModuleItem( FilledTonalButton( modifier = Modifier.defaultMinSize(52.dp, 32.dp), onClick = { - navigator.navigate(ExecuteModuleActionScreenDestination(module.id)) - viewModel.markNeedRefresh() + navigator.navigate(ExecuteModuleActionScreenDestination(module.id)) + viewModel.markNeedRefresh() }, contentPadding = ButtonDefaults.TextButtonContentPadding ) { Icon( - modifier = Modifier.size(20.dp), + modifier = Modifier.size(20.dp), imageVector = Icons.Outlined.PlayArrow, contentDescription = null ) - if (!module.hasWebUi && updateUrl.isEmpty()) { + if (!module.hasWebUi && updateUrl.isEmpty()) { Text( - modifier = Modifier.padding(start = 7.dp), + modifier = Modifier.padding(start = 7.dp), text = stringResource(R.string.action), fontFamily = MaterialTheme.typography.labelMedium.fontFamily, fontSize = MaterialTheme.typography.labelMedium.fontSize ) - } + } } Spacer(modifier = Modifier.weight(0.1f, true)) @@ -608,16 +668,16 @@ fun ModuleItem( contentPadding = ButtonDefaults.TextButtonContentPadding ) { Icon( - modifier = Modifier.size(20.dp), + modifier = Modifier.size(20.dp), imageVector = Icons.AutoMirrored.Outlined.Wysiwyg, contentDescription = null ) if (!module.hasActionScript && updateUrl.isEmpty()) { Text( - modifier = Modifier.padding(start = 7.dp), - fontFamily = MaterialTheme.typography.labelMedium.fontFamily, - fontSize = MaterialTheme.typography.labelMedium.fontSize, - text = stringResource(R.string.open) + modifier = Modifier.padding(start = 7.dp), + fontFamily = MaterialTheme.typography.labelMedium.fontFamily, + fontSize = MaterialTheme.typography.labelMedium.fontSize, + text = stringResource(R.string.open) ) } } @@ -632,19 +692,19 @@ fun ModuleItem( shape = ButtonDefaults.textShape, contentPadding = ButtonDefaults.TextButtonContentPadding ) { - Icon( - modifier = Modifier.size(20.dp), - imageVector = Icons.Outlined.Download, - contentDescription = null - ) - if (!module.hasActionScript || !module.hasWebUi) { + Icon( + modifier = Modifier.size(20.dp), + imageVector = Icons.Outlined.Download, + contentDescription = null + ) + if (!module.hasActionScript || !module.hasWebUi) { Text( - modifier = Modifier.padding(start = 7.dp), + modifier = Modifier.padding(start = 7.dp), fontFamily = MaterialTheme.typography.labelMedium.fontFamily, fontSize = MaterialTheme.typography.labelMedium.fontSize, text = stringResource(R.string.module_update) ) - } + } } Spacer(modifier = Modifier.weight(0.1f, true)) @@ -656,19 +716,19 @@ fun ModuleItem( onClick = { onUninstall(module) }, contentPadding = ButtonDefaults.TextButtonContentPadding ) { - Icon( - modifier = Modifier.size(20.dp), - imageVector = Icons.Outlined.Delete, - contentDescription = null - ) - if (!module.hasActionScript && !module.hasWebUi && updateUrl.isEmpty()) { - Text( - modifier = Modifier.padding(start = 7.dp), + Icon( + modifier = Modifier.size(20.dp), + imageVector = Icons.Outlined.Delete, + contentDescription = null + ) + if (!module.hasActionScript && !module.hasWebUi && updateUrl.isEmpty()) { + Text( + modifier = Modifier.padding(start = 7.dp), fontFamily = MaterialTheme.typography.labelMedium.fontFamily, fontSize = MaterialTheme.typography.labelMedium.fontSize, text = stringResource(R.string.uninstall) ) - } + } } } } diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/viewmodel/ModuleViewModel.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/viewmodel/ModuleViewModel.kt index 4533e6e5ffc4..b323db188d79 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/viewmodel/ModuleViewModel.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/viewmodel/ModuleViewModel.kt @@ -14,8 +14,6 @@ import me.weishu.kernelsu.ui.util.listModules import me.weishu.kernelsu.ui.util.overlayFsAvailable import org.json.JSONArray import org.json.JSONObject -import java.text.Collator -import java.util.Locale class ModuleViewModel : ViewModel() { @@ -52,8 +50,14 @@ class ModuleViewModel : ViewModel() { var isOverlayAvailable by mutableStateOf(overlayFsAvailable()) private set + var sortEnabledFirst by mutableStateOf(false) + var sortActionFirst by mutableStateOf(false) val moduleList by derivedStateOf { - val comparator = compareBy(Collator.getInstance(Locale.getDefault()), ModuleInfo::id) + val comparator = + compareBy( + { if (sortEnabledFirst) !it.enabled else 0 }, + { if (sortActionFirst) !it.hasWebUi && !it.hasActionScript else 0 }, + { it.id }) modules.sortedWith(comparator).also { isRefreshing = false } @@ -159,4 +163,4 @@ class ModuleViewModel : ViewModel() { return Triple(zipUrl, version, changelog) } -} +} \ No newline at end of file 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 d56b625a91ff..ed50ce258478 100644 --- a/manager/app/src/main/res/values-zh-rCN/strings.xml +++ b/manager/app/src/main/res/values-zh-rCN/strings.xml @@ -21,6 +21,8 @@ 无法禁用模块: %s 没有安装模块 模块 + 可执行优先 + 已启用优先 卸载 安装 安装 diff --git a/manager/app/src/main/res/values/strings.xml b/manager/app/src/main/res/values/strings.xml index 032815da05a2..3ced15e98107 100644 --- a/manager/app/src/main/res/values/strings.xml +++ b/manager/app/src/main/res/values/strings.xml @@ -25,6 +25,8 @@ Failed to disable module: %s No module installed Module + Sort (Action First) + Sort (Enabled First) Uninstall Install Install