You've already forked KernelSU-Next
mirror of
https://github.com/KernelSU-Next/KernelSU-Next.git
synced 2025-08-27 23:46:34 +00:00
manager: support search module list (#2331)
This commit is contained in:
@@ -55,7 +55,6 @@ import androidx.compose.material3.SnackbarResult
|
|||||||
import androidx.compose.material3.Switch
|
import androidx.compose.material3.Switch
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.TopAppBar
|
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||||
import androidx.compose.material3.rememberTopAppBarState
|
import androidx.compose.material3.rememberTopAppBarState
|
||||||
@@ -96,6 +95,7 @@ import com.rifsxd.ksunext.R
|
|||||||
import com.rifsxd.ksunext.ui.component.ConfirmResult
|
import com.rifsxd.ksunext.ui.component.ConfirmResult
|
||||||
import com.rifsxd.ksunext.ui.component.rememberConfirmDialog
|
import com.rifsxd.ksunext.ui.component.rememberConfirmDialog
|
||||||
import com.rifsxd.ksunext.ui.component.rememberLoadingDialog
|
import com.rifsxd.ksunext.ui.component.rememberLoadingDialog
|
||||||
|
import com.rifsxd.ksunext.ui.component.SearchAppBar
|
||||||
import com.rifsxd.ksunext.ui.util.*
|
import com.rifsxd.ksunext.ui.util.*
|
||||||
import com.rifsxd.ksunext.ui.util.DownloadListener
|
import com.rifsxd.ksunext.ui.util.DownloadListener
|
||||||
import com.rifsxd.ksunext.ui.util.LocalSnackbarHost
|
import com.rifsxd.ksunext.ui.util.LocalSnackbarHost
|
||||||
@@ -145,8 +145,12 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
|||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
SearchAppBar(
|
||||||
actions = {
|
title = { Text(stringResource(R.string.module)) },
|
||||||
|
searchText = viewModel.search,
|
||||||
|
onSearchTextChange = { viewModel.search = it },
|
||||||
|
onClearClick = { viewModel.search = "" },
|
||||||
|
dropdownContent = {
|
||||||
var showDropdown by remember { mutableStateOf(false) }
|
var showDropdown by remember { mutableStateOf(false) }
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = { showDropdown = true },
|
onClick = { showDropdown = true },
|
||||||
@@ -203,9 +207,7 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
scrollBehavior = scrollBehavior,
|
scrollBehavior = scrollBehavior
|
||||||
title = { Text(stringResource(R.string.module)) },
|
|
||||||
windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal)
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
@@ -535,7 +537,6 @@ private fun ModuleList(
|
|||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
items(viewModel.moduleList) { module ->
|
items(viewModel.moduleList) { module ->
|
||||||
var isChecked by rememberSaveable(module) { mutableStateOf(module.enabled) }
|
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val updatedModule by produceState(initialValue = Triple("", "", "")) {
|
val updatedModule by produceState(initialValue = Triple("", "", "")) {
|
||||||
scope.launch(Dispatchers.IO) {
|
scope.launch(Dispatchers.IO) {
|
||||||
@@ -546,7 +547,6 @@ private fun ModuleList(
|
|||||||
ModuleItem(
|
ModuleItem(
|
||||||
navigator = navigator,
|
navigator = navigator,
|
||||||
module = module,
|
module = module,
|
||||||
isChecked = isChecked,
|
|
||||||
updateUrl = updatedModule.first,
|
updateUrl = updatedModule.first,
|
||||||
onUninstall = {
|
onUninstall = {
|
||||||
scope.launch { onModuleUninstall(module) }
|
scope.launch { onModuleUninstall(module) }
|
||||||
@@ -558,11 +558,10 @@ private fun ModuleList(
|
|||||||
scope.launch {
|
scope.launch {
|
||||||
val success = loadingDialog.withLoading {
|
val success = loadingDialog.withLoading {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
toggleModule(module.dirId, !isChecked)
|
toggleModule(module.dirId, !module.enabled)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (success) {
|
if (success) {
|
||||||
isChecked = it
|
|
||||||
viewModel.fetchModuleList()
|
viewModel.fetchModuleList()
|
||||||
|
|
||||||
val result = snackBarHost.showSnackbar(
|
val result = snackBarHost.showSnackbar(
|
||||||
@@ -574,7 +573,7 @@ private fun ModuleList(
|
|||||||
reboot()
|
reboot()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val message = if (isChecked) failedDisable else failedEnable
|
val message = if (module.enabled) failedDisable else failedEnable
|
||||||
snackBarHost.showSnackbar(message.format(module.name))
|
snackBarHost.showSnackbar(message.format(module.name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -610,7 +609,6 @@ private fun ModuleList(
|
|||||||
fun ModuleItem(
|
fun ModuleItem(
|
||||||
navigator: DestinationsNavigator,
|
navigator: DestinationsNavigator,
|
||||||
module: ModuleViewModel.ModuleInfo,
|
module: ModuleViewModel.ModuleInfo,
|
||||||
isChecked: Boolean,
|
|
||||||
updateUrl: String,
|
updateUrl: String,
|
||||||
onUninstall: (ModuleViewModel.ModuleInfo) -> Unit,
|
onUninstall: (ModuleViewModel.ModuleInfo) -> Unit,
|
||||||
onRestore: (ModuleViewModel.ModuleInfo) -> Unit,
|
onRestore: (ModuleViewModel.ModuleInfo) -> Unit,
|
||||||
@@ -631,7 +629,7 @@ fun ModuleItem(
|
|||||||
.run {
|
.run {
|
||||||
if (module.hasWebUi) {
|
if (module.hasWebUi) {
|
||||||
toggleable(
|
toggleable(
|
||||||
value = isChecked,
|
value = module.enabled,
|
||||||
interactionSource = interactionSource,
|
interactionSource = interactionSource,
|
||||||
role = Role.Button,
|
role = Role.Button,
|
||||||
indication = indication,
|
indication = indication,
|
||||||
@@ -687,7 +685,7 @@ fun ModuleItem(
|
|||||||
) {
|
) {
|
||||||
Switch(
|
Switch(
|
||||||
enabled = !module.update,
|
enabled = !module.update,
|
||||||
checked = isChecked,
|
checked = module.enabled,
|
||||||
onCheckedChange = onCheckChanged,
|
onCheckedChange = onCheckChanged,
|
||||||
interactionSource = if (!module.hasWebUi) interactionSource else null
|
interactionSource = if (!module.hasWebUi) interactionSource else null
|
||||||
)
|
)
|
||||||
@@ -826,7 +824,7 @@ fun ModuleItem(
|
|||||||
imageVector = Icons.Outlined.Delete,
|
imageVector = Icons.Outlined.Delete,
|
||||||
contentDescription = null
|
contentDescription = null
|
||||||
)
|
)
|
||||||
if (!module.hasActionScript && !module.hasWebUi && updateUrl.isEmpty()) {
|
if (!module.hasActionScript && !module.hasWebUi && updateUrl.isEmpty()) {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.padding(start = 7.dp),
|
modifier = Modifier.padding(start = 7.dp),
|
||||||
fontFamily = MaterialTheme.typography.labelMedium.fontFamily,
|
fontFamily = MaterialTheme.typography.labelMedium.fontFamily,
|
||||||
@@ -859,5 +857,5 @@ fun ModuleItemPreview() {
|
|||||||
hasActionScript = false,
|
hasActionScript = false,
|
||||||
dirId = "dirId"
|
dirId = "dirId"
|
||||||
)
|
)
|
||||||
ModuleItem(EmptyDestinationsNavigator, module, true, "", {}, {}, {}, {}, {})
|
ModuleItem(EmptyDestinationsNavigator, module, "", {}, {}, {}, {}, {})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import java.text.Collator
|
||||||
|
import java.util.Locale
|
||||||
|
import com.rifsxd.ksunext.ui.util.HanziToPinyin
|
||||||
import com.rifsxd.ksunext.ui.util.listModules
|
import com.rifsxd.ksunext.ui.util.listModules
|
||||||
import com.rifsxd.ksunext.ui.util.overlayFsAvailable
|
import com.rifsxd.ksunext.ui.util.overlayFsAvailable
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
@@ -51,6 +54,8 @@ class ModuleViewModel : ViewModel() {
|
|||||||
var isRefreshing by mutableStateOf(false)
|
var isRefreshing by mutableStateOf(false)
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
var search by mutableStateOf("")
|
||||||
|
|
||||||
var sortAToZ by mutableStateOf(false)
|
var sortAToZ by mutableStateOf(false)
|
||||||
var sortZToA by mutableStateOf(false)
|
var sortZToA by mutableStateOf(false)
|
||||||
|
|
||||||
@@ -59,8 +64,13 @@ class ModuleViewModel : ViewModel() {
|
|||||||
sortAToZ -> compareBy<ModuleInfo> { it.name.lowercase() }
|
sortAToZ -> compareBy<ModuleInfo> { it.name.lowercase() }
|
||||||
sortZToA -> compareByDescending<ModuleInfo> { it.name.lowercase() }
|
sortZToA -> compareByDescending<ModuleInfo> { it.name.lowercase() }
|
||||||
else -> compareBy<ModuleInfo> { it.dirId }
|
else -> compareBy<ModuleInfo> { it.dirId }
|
||||||
}
|
}.thenBy(Collator.getInstance(Locale.getDefault()), ModuleInfo::id)
|
||||||
modules.sortedWith(comparator).also {
|
|
||||||
|
modules.filter {
|
||||||
|
it.id.contains(search, ignoreCase = true) ||
|
||||||
|
it.name.contains(search, ignoreCase = true) ||
|
||||||
|
HanziToPinyin.getInstance().toPinyinString(it.name).contains(search, ignoreCase = true)
|
||||||
|
}.sortedWith(comparator).also {
|
||||||
isRefreshing = false
|
isRefreshing = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -83,7 +93,7 @@ class ModuleViewModel : ViewModel() {
|
|||||||
|
|
||||||
kotlin.runCatching {
|
kotlin.runCatching {
|
||||||
isOverlayAvailable = overlayFsAvailable()
|
isOverlayAvailable = overlayFsAvailable()
|
||||||
|
|
||||||
val result = listModules()
|
val result = listModules()
|
||||||
|
|
||||||
Log.i(TAG, "result: $result")
|
Log.i(TAG, "result: $result")
|
||||||
@@ -167,4 +177,4 @@ class ModuleViewModel : ViewModel() {
|
|||||||
|
|
||||||
return Triple(zipUrl, version, changelog)
|
return Triple(zipUrl, version, changelog)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user