Step 5-1: Move the KPM interface to the settings
- Avoid multiple page re-rendering - Add hook type information - Clean up code
This commit is contained in:
@@ -335,11 +335,6 @@ NativeBridge(getUserName, jstring, jint uid) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if KPM is enabled
|
|
||||||
NativeBridgeNP(isKPMEnabled, jboolean) {
|
|
||||||
return is_KPM_enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get HOOK type
|
// Get HOOK type
|
||||||
NativeBridgeNP(getHookType, jstring) {
|
NativeBridgeNP(getHookType, jstring) {
|
||||||
char hook_type[32] = { 0 };
|
char hook_type[32] = { 0 };
|
||||||
|
|||||||
@@ -259,14 +259,6 @@ void get_full_version(char* buff) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_KPM_enable(void) {
|
|
||||||
struct ksu_enable_kpm_cmd cmd = {};
|
|
||||||
if (ksuctl(KSU_IOCTL_ENABLE_KPM, &cmd) == 0 && cmd.enabled) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return legacy_is_KPM_enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
void get_hook_type(char *buff) {
|
void get_hook_type(char *buff) {
|
||||||
struct ksu_hook_type_cmd cmd = {0};
|
struct ksu_hook_type_cmd cmd = {0};
|
||||||
if (ksuctl(KSU_IOCTL_HOOK_TYPE, &cmd) == 0) {
|
if (ksuctl(KSU_IOCTL_HOOK_TYPE, &cmd) == 0) {
|
||||||
|
|||||||
@@ -229,10 +229,6 @@ struct ksu_hook_type_cmd {
|
|||||||
char hook_type[32]; // Output: hook type string
|
char hook_type[32]; // Output: hook type string
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ksu_enable_kpm_cmd {
|
|
||||||
uint8_t enabled; // Output: true if KPM is enabled
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ksu_dynamic_manager_cmd {
|
struct ksu_dynamic_manager_cmd {
|
||||||
struct dynamic_manager_user_config config; // Input/Output: dynamic manager config
|
struct dynamic_manager_user_config config; // Input/Output: dynamic manager config
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -88,12 +88,6 @@ bool legacy_is_su_enabled() {
|
|||||||
return enabled;
|
return enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool legacy_is_KPM_enable() {
|
|
||||||
int enabled = false;
|
|
||||||
ksuctl(CMD_ENABLE_KPM, &enabled, NULL);
|
|
||||||
return enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool legacy_get_hook_type(char* hook_type, size_t size) {
|
bool legacy_get_hook_type(char* hook_type, size_t size) {
|
||||||
if (hook_type == NULL || size == 0) {
|
if (hook_type == NULL || size == 0) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -121,8 +121,6 @@ object Natives {
|
|||||||
*/
|
*/
|
||||||
external fun isSuLogEnabled(): Boolean
|
external fun isSuLogEnabled(): Boolean
|
||||||
external fun setSuLogEnabled(enabled: Boolean): Boolean
|
external fun setSuLogEnabled(enabled: Boolean): Boolean
|
||||||
|
|
||||||
external fun isKPMEnabled(): Boolean
|
|
||||||
external fun getHookType(): String
|
external fun getHookType(): String
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.sukisu.ultra.ui
|
package com.sukisu.ultra.ui
|
||||||
|
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
@@ -31,6 +30,7 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.navigation.NavBackStackEntry
|
import androidx.navigation.NavBackStackEntry
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import com.ramcosta.composedestinations.DestinationsNavHost
|
import com.ramcosta.composedestinations.DestinationsNavHost
|
||||||
@@ -47,13 +47,11 @@ import kotlinx.coroutines.launch
|
|||||||
import com.sukisu.ultra.Natives
|
import com.sukisu.ultra.Natives
|
||||||
import com.sukisu.ultra.ui.component.BottomBar
|
import com.sukisu.ultra.ui.component.BottomBar
|
||||||
import com.sukisu.ultra.ui.screen.HomePager
|
import com.sukisu.ultra.ui.screen.HomePager
|
||||||
import com.sukisu.ultra.ui.screen.KpmScreen
|
|
||||||
import com.sukisu.ultra.ui.screen.ModulePager
|
import com.sukisu.ultra.ui.screen.ModulePager
|
||||||
import com.sukisu.ultra.ui.screen.SettingPager
|
import com.sukisu.ultra.ui.screen.SettingPager
|
||||||
import com.sukisu.ultra.ui.screen.SuperUserPager
|
import com.sukisu.ultra.ui.screen.SuperUserPager
|
||||||
import com.sukisu.ultra.ui.theme.KernelSUTheme
|
import com.sukisu.ultra.ui.theme.KernelSUTheme
|
||||||
import com.sukisu.ultra.ui.util.install
|
import com.sukisu.ultra.ui.util.install
|
||||||
import com.sukisu.ultra.ui.util.rememberKpmAvailable
|
|
||||||
import top.yukonga.miuix.kmp.basic.Scaffold
|
import top.yukonga.miuix.kmp.basic.Scaffold
|
||||||
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
||||||
|
|
||||||
@@ -94,6 +92,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
android.graphics.Color.TRANSPARENT
|
android.graphics.Color.TRANSPARENT
|
||||||
) { darkMode },
|
) { darkMode },
|
||||||
)
|
)
|
||||||
|
|
||||||
val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
|
val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
|
||||||
when (key) {
|
when (key) {
|
||||||
"color_mode" -> colorMode = prefs.getInt("color_mode", 0)
|
"color_mode" -> colorMode = prefs.getInt("color_mode", 0)
|
||||||
@@ -161,11 +160,7 @@ val LocalHandlePageChange = compositionLocalOf<(Int) -> Unit> { error("No handle
|
|||||||
fun MainScreen(navController: DestinationsNavigator) {
|
fun MainScreen(navController: DestinationsNavigator) {
|
||||||
val activity = LocalActivity.current
|
val activity = LocalActivity.current
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
val pagerState = rememberPagerState(initialPage = 0, pageCount = { 4 })
|
||||||
val isKpmAvailable = rememberKpmAvailable()
|
|
||||||
val pageCount = if (isKpmAvailable) 5 else 4
|
|
||||||
|
|
||||||
val pagerState = rememberPagerState(initialPage = 0, pageCount = { pageCount })
|
|
||||||
val hazeState = remember { HazeState() }
|
val hazeState = remember { HazeState() }
|
||||||
val hazeStyle = HazeStyle(
|
val hazeStyle = HazeStyle(
|
||||||
backgroundColor = MiuixTheme.colorScheme.background,
|
backgroundColor = MiuixTheme.colorScheme.background,
|
||||||
@@ -193,7 +188,7 @@ fun MainScreen(navController: DestinationsNavigator) {
|
|||||||
) {
|
) {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
bottomBar = {
|
bottomBar = {
|
||||||
BottomBar(hazeState, hazeStyle, isKpmAvailable)
|
BottomBar(hazeState, hazeStyle)
|
||||||
},
|
},
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
HorizontalPager(
|
HorizontalPager(
|
||||||
@@ -202,17 +197,6 @@ fun MainScreen(navController: DestinationsNavigator) {
|
|||||||
beyondViewportPageCount = 2,
|
beyondViewportPageCount = 2,
|
||||||
userScrollEnabled = false
|
userScrollEnabled = false
|
||||||
) {
|
) {
|
||||||
when {
|
|
||||||
isKpmAvailable -> {
|
|
||||||
when (it) {
|
|
||||||
0 -> HomePager(pagerState, navController, innerPadding.calculateBottomPadding())
|
|
||||||
1 -> KpmScreen(bottomInnerPadding = innerPadding.calculateBottomPadding())
|
|
||||||
2 -> SuperUserPager(navController, innerPadding.calculateBottomPadding())
|
|
||||||
3 -> ModulePager(navController, innerPadding.calculateBottomPadding())
|
|
||||||
4 -> SettingPager(navController, innerPadding.calculateBottomPadding())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
when (it) {
|
when (it) {
|
||||||
0 -> HomePager(pagerState, navController, innerPadding.calculateBottomPadding())
|
0 -> HomePager(pagerState, navController, innerPadding.calculateBottomPadding())
|
||||||
1 -> SuperUserPager(navController, innerPadding.calculateBottomPadding())
|
1 -> SuperUserPager(navController, innerPadding.calculateBottomPadding())
|
||||||
@@ -222,6 +206,4 @@ fun MainScreen(navController: DestinationsNavigator) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,6 @@ package com.sukisu.ultra.ui.component
|
|||||||
|
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.rounded.Code
|
|
||||||
import androidx.compose.material.icons.rounded.Cottage
|
import androidx.compose.material.icons.rounded.Cottage
|
||||||
import androidx.compose.material.icons.rounded.Extension
|
import androidx.compose.material.icons.rounded.Extension
|
||||||
import androidx.compose.material.icons.rounded.Security
|
import androidx.compose.material.icons.rounded.Security
|
||||||
@@ -28,8 +27,7 @@ import top.yukonga.miuix.kmp.basic.NavigationItem
|
|||||||
@Composable
|
@Composable
|
||||||
fun BottomBar(
|
fun BottomBar(
|
||||||
hazeState: HazeState,
|
hazeState: HazeState,
|
||||||
hazeStyle: HazeStyle,
|
hazeStyle: HazeStyle
|
||||||
isKpmAvailable: Boolean = false
|
|
||||||
) {
|
) {
|
||||||
val isManager = Natives.isManager
|
val isManager = Natives.isManager
|
||||||
val fullFeatured = isManager && !Natives.requireNewKernel() && rootAvailable()
|
val fullFeatured = isManager && !Natives.requireNewKernel() && rootAvailable()
|
||||||
@@ -39,25 +37,13 @@ fun BottomBar(
|
|||||||
|
|
||||||
if (!fullFeatured) return
|
if (!fullFeatured) return
|
||||||
|
|
||||||
val destinations = if (isKpmAvailable) {
|
val item = BottomBarDestination.entries.mapIndexed { index, destination ->
|
||||||
BottomBarDestination.entries
|
|
||||||
} else {
|
|
||||||
BottomBarDestination.entries.filter { it != BottomBarDestination.KPM }
|
|
||||||
}
|
|
||||||
|
|
||||||
val item = destinations.mapIndexed { index, destination ->
|
|
||||||
NavigationItem(
|
NavigationItem(
|
||||||
label = stringResource(destination.label),
|
label = stringResource(destination.label),
|
||||||
icon = destination.icon,
|
icon = destination.icon,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val bottomBarIndex = if (!isKpmAvailable) {
|
|
||||||
page.coerceIn(0, item.size - 1)
|
|
||||||
} else {
|
|
||||||
page.coerceIn(0, item.size - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
NavigationBar(
|
NavigationBar(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.hazeEffect(hazeState) {
|
.hazeEffect(hazeState) {
|
||||||
@@ -67,10 +53,8 @@ fun BottomBar(
|
|||||||
},
|
},
|
||||||
color = Color.Transparent,
|
color = Color.Transparent,
|
||||||
items = item,
|
items = item,
|
||||||
selected = bottomBarIndex,
|
selected = page,
|
||||||
onClick = { index ->
|
onClick = handlePageChange
|
||||||
handlePageChange(index)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +63,6 @@ enum class BottomBarDestination(
|
|||||||
val icon: ImageVector,
|
val icon: ImageVector,
|
||||||
) {
|
) {
|
||||||
Home(R.string.home, Icons.Rounded.Cottage),
|
Home(R.string.home, Icons.Rounded.Cottage),
|
||||||
KPM(R.string.kpm_title, Icons.Rounded.Code),
|
|
||||||
SuperUser(R.string.superuser, Icons.Rounded.Security),
|
SuperUser(R.string.superuser, Icons.Rounded.Security),
|
||||||
Module(R.string.module, Icons.Rounded.Extension),
|
Module(R.string.module, Icons.Rounded.Extension),
|
||||||
Setting(R.string.settings, Icons.Rounded.Settings)
|
Setting(R.string.settings, Icons.Rounded.Settings)
|
||||||
|
|||||||
@@ -37,11 +37,14 @@ import androidx.compose.material.icons.rounded.CheckCircleOutline
|
|||||||
import androidx.compose.material.icons.rounded.ErrorOutline
|
import androidx.compose.material.icons.rounded.ErrorOutline
|
||||||
import androidx.compose.material.icons.rounded.Link
|
import androidx.compose.material.icons.rounded.Link
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.produceState
|
import androidx.compose.runtime.produceState
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
@@ -150,8 +153,6 @@ fun HomePager(
|
|||||||
if (kernelVersion.isGKI()) Natives.isLkmMode else null
|
if (kernelVersion.isGKI()) Natives.isLkmMode else null
|
||||||
}
|
}
|
||||||
|
|
||||||
val isKpmAvailable = rememberKpmAvailable()
|
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.padding(vertical = 12.dp),
|
modifier = Modifier.padding(vertical = 12.dp),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
@@ -179,12 +180,12 @@ fun HomePager(
|
|||||||
},
|
},
|
||||||
onClickSuperuser = {
|
onClickSuperuser = {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
pagerState.animateScrollToPage(getSuperuserPageIndex(isKpmAvailable))
|
pagerState.animateScrollToPage(1)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onclickModule = {
|
onclickModule = {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
pagerState.animateScrollToPage(getModulePageIndex(isKpmAvailable))
|
pagerState.animateScrollToPage(2)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
themeMode = themeMode
|
themeMode = themeMode
|
||||||
@@ -609,6 +610,19 @@ fun DonateCard() {
|
|||||||
private fun InfoCard() {
|
private fun InfoCard() {
|
||||||
val manualHookText = stringResource(R.string.manual_hook)
|
val manualHookText = stringResource(R.string.manual_hook)
|
||||||
val inlineHookText = stringResource(R.string.inline_hook)
|
val inlineHookText = stringResource(R.string.inline_hook)
|
||||||
|
val TracepointHookText = stringResource(R.string.tracepoint_hook)
|
||||||
|
val unknownHookText = stringResource(R.string.selinux_status_unknown)
|
||||||
|
val susfsInfo = rememberSusfsInfo(manualHookText, inlineHookText)
|
||||||
|
val isSusfsSupported = susfsInfo.status == SusfsStatus.Supported
|
||||||
|
val hookTypeLabel = remember(manualHookText, inlineHookText, TracepointHookText) {
|
||||||
|
val localized = when (val rawType = Natives.getHookType()) {
|
||||||
|
"Manual" -> manualHookText
|
||||||
|
"Tracepoint" -> TracepointHookText
|
||||||
|
else -> rawType
|
||||||
|
}
|
||||||
|
localized.ifBlank { unknownHookText }
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun InfoText(
|
fun InfoText(
|
||||||
title: String,
|
title: String,
|
||||||
@@ -632,23 +646,6 @@ private fun InfoCard() {
|
|||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val uname = Os.uname()
|
val uname = Os.uname()
|
||||||
val managerVersion = getManagerVersion(context)
|
val managerVersion = getManagerVersion(context)
|
||||||
val susfsPair by produceState(initialValue = "" to "") {
|
|
||||||
value = withContext(Dispatchers.IO) {
|
|
||||||
val rawFeature = getSuSFSFeatures()
|
|
||||||
val status = if (rawFeature.isNotEmpty() && !rawFeature.startsWith("[-]")) "Supported" else rawFeature
|
|
||||||
if (status == "Supported") {
|
|
||||||
val version = getSuSFSVersion()
|
|
||||||
val hook = when (Natives.getHookType()) {
|
|
||||||
"Manual" -> "($manualHookText)"
|
|
||||||
"Inline" -> "($inlineHookText)"
|
|
||||||
else -> "(${Natives.getHookType()})"
|
|
||||||
}
|
|
||||||
status to "$version $hook".trim()
|
|
||||||
} else {
|
|
||||||
"" to ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Card {
|
Card {
|
||||||
Column(
|
Column(
|
||||||
@@ -689,27 +686,26 @@ private fun InfoCard() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InfoText(
|
if (isSusfsSupported) {
|
||||||
title = stringResource(R.string.home_fingerprint),
|
|
||||||
content = Build.FINGERPRINT
|
|
||||||
)
|
|
||||||
if (susfsPair.first == "Supported" && susfsPair.second.isNotEmpty()) {
|
|
||||||
InfoText(
|
|
||||||
title = stringResource(R.string.home_selinux_status),
|
|
||||||
content = getSELinuxStatus(),
|
|
||||||
)
|
|
||||||
InfoText(
|
InfoText(
|
||||||
title = stringResource(R.string.home_susfs_version),
|
title = stringResource(R.string.home_susfs_version),
|
||||||
content = susfsPair.second,
|
content = susfsInfo.detail
|
||||||
bottomPadding = 0.dp
|
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
InfoText(
|
InfoText(
|
||||||
title = stringResource(R.string.home_selinux_status),
|
title = stringResource(R.string.hook_type),
|
||||||
content = getSELinuxStatus(),
|
content = hookTypeLabel
|
||||||
bottomPadding = 0.dp
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
InfoText(
|
||||||
|
title = stringResource(R.string.home_selinux_status),
|
||||||
|
content = getSELinuxStatus(),
|
||||||
|
)
|
||||||
|
InfoText(
|
||||||
|
title = stringResource(R.string.home_fingerprint),
|
||||||
|
content = Build.FINGERPRINT,
|
||||||
|
bottomPadding = 0.dp
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -720,10 +716,52 @@ fun getManagerVersion(context: Context): Pair<String, Long> {
|
|||||||
return Pair(packageInfo.versionName!!, versionCode)
|
return Pair(packageInfo.versionName!!, versionCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSuperuserPageIndex(isKpmAvailable: Boolean): Int {
|
private enum class SusfsStatus {
|
||||||
return if (isKpmAvailable) 2 else 1
|
Idle, Loading, Supported, Unsupported, Error
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getModulePageIndex(isKpmAvailable: Boolean): Int {
|
private data class SusfsInfoState(
|
||||||
return if (isKpmAvailable) 3 else 2
|
val status: SusfsStatus = SusfsStatus.Idle,
|
||||||
|
val detail: String = "",
|
||||||
|
)
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun rememberSusfsInfo(
|
||||||
|
manualHookLabel: String,
|
||||||
|
inlineHookLabel: String,
|
||||||
|
): SusfsInfoState {
|
||||||
|
var susfsInfo by remember { mutableStateOf(SusfsInfoState(status = SusfsStatus.Loading)) }
|
||||||
|
|
||||||
|
LaunchedEffect(manualHookLabel, inlineHookLabel) {
|
||||||
|
val info = withContext(Dispatchers.IO) {
|
||||||
|
runCatching {
|
||||||
|
val rawFeature = getSuSFSFeatures()
|
||||||
|
val supported = rawFeature.isNotEmpty() && !rawFeature.startsWith("[-]")
|
||||||
|
if (supported) {
|
||||||
|
val version = getSuSFSVersion().trim()
|
||||||
|
val hookLabel = when (val type = Natives.getHookType()) {
|
||||||
|
"Manual" -> manualHookLabel
|
||||||
|
"Inline" -> inlineHookLabel
|
||||||
|
else -> type
|
||||||
|
}.takeIf { it.isNotBlank() }?.let { "($it)" }.orEmpty()
|
||||||
|
SusfsInfoState(
|
||||||
|
status = SusfsStatus.Supported,
|
||||||
|
detail = listOf(version, hookLabel).filter { it.isNotBlank() }.joinToString(" ")
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
SusfsInfoState(
|
||||||
|
status = SusfsStatus.Unsupported,
|
||||||
|
detail = rawFeature
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}.getOrElse {
|
||||||
|
SusfsInfoState(status = SusfsStatus.Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (susfsInfo != info) {
|
||||||
|
susfsInfo = info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return susfsInfo
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import androidx.compose.material.icons.rounded.Palette
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.rounded.Adb
|
import androidx.compose.material.icons.rounded.Adb
|
||||||
import androidx.compose.material.icons.rounded.BugReport
|
import androidx.compose.material.icons.rounded.BugReport
|
||||||
|
import androidx.compose.material.icons.rounded.Code
|
||||||
import androidx.compose.material.icons.rounded.ContactPage
|
import androidx.compose.material.icons.rounded.ContactPage
|
||||||
import androidx.compose.material.icons.rounded.Delete
|
import androidx.compose.material.icons.rounded.Delete
|
||||||
import androidx.compose.material.icons.rounded.DeleteForever
|
import androidx.compose.material.icons.rounded.DeleteForever
|
||||||
@@ -49,6 +50,7 @@ import com.ramcosta.composedestinations.annotation.RootGraph
|
|||||||
import com.ramcosta.composedestinations.generated.destinations.AboutScreenDestination
|
import com.ramcosta.composedestinations.generated.destinations.AboutScreenDestination
|
||||||
import com.ramcosta.composedestinations.generated.destinations.AppProfileTemplateScreenDestination
|
import com.ramcosta.composedestinations.generated.destinations.AppProfileTemplateScreenDestination
|
||||||
import com.ramcosta.composedestinations.generated.destinations.LogViewerDestination
|
import com.ramcosta.composedestinations.generated.destinations.LogViewerDestination
|
||||||
|
import com.ramcosta.composedestinations.generated.destinations.KpmScreenDestination
|
||||||
import com.ramcosta.composedestinations.generated.destinations.PersonalizationDestination
|
import com.ramcosta.composedestinations.generated.destinations.PersonalizationDestination
|
||||||
import com.ramcosta.composedestinations.generated.destinations.ToolsDestination
|
import com.ramcosta.composedestinations.generated.destinations.ToolsDestination
|
||||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||||
@@ -64,6 +66,7 @@ import com.sukisu.ultra.ui.component.SendLogDialog
|
|||||||
import com.sukisu.ultra.ui.component.UninstallDialog
|
import com.sukisu.ultra.ui.component.UninstallDialog
|
||||||
import com.sukisu.ultra.ui.component.rememberLoadingDialog
|
import com.sukisu.ultra.ui.component.rememberLoadingDialog
|
||||||
import com.sukisu.ultra.ui.util.execKsud
|
import com.sukisu.ultra.ui.util.execKsud
|
||||||
|
import com.sukisu.ultra.ui.util.rememberKpmAvailable
|
||||||
import top.yukonga.miuix.kmp.basic.Card
|
import top.yukonga.miuix.kmp.basic.Card
|
||||||
import top.yukonga.miuix.kmp.basic.Icon
|
import top.yukonga.miuix.kmp.basic.Icon
|
||||||
import top.yukonga.miuix.kmp.basic.MiuixScrollBehavior
|
import top.yukonga.miuix.kmp.basic.MiuixScrollBehavior
|
||||||
@@ -94,6 +97,8 @@ fun SettingPager(
|
|||||||
tint = HazeTint(colorScheme.surface.copy(0.8f))
|
tint = HazeTint(colorScheme.surface.copy(0.8f))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val isKpmAvailable = rememberKpmAvailable()
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
@@ -265,6 +270,33 @@ fun SettingPager(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isKpmAvailable) {
|
||||||
|
Card(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(top = 12.dp)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
) {
|
||||||
|
val kpmTitle = stringResource(id = R.string.kpm_title)
|
||||||
|
SuperArrow(
|
||||||
|
title = kpmTitle,
|
||||||
|
summary = stringResource(id = R.string.settings_kpm_summary),
|
||||||
|
leftAction = {
|
||||||
|
Icon(
|
||||||
|
Icons.Rounded.Code,
|
||||||
|
modifier = Modifier.padding(end = 16.dp),
|
||||||
|
contentDescription = kpmTitle,
|
||||||
|
tint = colorScheme.onBackground
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onClick = {
|
||||||
|
navigator.navigate(KpmScreenDestination) {
|
||||||
|
launchSingleTop = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
KsuIsValid {
|
KsuIsValid {
|
||||||
Card(
|
Card(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|||||||
@@ -12,7 +12,10 @@ import android.system.Os
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.produceState
|
import androidx.compose.runtime.produceState
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import com.topjohnwu.superuser.CallbackList
|
import com.topjohnwu.superuser.CallbackList
|
||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
import com.topjohnwu.superuser.ShellUtils
|
import com.topjohnwu.superuser.ShellUtils
|
||||||
@@ -727,14 +730,13 @@ fun applyUmountConfigToKernel(): Boolean {
|
|||||||
// 检查 KPM 版本是否可用
|
// 检查 KPM 版本是否可用
|
||||||
@Composable
|
@Composable
|
||||||
fun rememberKpmAvailable(): Boolean {
|
fun rememberKpmAvailable(): Boolean {
|
||||||
val kpmVersion by produceState(initialValue = "") {
|
var cachedVersion by rememberSaveable { mutableStateOf("") }
|
||||||
value = withContext(Dispatchers.IO) {
|
val kpmVersion by produceState(initialValue = cachedVersion) {
|
||||||
try {
|
val result = withContext(Dispatchers.IO) {
|
||||||
getKpmVersion()
|
runCatching { getKpmVersion() }.getOrElse { "" }
|
||||||
} catch (_: Exception) {
|
|
||||||
""
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
cachedVersion = result
|
||||||
|
value = result
|
||||||
}
|
}
|
||||||
return kpmVersion.isNotEmpty() && !kpmVersion.contains("Error", ignoreCase = true)
|
return kpmVersion.isNotEmpty() && !kpmVersion.contains("Error", ignoreCase = true)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -178,9 +178,8 @@
|
|||||||
<string name="color_pink">粉色</string>
|
<string name="color_pink">粉色</string>
|
||||||
<string name="color_brown">棕色</string>
|
<string name="color_brown">棕色</string>
|
||||||
<!-- Customize -->
|
<!-- Customize -->
|
||||||
|
<string name="hook_type">钩子类型</string>
|
||||||
<string name="home_susfs_version">SuSFS 版本</string>
|
<string name="home_susfs_version">SuSFS 版本</string>
|
||||||
<string name="manual_hook">Manual Hook</string>
|
|
||||||
<string name="inline_hook">Inline Hook</string>
|
|
||||||
<string name="multi_manager_list">活跃管理器</string>
|
<string name="multi_manager_list">活跃管理器</string>
|
||||||
<string name="default_signature">SukiSU</string>
|
<string name="default_signature">SukiSU</string>
|
||||||
<string name="dynamic_managerature">Dynamic</string>
|
<string name="dynamic_managerature">Dynamic</string>
|
||||||
@@ -320,7 +319,7 @@
|
|||||||
<string name="horizon_kernel">AnyKernel3 内核</string>
|
<string name="horizon_kernel">AnyKernel3 内核</string>
|
||||||
<string name="horizon_kernel_summary">刷入AnyKernel3格式的内核zip包</string>
|
<string name="horizon_kernel_summary">刷入AnyKernel3格式的内核zip包</string>
|
||||||
<!-- kpm -->
|
<!-- kpm -->
|
||||||
<string name="kpm_title">KPM</string>
|
<string name="settings_kpm_summary">使用 KPM 管理内核模块</string>
|
||||||
<string name="kpm_empty">当前没有安装内核模块</string>
|
<string name="kpm_empty">当前没有安装内核模块</string>
|
||||||
<string name="kpm_version">版本</string>
|
<string name="kpm_version">版本</string>
|
||||||
<string name="kpm_author">作者</string>
|
<string name="kpm_author">作者</string>
|
||||||
|
|||||||
@@ -180,9 +180,11 @@
|
|||||||
<string name="color_pink">Pink</string>
|
<string name="color_pink">Pink</string>
|
||||||
<string name="color_brown">Brown</string>
|
<string name="color_brown">Brown</string>
|
||||||
<!-- Customize -->
|
<!-- Customize -->
|
||||||
<string name="home_susfs_version">SuSFS Version</string>
|
<string name="hook_type">Hook Type</string>
|
||||||
<string name="manual_hook">Manual Hook</string>
|
<string name="manual_hook">Manual Hook</string>
|
||||||
<string name="inline_hook">Inline Hook</string>
|
<string name="inline_hook">Inline Hook</string>
|
||||||
|
<string name="tracepoint_hook">Tracepoint Hook</string>
|
||||||
|
<string name="home_susfs_version">SuSFS Version</string>
|
||||||
<string name="multi_manager_list">Active Manager</string>
|
<string name="multi_manager_list">Active Manager</string>
|
||||||
<string name="default_signature">SukiSU</string>
|
<string name="default_signature">SukiSU</string>
|
||||||
<string name="dynamic_managerature">Dynamic</string>
|
<string name="dynamic_managerature">Dynamic</string>
|
||||||
@@ -325,6 +327,7 @@
|
|||||||
<string name="horizon_kernel_summary">Flash AnyKernel3 format kernel zip</string>
|
<string name="horizon_kernel_summary">Flash AnyKernel3 format kernel zip</string>
|
||||||
<!-- kpm -->
|
<!-- kpm -->
|
||||||
<string name="kpm_title">KPM</string>
|
<string name="kpm_title">KPM</string>
|
||||||
|
<string name="settings_kpm_summary">Manage kernel modules with KPM</string>
|
||||||
<string name="kpm_empty">No installed kernel modules at this time</string>
|
<string name="kpm_empty">No installed kernel modules at this time</string>
|
||||||
<string name="kpm_version">Version</string>
|
<string name="kpm_version">Version</string>
|
||||||
<string name="kpm_author">Author</string>
|
<string name="kpm_author">Author</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user