diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Flash.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Flash.kt index e991839c..917f5dc6 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Flash.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Flash.kt @@ -33,6 +33,8 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import androidx.activity.ComponentActivity +import androidx.compose.material.icons.outlined.Info +import androidx.compose.ui.platform.LocalUriHandler import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.RootGraph import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination @@ -52,8 +54,10 @@ import java.io.File import java.text.SimpleDateFormat import java.util.* import androidx.core.content.edit +import com.sukisu.ultra.ui.component.rememberCustomDialog import com.sukisu.ultra.ui.util.module.ModuleOperationUtils import com.sukisu.ultra.ui.util.module.ModuleUtils +import com.topjohnwu.superuser.io.SuFile /** * @author ShirkNeko @@ -152,6 +156,7 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) { var tempText: String val logContent = rememberSaveable { StringBuilder() } var showFloatAction by rememberSaveable { mutableStateOf(false) } + var shouldWarningUserMetaModule by rememberSaveable { mutableStateOf(false) } // 添加状态跟踪是否已经完成刷写 var hasFlashCompleted by rememberSaveable { mutableStateOf(false) } var hasExecuted by rememberSaveable { mutableStateOf(false) } @@ -170,6 +175,39 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) { val logSavedString = stringResource(R.string.log_saved) val installingModuleString = stringResource(R.string.installing_module) + val alertDialog = rememberCustomDialog { dismiss: () -> Unit -> + val uriHandler = LocalUriHandler.current + AlertDialog( + onDismissRequest = { dismiss() }, + icon = { + Icon(Icons.Outlined.Info, contentDescription = null) + }, + title = { + Row(modifier = Modifier + .fillMaxWidth(), + horizontalArrangement = Arrangement.Center + ) { + Text(text = stringResource(R.string.warning_of_meta_module_title)) + } + }, + text = { + Text(text = stringResource(R.string.warning_of_meta_module_summary)) + }, + confirmButton = { + FilledTonalButton(onClick = { dismiss() }) { + Text(text = stringResource(id = android.R.string.ok)) + } + }, + dismissButton = { + OutlinedButton(onClick = { + uriHandler.openUri("https://kernelsu.org/guide/metamodule.html") + }) { + Text(text = stringResource(id = R.string.learn_more)) + } + }, + ) + } + // 当前模块安装状态 val currentStatus = moduleInstallStatus.value @@ -182,16 +220,19 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) { totalModules = flashIt.uris.size, currentModule = 1 ) + shouldWarningUserMetaModule = false hasFlashCompleted = false hasExecuted = false moduleVerificationMap.clear() } } is FlashIt.FlashModuleUpdate -> { + shouldWarningUserMetaModule = false hasUpdateCompleted = false hasUpdateExecuted = false } else -> { + shouldWarningUserMetaModule = false hasFlashCompleted = false hasExecuted = false } @@ -240,10 +281,29 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) { } hasUpdateCompleted = true + if (!hasMetaModule() && code == 0) { + // 如果没安装 MetaModule,且此模块需要挂载,并且当前模块安装成功,警告用户 + scope.launch { + val mountOldDirectory = SuFile.open("/data/adb/modules/${getModuleIdFromUri(context,flashIt.uri)}/system") + val mountNewDirectory = SuFile.open("/data/adb/modules_update/${getModuleIdFromUri(context,flashIt.uri)}/system") + if (!(mountNewDirectory.isDirectory) && !(mountOldDirectory.isDirectory)) return@launch + shouldWarningUserMetaModule = true + + alertDialog.show() + shouldWarningUserMetaModule = false + } + } + // 如果是外部安装或需要自动退出的模块更新且不需要重启,延迟后自动返回 if (isExternalInstall || shouldAutoExit) { scope.launch { + while (shouldWarningUserMetaModule) { + kotlinx.coroutines.delay(100) + } kotlinx.coroutines.delay(1000) + while (shouldWarningUserMetaModule) { + kotlinx.coroutines.delay(100) + } if (shouldAutoExit) { val sharedPref = context.getSharedPreferences("kernel_flash_prefs", Context.MODE_PRIVATE) sharedPref.edit { remove("auto_exit_after_flash") } @@ -334,6 +394,38 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) { } hasFlashCompleted = true + if (!hasMetaModule() && code == 0) { + // 没有 MetaModule,且安装成功,检查此模块是否有自动挂载 + scope.launch { + var mountOldDirectory : File + var mountNewDirectory : File + when (flashIt) { + is FlashIt.FlashModules -> { + mountOldDirectory = SuFile.open("/data/adb/modules/${getModuleIdFromUri(context,flashIt.uris[flashIt.currentIndex])}/system") + mountNewDirectory = SuFile.open("/data/adb/modules_update/${getModuleIdFromUri(context,flashIt.uris[flashIt.currentIndex])}/system") + } + + is FlashIt.FlashModule -> { + mountOldDirectory = SuFile.open("/data/adb/modules/${getModuleIdFromUri(context,flashIt.uri)}/system") + mountNewDirectory = SuFile.open("/data/adb/modules_update/${getModuleIdFromUri(context,flashIt.uri)}/system") + } + + is FlashIt.FlashModuleUpdate -> { + mountOldDirectory = SuFile.open("/data/adb/modules/${getModuleIdFromUri(context,flashIt.uri)}/system") + mountNewDirectory = SuFile.open("/data/adb/modules_update/${getModuleIdFromUri(context,flashIt.uri)}/system") + } + + else -> return@launch + } + if (!mountNewDirectory.isDirectory && !mountOldDirectory.isDirectory) return@launch + shouldWarningUserMetaModule = true + + if (!hasMetaModule() && (flashIt !is FlashIt.FlashModules || flashIt.currentIndex >= flashIt.uris.size - 1)) { + // 如果没有 MetaModule,且当前不是多模块刷写或是最后一个需要自动刷写的模块,而且有模块需要挂载,警告用户 + alertDialog.show() + } + } + } if (flashIt is FlashIt.FlashModules && flashIt.currentIndex < flashIt.uris.size - 1) { val nextFlashIt = flashIt.copy( @@ -346,7 +438,13 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) { } else if ((isExternalInstall || shouldAutoExit) && flashIt is FlashIt.FlashModules && flashIt.currentIndex >= flashIt.uris.size - 1) { // 如果是外部安装或需要自动退出且是最后一个模块,安装完成后自动返回 scope.launch { + while (shouldWarningUserMetaModule) { + kotlinx.coroutines.delay(100) + } kotlinx.coroutines.delay(1000) + while (shouldWarningUserMetaModule) { + kotlinx.coroutines.delay(100) + } if (shouldAutoExit) { val sharedPref = context.getSharedPreferences("kernel_flash_prefs", Context.MODE_PRIVATE) sharedPref.edit { remove("auto_exit_after_flash") } @@ -356,7 +454,13 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) { } else if ((isExternalInstall || shouldAutoExit) && flashIt is FlashIt.FlashModule) { // 如果是外部安装或需要自动退出的单个模块,安装完成后自动返回 scope.launch { + while (shouldWarningUserMetaModule) { + kotlinx.coroutines.delay(100) + } kotlinx.coroutines.delay(1000) + while (shouldWarningUserMetaModule) { + kotlinx.coroutines.delay(100) + } if (shouldAutoExit) { val sharedPref = context.getSharedPreferences("kernel_flash_prefs", Context.MODE_PRIVATE) sharedPref.edit { remove("auto_exit_after_flash") } @@ -705,6 +809,22 @@ suspend fun getModuleNameFromUri(context: Context, uri: Uri): String { } } +suspend fun getModuleIdFromUri(context: Context, uri: Uri): String? { + return withContext(Dispatchers.IO) { + try { + if (uri == Uri.EMPTY) { + return@withContext null + } + if (!ModuleUtils.isUriAccessible(context, uri)) { + return@withContext null + } + ModuleUtils.extractModuleId(context, uri) + } catch (_: Exception) { + null + } + } +} + @Parcelize sealed class FlashIt : Parcelable { data class FlashBoot(val boot: Uri? = null, val lkm: LkmSelection, val ota: Boolean, val partition: String? = null) : FlashIt() diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Home.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Home.kt index b6ef7128..35f95d26 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Home.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Home.kt @@ -182,6 +182,7 @@ fun HomeScreen(navigator: DestinationsNavigator) { isSimpleMode = viewModel.isSimpleMode, isHideSusfsStatus = viewModel.isHideSusfsStatus, isHideZygiskImplement = viewModel.isHideZygiskImplement, + isHideMetaModuleImplement = viewModel.isHideMetaModuleImplement, showKpmInfo = viewModel.showKpmInfo, lkmMode = viewModel.systemStatus.lkmMode, ) @@ -652,6 +653,7 @@ private fun InfoCard( isSimpleMode: Boolean, isHideSusfsStatus: Boolean, isHideZygiskImplement: Boolean, + isHideMetaModuleImplement: Boolean, showKpmInfo: Boolean, lkmMode: Boolean? ) { @@ -784,6 +786,14 @@ private fun InfoCard( ) } + if (!isHideMetaModuleImplement && !isSimpleMode && systemInfo.metaModuleImplement != "None") { + InfoCardItem( + stringResource(R.string.home_meta_module_implement), + systemInfo.metaModuleImplement, + icon = Icons.Default.Extension, + ) + } + if (!isSimpleMode) { if (lkmMode != true && !showKpmInfo) { val displayVersion = diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Module.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Module.kt index 65d85740..360b865a 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Module.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Module.kt @@ -752,6 +752,7 @@ private fun ModuleList( val uninstall = stringResource(R.string.uninstall) val cancel = stringResource(android.R.string.cancel) val moduleUninstallConfirm = stringResource(R.string.module_uninstall_confirm) + val metaModuleUninstallConfirm = stringResource(R.string.metamodule_uninstall_confirm) val updateText = stringResource(R.string.module_update) val changelogText = stringResource(R.string.module_changelog) val downloadingText = stringResource(R.string.module_downloading) @@ -847,9 +848,10 @@ private fun ModuleList( suspend fun onModuleUninstallClicked(module: ModuleViewModel.ModuleInfo) { val isUninstall = !module.remove if (isUninstall) { + val formatter = if (module.metamodule) metaModuleUninstallConfirm else moduleUninstallConfirm val confirmResult = confirmDialog.awaitConfirm( moduleStr, - content = moduleUninstallConfirm.format(module.name), + content = formatter.format(module.name), confirm = uninstall, dismiss = cancel ) @@ -1199,6 +1201,22 @@ fun ModuleItem( horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth() ) { + if (module.metamodule) { + Surface( + shape = RoundedCornerShape(4.dp), + color = MaterialTheme.colorScheme.primary, + modifier = Modifier + ) { + Text( + text = "META", + style = MaterialTheme.typography.labelSmall, + modifier = Modifier.padding(horizontal = 4.dp, vertical = 1.dp), + color = MaterialTheme.colorScheme.onPrimary, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + } Surface( shape = RoundedCornerShape(4.dp), color = MaterialTheme.colorScheme.primary, @@ -1329,8 +1347,9 @@ fun ModuleItemPreview() { update = true, remove = false, updateJson = "", - hasWebUi = false, - hasActionScript = false, + hasWebUi = true, + hasActionScript = true, + metamodule = true, dirId = "dirId", config = ModuleConfig(), isVerified = true, diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/util/KsuCli.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/util/KsuCli.kt index 3e76b11d..9ed4d03f 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/util/KsuCli.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/util/KsuCli.kt @@ -19,8 +19,10 @@ import kotlinx.parcelize.Parcelize import com.sukisu.ultra.BuildConfig import com.sukisu.ultra.Natives import com.sukisu.ultra.ksuApp +import com.topjohnwu.superuser.io.SuFile import org.json.JSONArray import java.io.File +import java.util.Properties /** @@ -111,6 +113,10 @@ fun install() { Log.w(TAG, "install result: $result, cost: ${SystemClock.elapsedRealtime() - start}ms") } +fun hasMetaModule(): Boolean { + return getMetaModuleImplement() != "None" +} + fun listModules(): String { val shell = getRootShell() @@ -575,6 +581,26 @@ fun getSuSFSFeatures(): String { return runCmd(shell, cmd) } +fun getMetaModuleImplement(): String { + try { + val metaModuleProp = SuFile.open("/data/adb/metamodule/module.prop") + if (!metaModuleProp.isFile) { + Log.i(TAG, "Meta module implement: None") + return "None" + } + + val prop = Properties() + prop.load(metaModuleProp.newInputStream()) + + val name = prop.getProperty("name") + Log.i(TAG, "Meta module implement: $name") + return name + } catch (t : Throwable) { + Log.i(TAG, "Meta module implement: None") + return "None" + } +} + fun getZygiskImplement(): String { val zygiskModuleIds = listOf( "zygisksu", diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/viewmodel/HomeViewModel.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/viewmodel/HomeViewModel.kt index 72e069f9..924e3a20 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/viewmodel/HomeViewModel.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/viewmodel/HomeViewModel.kt @@ -54,7 +54,8 @@ class HomeViewModel : ViewModel() { val kpmModuleCount: Int = 0, val managersList: Natives.ManagersList? = null, val isDynamicSignEnabled: Boolean = false, - val zygiskImplement: String = "" + val zygiskImplement: String = "", + val metaModuleImplement: String = "" ) // 状态变量 @@ -79,6 +80,8 @@ class HomeViewModel : ViewModel() { private set var isHideZygiskImplement by mutableStateOf(false) private set + var isHideMetaModuleImplement by mutableStateOf(false) + private set var isHideLinkCard by mutableStateOf(false) private set var showKpmInfo by mutableStateOf(false) @@ -109,6 +112,7 @@ class HomeViewModel : ViewModel() { isHideSusfsStatus = settingsPrefs.getBoolean("is_hide_susfs_status", false) isHideLinkCard = settingsPrefs.getBoolean("is_hide_link_card", false) isHideZygiskImplement = settingsPrefs.getBoolean("is_hide_zygisk_Implement", false) + isHideMetaModuleImplement = settingsPrefs.getBoolean("is_hide_meta_module_Implement", false) showKpmInfo = settingsPrefs.getBoolean("show_kpm_info", false) } } @@ -222,7 +226,8 @@ class HomeViewModel : ViewModel() { superuserCount = moduleInfo.second, moduleCount = moduleInfo.third, kpmModuleCount = moduleInfo.fourth, - zygiskImplement = moduleInfo.fifth + zygiskImplement = moduleInfo.fifth, + metaModuleImplement = moduleInfo.sixth ) } @@ -398,7 +403,7 @@ class HomeViewModel : ViewModel() { } } - private suspend fun loadModuleInfo(): Tuple5 { + private suspend fun loadModuleInfo(): Tuple6 { return withContext(Dispatchers.IO) { val kpmVersion = try { getKpmVersion() @@ -430,7 +435,13 @@ class HomeViewModel : ViewModel() { "None" } - Tuple5(kpmVersion, superuserCount, moduleCount, kpmModuleCount, zygiskImplement) + val metaModuleImplement = try { + getMetaModuleImplement() + } catch (_: Exception) { + "None" + } + + Tuple6(kpmVersion, superuserCount, moduleCount, kpmModuleCount, zygiskImplement, metaModuleImplement) } } @@ -567,6 +578,15 @@ class HomeViewModel : ViewModel() { } } + data class Tuple6( + val first: T1, + val second: T2, + val third: T3, + val fourth: T4, + val fifth: T5, + val sixth: T6 + ) + data class Tuple5( val first: T1, val second: T2, diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/viewmodel/ModuleViewModel.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/viewmodel/ModuleViewModel.kt index 7a5fd9b3..c8b83024 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/viewmodel/ModuleViewModel.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/viewmodel/ModuleViewModel.kt @@ -85,6 +85,7 @@ class ModuleViewModel : ViewModel() { val updateJson: String, val hasWebUi: Boolean, val hasActionScript: Boolean, + val metamodule: Boolean, val dirId: String, // real module id (dir name) var config: ModuleConfig? = null, var isVerified: Boolean = false, // 添加验证状态字段 @@ -151,6 +152,7 @@ class ModuleViewModel : ViewModel() { obj.optString("updateJson"), obj.getBooleanCompat("web"), obj.getBooleanCompat("action"), + obj.getBooleanCompat("metamodule"), obj.optString("dir_id", obj.getString("id")) ) }.toList() @@ -305,6 +307,7 @@ fun ModuleViewModel.ModuleInfo.copy( updateJson: String = this.updateJson, hasWebUi: Boolean = this.hasWebUi, hasActionScript: Boolean = this.hasActionScript, + metamodule: Boolean = this.metamodule, dirId: String = this.dirId, config: ModuleConfig? = this.config, isVerified: Boolean = this.isVerified, @@ -312,7 +315,7 @@ fun ModuleViewModel.ModuleInfo.copy( ): ModuleViewModel.ModuleInfo { return ModuleViewModel.ModuleInfo( id, name, author, version, versionCode, description, - enabled, update, remove, updateJson, hasWebUi, hasActionScript, + enabled, update, remove, updateJson, hasWebUi, hasActionScript, metamodule, dirId, config, isVerified, verificationTimestamp ) } diff --git a/manager/app/src/main/java/zako/zako/zako/zakoui/screen/moreSettings/MoreSettings.kt b/manager/app/src/main/java/zako/zako/zako/zakoui/screen/moreSettings/MoreSettings.kt index 1540d3a6..22a824d7 100644 --- a/manager/app/src/main/java/zako/zako/zako/zakoui/screen/moreSettings/MoreSettings.kt +++ b/manager/app/src/main/java/zako/zako/zako/zakoui/screen/moreSettings/MoreSettings.kt @@ -312,6 +312,16 @@ private fun HideOptionsSettings( onChange = handlers::handleHideZygiskImplementChange ) + // 元模块实现状态信息 + SwitchSettingItem( + icon = Icons.Filled.VisibilityOff, + title = stringResource(R.string.hide_meta_module_implement), + summary = stringResource(R.string.hide_meta_module_implement_summary), + checked = state.isHideMetaModuleImplement, + onChange = handlers::handleHideMetaModuleImplementChange + ) + + // KPM 状态信息隐藏 if (Natives.version >= Natives.MINIMAL_SUPPORTED_KPM) { SwitchSettingItem( icon = Icons.Filled.VisibilityOff, diff --git a/manager/app/src/main/java/zako/zako/zako/zakoui/screen/moreSettings/MoreSettingsHandlers.kt b/manager/app/src/main/java/zako/zako/zako/zakoui/screen/moreSettings/MoreSettingsHandlers.kt index b5b9921a..8975c922 100644 --- a/manager/app/src/main/java/zako/zako/zako/zakoui/screen/moreSettings/MoreSettingsHandlers.kt +++ b/manager/app/src/main/java/zako/zako/zako/zakoui/screen/moreSettings/MoreSettingsHandlers.kt @@ -341,6 +341,14 @@ class MoreSettingsHandlers( state.isHideZygiskImplement = newValue } + /** + * 处理隐藏元模块实现变更 + */ + fun handleHideMetaModuleImplementChange(newValue: Boolean) { + prefs.edit { putBoolean("is_hide_meta_module_Implement", newValue) } + state.isHideMetaModuleImplement = newValue + } + /** * 处理隐藏链接卡片变更 */ diff --git a/manager/app/src/main/java/zako/zako/zako/zakoui/screen/moreSettings/state/MoreSettingsState.kt b/manager/app/src/main/java/zako/zako/zako/zakoui/screen/moreSettings/state/MoreSettingsState.kt index 26e9593c..8d73d984 100644 --- a/manager/app/src/main/java/zako/zako/zako/zakoui/screen/moreSettings/state/MoreSettingsState.kt +++ b/manager/app/src/main/java/zako/zako/zako/zakoui/screen/moreSettings/state/MoreSettingsState.kt @@ -60,6 +60,7 @@ class MoreSettingsState( var isHideOtherInfo by mutableStateOf(prefs.getBoolean("is_hide_other_info", false)) var isShowKpmInfo by mutableStateOf(prefs.getBoolean("show_kpm_info", false)) var isHideZygiskImplement by mutableStateOf(prefs.getBoolean("is_hide_zygisk_Implement", false)) + var isHideMetaModuleImplement by mutableStateOf(prefs.getBoolean("is_hide_meta_module_Implement", false)) var isHideSusfsStatus by mutableStateOf(prefs.getBoolean("is_hide_susfs_status", false)) var isHideLinkCard by mutableStateOf(prefs.getBoolean("is_hide_link_card", false)) var isHideTagRow by mutableStateOf(prefs.getBoolean("is_hide_tag_row", false)) 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 29a7e81a..f7146b9f 100644 --- a/manager/app/src/main/res/values-zh-rCN/strings.xml +++ b/manager/app/src/main/res/values-zh-rCN/strings.xml @@ -1,5 +1,6 @@ + 了解更多 主页 未安装 点击安装 @@ -34,6 +35,7 @@ 重启到 EDL 关于 确定要卸载模块 %s 吗? + "您确定要卸载模块 %s 吗?此操作将影响所有模块,并且元模块提供的某些功能(如挂载)将不再工作。 " %s 已卸载 卸载失败:%s 版本 @@ -188,10 +190,12 @@ 隐藏主页上的 SuSFS 状态信息 隐藏 Zygisk 状态信息 隐藏主页上的 Zygisk 实现状态信息 + 隐藏元模块状态信息 + 隐藏主页上的元模块实现状态信息 隐藏链接卡片 隐藏主页上的链接卡片信息 隐藏模块标签行 - 隐藏模块卡片中的文件夹名称和大小标签 + 隐藏模块卡片中的文件夹名称、大小标签和是否为元模块 主题模式 跟随系统 浅色 @@ -328,6 +332,8 @@ %d 个模块安装失败 模块下载失败 内核刷写 + 需要元模块 + 这个模块需要挂载一些文件。需要安装元模块,使它正常工作 全部 Root @@ -588,6 +594,7 @@ 无活跃管理器 SukiSU Zygisk 实现 + 元模块实现 SuS 循环路径 添加 SuS 循环路径 diff --git a/manager/app/src/main/res/values/strings.xml b/manager/app/src/main/res/values/strings.xml index ab839eec..700bcfc7 100644 --- a/manager/app/src/main/res/values/strings.xml +++ b/manager/app/src/main/res/values/strings.xml @@ -2,6 +2,7 @@ SukiSU Ultra Home + Learn more Not installed Click to install Working @@ -35,6 +36,7 @@ Reboot to EDL About Are you sure you want to uninstall module %s? + "Are you sure you want to uninstall module %s? This action will affect all modules, and certain features provided by the metamodule (such as mounting) will no longer work. " %s uninstalled Failed to uninstall: %s Version @@ -190,10 +192,12 @@ Hide SuSFS status information on the home page Hide Zygisk status Hide Zygisk implementation information on the home page + Hide Meta Module status + Hide Meta Module implementation information on the home page Hide Link Card Status Hide link card information on the home page Hide module label rows - Hide folder name and size labels in module cards + Hide folder name,size,metamodule notice labels in module cards Theme Follow system Light @@ -331,6 +335,8 @@ %d Failed to install a new module Module download failed Kernel Flashing + Require Meta module + This module want to mount /system, meta module will handle that. Otherwise, it might not work. All Root @@ -591,6 +597,7 @@ No active manager SukiSU Zygisk implement + Meta Module implement SUS Loop Paths Add SUS Loop Path