diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Install.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Install.kt
index 603a7aac..7d45c2b5 100644
--- a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Install.kt
+++ b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Install.kt
@@ -1,8 +1,10 @@
package com.sukisu.ultra.ui.screen
import android.app.Activity
+import android.content.Context
import android.content.Intent
import android.net.Uri
+import android.provider.OpenableColumns
import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
@@ -211,7 +213,17 @@ fun InstallScreen(
) {
if (it.resultCode == Activity.RESULT_OK) {
it.data?.data?.let { uri ->
- lkmSelection = LkmSelection.LkmUri(uri)
+ val isKo = isKoFile(context, uri)
+ if (isKo) {
+ lkmSelection = LkmSelection.LkmUri(uri)
+ } else {
+ lkmSelection = LkmSelection.KmiNone
+ Toast.makeText(
+ context,
+ context.getString(R.string.install_only_support_ko_file),
+ Toast.LENGTH_SHORT
+ ).show()
+ }
}
}
}
@@ -228,7 +240,6 @@ fun InstallScreen(
topBar = {
TopBar(
onBack = { navigator.popBackStack() },
- onLkmUpload = onLkmUpload,
scrollBehavior = scrollBehavior
)
},
@@ -268,32 +279,39 @@ fun InstallScreen(
.fillMaxWidth()
.padding(16.dp)
) {
- (lkmSelection as? LkmSelection.LkmUri)?.let {
- ElevatedCard(
- colors = getCardColors(MaterialTheme.colorScheme.surfaceVariant),
- elevation = getCardElevation(),
+ ElevatedCard(
+ colors = getCardColors(MaterialTheme.colorScheme.surfaceVariant),
+ elevation = getCardElevation(),
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(bottom = 12.dp),
+ ) {
+ ListItem(
+ headlineContent = {
+ Text(stringResource(id = R.string.install_upload_lkm_file))
+ },
+ supportingContent = {
+ (lkmSelection as? LkmSelection.LkmUri)?.let {
+ Text(
+ stringResource(
+ id = R.string.selected_lkm,
+ it.uri.lastPathSegment ?: "(file)"
+ )
+ )
+ }
+ },
+ leadingContent = {
+ Icon(
+ Icons.AutoMirrored.Filled.Input,
+ contentDescription = null
+ )
+ },
modifier = Modifier
.fillMaxWidth()
- .padding(bottom = 12.dp)
- .clip(MaterialTheme.shapes.medium)
- .shadow(
- elevation = cardElevation,
- shape = MaterialTheme.shapes.medium,
- spotColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.1f)
- )
- ) {
- Text(
- text = stringResource(
- id = R.string.selected_lkm,
- it.uri.lastPathSegment ?: "(file)"
- ),
- style = MaterialTheme.typography.bodyMedium,
- modifier = Modifier.padding(16.dp)
- )
- }
+ .clickable { onLkmUpload() }
+ )
}
-
- (installMethod as? InstallMethod.HorizonKernel)?.let { method ->
+ (installMethod as? InstallMethod.HorizonKernel)?.let { method ->
if (method.slot != null) {
ElevatedCard(
colors = getCardColors(MaterialTheme.colorScheme.surfaceVariant),
@@ -952,7 +970,6 @@ fun rememberSelectKmiDialog(onSelected: (String?) -> Unit): DialogHandle {
@Composable
private fun TopBar(
onBack: () -> Unit = {},
- onLkmUpload: () -> Unit = {},
scrollBehavior: TopAppBarScrollBehavior? = null
) {
val colorScheme = MaterialTheme.colorScheme
@@ -985,21 +1002,35 @@ private fun TopBar(
windowInsets = WindowInsets.safeDrawing.only(
WindowInsetsSides.Top + WindowInsetsSides.Horizontal
),
- actions = {
- IconButton(
- modifier = Modifier.padding(end = 16.dp),
- onClick = onLkmUpload
- ) {
- Icon(
- Icons.AutoMirrored.Filled.Input,
- contentDescription = null
- )
- }
- },
scrollBehavior = scrollBehavior
)
}
+private fun isKoFile(context: Context, uri: Uri): Boolean {
+ val seg = uri.lastPathSegment ?: ""
+ if (seg.endsWith(".ko", ignoreCase = true)) return true
+
+ return try {
+ context.contentResolver.query(
+ uri,
+ arrayOf(OpenableColumns.DISPLAY_NAME),
+ null,
+ null,
+ null
+ )?.use { cursor ->
+ val idx = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
+ if (idx != -1 && cursor.moveToFirst()) {
+ val name = cursor.getString(idx)
+ name?.endsWith(".ko", ignoreCase = true) == true
+ } else {
+ false
+ }
+ } ?: false
+ } catch (_: Throwable) {
+ false
+ }
+}
+
@Preview
@Composable
fun SelectInstallPreview() {
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 108901ca..a7e36e67 100644
--- a/manager/app/src/main/res/values-zh-rCN/strings.xml
+++ b/manager/app/src/main/res/values-zh-rCN/strings.xml
@@ -110,6 +110,8 @@
安装到未使用的槽位(OTA 后)
将在重启后强制切换到另一个槽位!\n注意只能在 OTA 更新完成后的重启之前使用。\n确认?
下一步
+ 使用本地 LKM 文件
+ 仅支持选择 .ko 文件
建议选择 %1$s 分区镜像
(实验性的)
选择 KMI
@@ -123,7 +125,7 @@
刷写中
刷写完成
刷写失败
- 选择的 LKM:%s
+ 已选择的 LKM:%s
保存日志
日志已保存
SuS SU 模式:
diff --git a/manager/app/src/main/res/values/strings.xml b/manager/app/src/main/res/values/strings.xml
index 4664ff11..0bce4b1c 100644
--- a/manager/app/src/main/res/values/strings.xml
+++ b/manager/app/src/main/res/values/strings.xml
@@ -112,6 +112,8 @@
Install to inactive slot (After OTA)
Your device will be **FORCED** to boot to the current inactive slot after a reboot!\nOnly use this option after OTA is done.\nContinue?
Next
+ Use local LKM file
+ Only .ko files are supported
%1$s partition image is recommended
(Unstable)
Select KMI