manager: Refinement of module signatures again
This commit is contained in:
@@ -70,11 +70,15 @@ data class ModuleInstallStatus(
|
||||
val totalModules: Int = 0,
|
||||
val currentModule: Int = 0,
|
||||
val currentModuleName: String = "",
|
||||
val failedModules: MutableList<String> = mutableListOf()
|
||||
val failedModules: MutableList<String> = mutableListOf(),
|
||||
val verifiedModules: MutableList<String> = mutableListOf() // 添加已验证模块列表
|
||||
)
|
||||
|
||||
private var moduleInstallStatus = mutableStateOf(ModuleInstallStatus())
|
||||
|
||||
// 存储模块URI和验证状态的映射
|
||||
private var moduleVerificationMap = mutableMapOf<Uri, Boolean>()
|
||||
|
||||
fun setFlashingStatus(status: FlashingStatus) {
|
||||
currentFlashingStatus.value = status
|
||||
}
|
||||
@@ -83,7 +87,8 @@ fun updateModuleInstallStatus(
|
||||
totalModules: Int? = null,
|
||||
currentModule: Int? = null,
|
||||
currentModuleName: String? = null,
|
||||
failedModule: String? = null
|
||||
failedModule: String? = null,
|
||||
verifiedModule: String? = null
|
||||
) {
|
||||
val current = moduleInstallStatus.value
|
||||
moduleInstallStatus.value = current.copy(
|
||||
@@ -99,6 +104,18 @@ fun updateModuleInstallStatus(
|
||||
failedModules = updatedFailedModules
|
||||
)
|
||||
}
|
||||
|
||||
if (verifiedModule != null) {
|
||||
val updatedVerifiedModules = current.verifiedModules.toMutableList()
|
||||
updatedVerifiedModules.add(verifiedModule)
|
||||
moduleInstallStatus.value = moduleInstallStatus.value.copy(
|
||||
verifiedModules = updatedVerifiedModules
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun setModuleVerificationStatus(uri: Uri, isVerified: Boolean) {
|
||||
moduleVerificationMap[uri] = isVerified
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@@ -142,6 +159,7 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
)
|
||||
hasFlashCompleted = false
|
||||
hasExecuted = false
|
||||
moduleVerificationMap.clear()
|
||||
}
|
||||
}
|
||||
is FlashIt.FlashModuleUpdate -> {
|
||||
@@ -179,6 +197,11 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
setFlashingStatus(FlashingStatus.FAILED)
|
||||
} else {
|
||||
setFlashingStatus(FlashingStatus.SUCCESS)
|
||||
|
||||
// 处理模块更新成功后的验证标志
|
||||
val isVerified = moduleVerificationMap[flashIt.uri] ?: false
|
||||
ModuleOperationUtils.handleModuleUpdate(context, flashIt.uri, isVerified)
|
||||
|
||||
viewModel.markNeedRefresh()
|
||||
}
|
||||
if (showReboot) {
|
||||
@@ -239,6 +262,28 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
}
|
||||
} else {
|
||||
setFlashingStatus(FlashingStatus.SUCCESS)
|
||||
|
||||
// 处理模块安装成功后的验证标志
|
||||
when (flashIt) {
|
||||
is FlashIt.FlashModule -> {
|
||||
val isVerified = moduleVerificationMap[flashIt.uri] ?: false
|
||||
ModuleOperationUtils.handleModuleInstallSuccess(context, flashIt.uri, isVerified)
|
||||
if (isVerified) {
|
||||
updateModuleInstallStatus(verifiedModule = moduleInstallStatus.value.currentModuleName)
|
||||
}
|
||||
}
|
||||
is FlashIt.FlashModules -> {
|
||||
val currentUri = flashIt.uris[flashIt.currentIndex]
|
||||
val isVerified = moduleVerificationMap[currentUri] ?: false
|
||||
ModuleOperationUtils.handleModuleInstallSuccess(context, currentUri, isVerified)
|
||||
if (isVerified) {
|
||||
updateModuleInstallStatus(verifiedModule = moduleInstallStatus.value.currentModuleName)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
|
||||
viewModel.markNeedRefresh()
|
||||
}
|
||||
if (showReboot) {
|
||||
|
||||
@@ -189,6 +189,8 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||
for (uri in selectedModules) {
|
||||
val isVerified = verifyModuleSignature(context, uri)
|
||||
verificationResults[uri] = isVerified
|
||||
// 存储验证状态
|
||||
setModuleVerificationStatus(uri, isVerified)
|
||||
|
||||
if (forceVerification && !isVerified) {
|
||||
withContext(Dispatchers.Main) {
|
||||
@@ -253,6 +255,8 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||
// 验证模块签名
|
||||
val forceVerification = prefs.getBoolean("force_signature_verification", false)
|
||||
val isVerified = verifyModuleSignature(context, uri)
|
||||
// 存储验证状态
|
||||
setModuleVerificationStatus(uri, isVerified)
|
||||
|
||||
if (forceVerification && !isVerified) {
|
||||
signatureDialogMessage = context.getString(R.string.module_signature_invalid_message)
|
||||
@@ -835,7 +839,12 @@ private fun ModuleList(
|
||||
downloadUrl,
|
||||
fileName,
|
||||
downloading,
|
||||
onDownloaded = onUpdateModule,
|
||||
onDownloaded = { uri ->
|
||||
// 验证更新模块的签名
|
||||
val isVerified = verifyModuleSignature(context, uri)
|
||||
setModuleVerificationStatus(uri, isVerified)
|
||||
onUpdateModule(uri)
|
||||
},
|
||||
onDownloading = {
|
||||
launch(Dispatchers.Main) {
|
||||
Toast.makeText(context, downloading, Toast.LENGTH_SHORT).show()
|
||||
@@ -867,6 +876,8 @@ private fun ModuleList(
|
||||
val success = loadingDialog.withLoading {
|
||||
withContext(Dispatchers.IO) {
|
||||
if (isUninstall) {
|
||||
// 卸载时移除验证标志
|
||||
ModuleOperationUtils.handleModuleUninstall(module.dirId)
|
||||
uninstallModule(module.dirId)
|
||||
} else {
|
||||
restoreModule(module.dirId)
|
||||
@@ -1075,14 +1086,48 @@ fun ModuleItem(
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(0.8f)
|
||||
) {
|
||||
Text(
|
||||
text = module.name,
|
||||
fontSize = MaterialTheme.typography.titleMedium.fontSize,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
fontFamily = MaterialTheme.typography.titleMedium.fontFamily,
|
||||
textDecoration = textDecoration,
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
text = module.name,
|
||||
fontSize = MaterialTheme.typography.titleMedium.fontSize,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
fontFamily = MaterialTheme.typography.titleMedium.fontFamily,
|
||||
textDecoration = textDecoration,
|
||||
modifier = Modifier.weight(1f, false)
|
||||
)
|
||||
|
||||
// 显示验证标签
|
||||
if (module.isVerified) {
|
||||
Surface(
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.padding(horizontal = 6.dp, vertical = 2.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Verified,
|
||||
contentDescription = stringResource(R.string.module_signature_verified),
|
||||
tint = MaterialTheme.colorScheme.onPrimary,
|
||||
modifier = Modifier.size(12.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(2.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.module_verified),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.onPrimary,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text(
|
||||
text = "$moduleVersion: ${module.version}",
|
||||
@@ -1309,6 +1354,8 @@ fun ModuleItemPreview() {
|
||||
hasActionScript = false,
|
||||
dirId = "dirId",
|
||||
config = ModuleConfig(),
|
||||
isVerified = true,
|
||||
verificationTimestamp = System.currentTimeMillis()
|
||||
)
|
||||
ModuleItem(EmptyDestinationsNavigator, module, "", {}, {}, {}, {})
|
||||
}
|
||||
@@ -36,7 +36,7 @@ object ModuleUtils {
|
||||
}
|
||||
}?.removeSuffix(".zip") ?: context.getString(R.string.unknown_module)
|
||||
|
||||
var formattedFileName = fileName.replace(Regex("[^a-zA-Z0-9\\s\\-_.@()\\u4e00-\\u9fa5]"), "").trim()
|
||||
val formattedFileName = fileName.replace(Regex("[^a-zA-Z0-9\\s\\-_.@()\\u4e00-\\u9fa5]"), "").trim()
|
||||
var moduleName = formattedFileName
|
||||
|
||||
try {
|
||||
@@ -55,12 +55,10 @@ object ModuleUtils {
|
||||
if (entry.name == "module.prop") {
|
||||
val reader = BufferedReader(InputStreamReader(zipInputStream, StandardCharsets.UTF_8))
|
||||
var line: String?
|
||||
var nameFound = false
|
||||
while (reader.readLine().also { line = it } != null) {
|
||||
if (line?.startsWith("name=") == true) {
|
||||
moduleName = line.substringAfter("=")
|
||||
moduleName = moduleName.replace(Regex("[^a-zA-Z0-9\\s\\-_.@()\\u4e00-\\u9fa5]"), "").trim()
|
||||
nameFound = true
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -105,6 +103,45 @@ object ModuleUtils {
|
||||
Log.e(TAG, "Unable to get persistent permissions on URIs: $uri, Error: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
fun extractModuleId(context: Context, uri: Uri): String? {
|
||||
if (uri == Uri.EMPTY) {
|
||||
return null
|
||||
}
|
||||
|
||||
return try {
|
||||
|
||||
val inputStream = context.contentResolver.openInputStream(uri)
|
||||
if (inputStream == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
val zipInputStream = ZipInputStream(inputStream)
|
||||
var entry = zipInputStream.nextEntry
|
||||
var moduleId: String? = null
|
||||
|
||||
// 遍历ZIP文件中的条目,查找module.prop文件
|
||||
while (entry != null) {
|
||||
if (entry.name == "module.prop") {
|
||||
val reader = BufferedReader(InputStreamReader(zipInputStream, StandardCharsets.UTF_8))
|
||||
var line: String?
|
||||
while (reader.readLine().also { line = it } != null) {
|
||||
if (line?.startsWith("id=") == true) {
|
||||
moduleId = line.substringAfter("=").trim()
|
||||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
entry = zipInputStream.nextEntry
|
||||
}
|
||||
zipInputStream.close()
|
||||
moduleId
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "提取模块ID时发生异常: ${e.message}", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 模块签名验证工具类
|
||||
@@ -143,3 +180,71 @@ object ModuleSignatureUtils {
|
||||
fun verifyModuleSignature(context: Context, moduleUri: Uri): Boolean {
|
||||
return ModuleSignatureUtils.verifyModuleSignature(context, moduleUri)
|
||||
}
|
||||
|
||||
object ModuleOperationUtils {
|
||||
private const val TAG = "ModuleOperationUtils"
|
||||
|
||||
fun handleModuleInstallSuccess(context: Context, moduleUri: Uri, isSignatureVerified: Boolean) {
|
||||
if (!isSignatureVerified) {
|
||||
Log.d(TAG, "模块签名未验证,跳过创建验证标志")
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// 从ZIP文件提取模块ID
|
||||
val moduleId = ModuleUtils.extractModuleId(context, moduleUri)
|
||||
if (moduleId == null) {
|
||||
Log.e(TAG, "无法提取模块ID,无法创建验证标志")
|
||||
return
|
||||
}
|
||||
|
||||
// 创建验证标志文件
|
||||
val success = ModuleVerificationManager.createVerificationFlag(moduleId)
|
||||
if (success) {
|
||||
Log.d(TAG, "模块 $moduleId 验证标志创建成功")
|
||||
} else {
|
||||
Log.e(TAG, "模块 $moduleId 验证标志创建失败")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "处理模块安装成功时发生异常", e)
|
||||
}
|
||||
}
|
||||
|
||||
fun handleModuleUninstall(moduleId: String) {
|
||||
try {
|
||||
val success = ModuleVerificationManager.removeVerificationFlag(moduleId)
|
||||
if (success) {
|
||||
Log.d(TAG, "模块 $moduleId 验证标志移除成功")
|
||||
} else {
|
||||
Log.d(TAG, "模块 $moduleId 验证标志移除失败或不存在")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "处理模块卸载时发生异常: $moduleId", e)
|
||||
}
|
||||
}
|
||||
fun handleModuleUpdate(context: Context, moduleUri: Uri, isSignatureVerified: Boolean) {
|
||||
try {
|
||||
val moduleId = ModuleUtils.extractModuleId(context, moduleUri)
|
||||
if (moduleId == null) {
|
||||
Log.e(TAG, "无法提取模块ID,无法处理验证标志")
|
||||
return
|
||||
}
|
||||
|
||||
if (isSignatureVerified) {
|
||||
// 签名验证通过,创建或更新验证标志
|
||||
val success = ModuleVerificationManager.createVerificationFlag(moduleId)
|
||||
if (success) {
|
||||
Log.d(TAG, "模块 $moduleId 更新后验证标志已更新")
|
||||
} else {
|
||||
Log.e(TAG, "模块 $moduleId 更新后验证标志更新失败")
|
||||
}
|
||||
} else {
|
||||
// 签名验证失败,移除验证标志
|
||||
ModuleVerificationManager.removeVerificationFlag(moduleId)
|
||||
Log.d(TAG, "模块 $moduleId 更新后签名未验证,验证标志已移除")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "处理模块更新时发生异常", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package com.sukisu.ultra.ui.util
|
||||
|
||||
import android.util.Log
|
||||
|
||||
/**
|
||||
|
||||
* @author ShirkNeko
|
||||
* @date 2025/8/3
|
||||
*/
|
||||
object ModuleVerificationManager {
|
||||
private const val TAG = "ModuleVerificationManager"
|
||||
private const val VERIFICATION_FLAGS_DIR = "/data/adb/ksu/verified_modules"
|
||||
|
||||
/**
|
||||
* 为指定模块创建验证标志文件
|
||||
*
|
||||
* @param moduleId 模块文件夹名称
|
||||
* @return 是否成功创建标志文件
|
||||
*/
|
||||
fun createVerificationFlag(moduleId: String): Boolean {
|
||||
return try {
|
||||
val shell = getRootShell()
|
||||
val flagFilePath = "$VERIFICATION_FLAGS_DIR/$moduleId"
|
||||
|
||||
// 确保目录存在
|
||||
val createDirCommand = "mkdir -p '$VERIFICATION_FLAGS_DIR'"
|
||||
shell.newJob().add(createDirCommand).exec()
|
||||
|
||||
// 创建验证标志文件,写入验证时间戳
|
||||
val timestamp = System.currentTimeMillis()
|
||||
val command = "echo '$timestamp' > '$flagFilePath'"
|
||||
|
||||
val result = shell.newJob().add(command).exec()
|
||||
|
||||
if (result.isSuccess) {
|
||||
Log.d(TAG, "验证标志文件创建成功: $flagFilePath")
|
||||
true
|
||||
} else {
|
||||
Log.e(TAG, "验证标志文件创建失败: $moduleId")
|
||||
false
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "创建验证标志文件时发生异常: $moduleId", e)
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun removeVerificationFlag(moduleId: String): Boolean {
|
||||
return try {
|
||||
val shell = getRootShell()
|
||||
val flagFilePath = "$VERIFICATION_FLAGS_DIR/$moduleId"
|
||||
|
||||
val command = "rm -f '$flagFilePath'"
|
||||
val result = shell.newJob().add(command).exec()
|
||||
|
||||
if (result.isSuccess) {
|
||||
Log.d(TAG, "验证标志文件移除成功: $flagFilePath")
|
||||
true
|
||||
} else {
|
||||
Log.e(TAG, "验证标志文件移除失败: $moduleId")
|
||||
false
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "移除验证标志文件时发生异常: $moduleId", e)
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun getVerificationTimestamp(moduleId: String): Long {
|
||||
return try {
|
||||
val shell = getRootShell()
|
||||
val flagFilePath = "$VERIFICATION_FLAGS_DIR/$moduleId"
|
||||
|
||||
val command = "cat '$flagFilePath' 2>/dev/null || echo '0'"
|
||||
val result = shell.newJob().add(command).to(ArrayList(), null).exec()
|
||||
|
||||
if (result.isSuccess && result.out.isNotEmpty()) {
|
||||
val timestampStr = result.out.firstOrNull()?.trim() ?: "0"
|
||||
timestampStr.toLongOrNull() ?: 0L
|
||||
} else {
|
||||
0L
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "获取验证时间戳时发生异常: $moduleId", e)
|
||||
0L
|
||||
}
|
||||
}
|
||||
|
||||
fun batchCheckVerificationStatus(moduleIds: List<String>): Map<String, Boolean> {
|
||||
if (moduleIds.isEmpty()) return emptyMap()
|
||||
|
||||
return try {
|
||||
val shell = getRootShell()
|
||||
val result = mutableMapOf<String, Boolean>()
|
||||
|
||||
// 确保目录存在
|
||||
val createDirCommand = "mkdir -p '$VERIFICATION_FLAGS_DIR'"
|
||||
shell.newJob().add(createDirCommand).exec()
|
||||
|
||||
// 批量检查所有模块的验证标志文件
|
||||
val commands = moduleIds.map { moduleId ->
|
||||
"test -f '$VERIFICATION_FLAGS_DIR/$moduleId' && echo '$moduleId:true' || echo '$moduleId:false'"
|
||||
}
|
||||
|
||||
val command = commands.joinToString(" && ")
|
||||
val shellResult = shell.newJob().add(command).to(ArrayList(), null).exec()
|
||||
|
||||
if (shellResult.isSuccess) {
|
||||
shellResult.out.forEach { line ->
|
||||
val parts = line.split(":")
|
||||
if (parts.size == 2) {
|
||||
val moduleId = parts[0]
|
||||
val isVerified = parts[1] == "true"
|
||||
result[moduleId] = isVerified
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(TAG, "批量验证检查完成,共检查 ${moduleIds.size} 个模块")
|
||||
result
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "批量检查验证状态时发生异常", e)
|
||||
// 返回默认值,所有模块都标记为未验证
|
||||
moduleIds.associateWith { false }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import kotlinx.coroutines.launch
|
||||
import com.sukisu.ultra.ui.util.HanziToPinyin
|
||||
import com.sukisu.ultra.ui.util.listModules
|
||||
import com.sukisu.ultra.ui.util.getRootShell
|
||||
import com.sukisu.ultra.ui.util.ModuleVerificationManager
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
@@ -86,6 +87,8 @@ class ModuleViewModel : ViewModel() {
|
||||
val hasActionScript: Boolean,
|
||||
val dirId: String, // real module id (dir name)
|
||||
var config: ModuleConfig? = null,
|
||||
var isVerified: Boolean = false, // 添加验证状态字段
|
||||
var verificationTimestamp: Long = 0L, // 添加验证时间戳
|
||||
)
|
||||
|
||||
var isRefreshing by mutableStateOf(false)
|
||||
@@ -131,7 +134,7 @@ class ModuleViewModel : ViewModel() {
|
||||
Log.i(TAG, "result: $result")
|
||||
|
||||
val array = JSONArray(result)
|
||||
modules = (0 until array.length())
|
||||
val moduleInfos = (0 until array.length())
|
||||
.asSequence()
|
||||
.map { array.getJSONObject(it) }
|
||||
.map { obj ->
|
||||
@@ -151,6 +154,26 @@ class ModuleViewModel : ViewModel() {
|
||||
obj.getString("dir_id")
|
||||
)
|
||||
}.toList()
|
||||
|
||||
// 批量检查所有模块的验证状态
|
||||
val moduleIds = moduleInfos.map { it.dirId }
|
||||
val verificationStatus = ModuleVerificationManager.batchCheckVerificationStatus(moduleIds)
|
||||
|
||||
// 更新模块验证状态
|
||||
modules = moduleInfos.map { moduleInfo ->
|
||||
val isVerified = verificationStatus[moduleInfo.dirId] ?: false
|
||||
val verificationTimestamp = if (isVerified) {
|
||||
ModuleVerificationManager.getVerificationTimestamp(moduleInfo.dirId)
|
||||
} else {
|
||||
0L
|
||||
}
|
||||
|
||||
moduleInfo.copy(
|
||||
isVerified = isVerified,
|
||||
verificationTimestamp = verificationTimestamp
|
||||
)
|
||||
}
|
||||
|
||||
launch {
|
||||
modules.forEach { module ->
|
||||
withContext(Dispatchers.IO) {
|
||||
@@ -207,6 +230,14 @@ class ModuleViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
fun createModuleVerificationFlag(moduleId: String): Boolean {
|
||||
return ModuleVerificationManager.createVerificationFlag(moduleId)
|
||||
}
|
||||
|
||||
fun removeModuleVerificationFlag(moduleId: String): Boolean {
|
||||
return ModuleVerificationManager.removeVerificationFlag(moduleId)
|
||||
}
|
||||
|
||||
private fun sanitizeVersionString(version: String): String {
|
||||
return version.replace(Regex("[^a-zA-Z0-9.\\-_]"), "_")
|
||||
}
|
||||
@@ -269,6 +300,31 @@ class ModuleViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
fun ModuleViewModel.ModuleInfo.copy(
|
||||
id: String = this.id,
|
||||
name: String = this.name,
|
||||
author: String = this.author,
|
||||
version: String = this.version,
|
||||
versionCode: Int = this.versionCode,
|
||||
description: String = this.description,
|
||||
enabled: Boolean = this.enabled,
|
||||
update: Boolean = this.update,
|
||||
remove: Boolean = this.remove,
|
||||
updateJson: String = this.updateJson,
|
||||
hasWebUi: Boolean = this.hasWebUi,
|
||||
hasActionScript: Boolean = this.hasActionScript,
|
||||
dirId: String = this.dirId,
|
||||
config: ModuleConfig? = this.config,
|
||||
isVerified: Boolean = this.isVerified,
|
||||
verificationTimestamp: Long = this.verificationTimestamp
|
||||
): ModuleViewModel.ModuleInfo {
|
||||
return ModuleViewModel.ModuleInfo(
|
||||
id, name, author, version, versionCode, description,
|
||||
enabled, update, remove, updateJson, hasWebUi, hasActionScript,
|
||||
dirId, config, isVerified, verificationTimestamp
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 模块大小缓存管理器
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user