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 8c7e9405..a08e69bb 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 @@ -56,7 +56,6 @@ import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarResult import androidx.compose.material3.Switch import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.pulltorefresh.PullToRefreshBox import androidx.compose.material3.rememberTopAppBarState @@ -67,7 +66,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.produceState import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -95,6 +93,7 @@ import kotlinx.coroutines.withContext import me.weishu.kernelsu.Natives import me.weishu.kernelsu.R import me.weishu.kernelsu.ui.component.ConfirmResult +import me.weishu.kernelsu.ui.component.SearchAppBar import me.weishu.kernelsu.ui.component.rememberConfirmDialog import me.weishu.kernelsu.ui.component.rememberLoadingDialog import me.weishu.kernelsu.ui.util.DownloadListener @@ -139,8 +138,12 @@ fun ModuleScreen(navigator: DestinationsNavigator) { Scaffold( topBar = { - TopAppBar( - actions = { + SearchAppBar( + title = { Text(stringResource(R.string.module)) }, + searchText = viewModel.search, + onSearchTextChange = { viewModel.search = it }, + onClearClick = { viewModel.search = "" }, + dropdownContent = { var showDropdown by remember { mutableStateOf(false) } IconButton( @@ -192,8 +195,6 @@ fun ModuleScreen(navigator: DestinationsNavigator) { } }, scrollBehavior = scrollBehavior, - title = { Text(stringResource(R.string.module)) }, - windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal) ) }, floatingActionButton = { @@ -458,7 +459,6 @@ private fun ModuleList( else -> { items(viewModel.moduleList) { module -> - var isChecked by rememberSaveable(module) { mutableStateOf(module.enabled) } val scope = rememberCoroutineScope() val updatedModule by produceState(initialValue = Triple("", "", "")) { scope.launch(Dispatchers.IO) { @@ -469,7 +469,6 @@ private fun ModuleList( ModuleItem( navigator = navigator, module = module, - isChecked = isChecked, updateUrl = updatedModule.first, onUninstall = { scope.launch { onModuleUninstall(module) } @@ -478,11 +477,10 @@ private fun ModuleList( scope.launch { val success = loadingDialog.withLoading { withContext(Dispatchers.IO) { - toggleModule(module.id, !isChecked) + toggleModule(module.id, !module.enabled) } } if (success) { - isChecked = it viewModel.fetchModuleList() val result = snackBarHost.showSnackbar( @@ -494,7 +492,7 @@ private fun ModuleList( reboot() } } else { - val message = if (isChecked) failedDisable else failedEnable + val message = if (module.enabled) failedDisable else failedEnable snackBarHost.showSnackbar(message.format(module.name)) } } @@ -530,7 +528,6 @@ private fun ModuleList( fun ModuleItem( navigator: DestinationsNavigator, module: ModuleViewModel.ModuleInfo, - isChecked: Boolean, updateUrl: String, onUninstall: (ModuleViewModel.ModuleInfo) -> Unit, onCheckChanged: (Boolean) -> Unit, @@ -550,7 +547,7 @@ fun ModuleItem( .run { if (module.hasWebUi) { toggleable( - value = isChecked, + value = module.enabled, interactionSource = interactionSource, role = Role.Button, indication = indication, @@ -606,7 +603,7 @@ fun ModuleItem( ) { Switch( enabled = !module.update, - checked = isChecked, + checked = module.enabled, onCheckedChange = onCheckChanged, interactionSource = if (!module.hasWebUi) interactionSource else null ) @@ -756,5 +753,5 @@ fun ModuleItemPreview() { hasWebUi = false, hasActionScript = false ) - ModuleItem(EmptyDestinationsNavigator, module, true, "", {}, {}, {}, {}) + ModuleItem(EmptyDestinationsNavigator, module, "", {}, {}, {}, {}) } 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 b323db18..6561aea1 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 @@ -10,10 +10,13 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import me.weishu.kernelsu.ui.util.HanziToPinyin 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() { @@ -46,6 +49,7 @@ class ModuleViewModel : ViewModel() { var isRefreshing by mutableStateOf(false) private set + var search by mutableStateOf("") var isOverlayAvailable by mutableStateOf(overlayFsAvailable()) private set @@ -57,8 +61,11 @@ class ModuleViewModel : ViewModel() { compareBy( { if (sortEnabledFirst) !it.enabled else 0 }, { if (sortActionFirst) !it.hasWebUi && !it.hasActionScript else 0 }, - { it.id }) - modules.sortedWith(comparator).also { + ).thenBy(Collator.getInstance(Locale.getDefault()), ModuleInfo::id) + modules.filter { + it.id.contains(search, true) || it.name.contains(search, true) || HanziToPinyin.getInstance() + .toPinyinString(it.name).contains(search, true) + }.sortedWith(comparator).also { isRefreshing = false } }