fix: scramble update-source (XOR+reverse+Base64) + hide in UI
- update-source in release notes now XOR-scrambled (not plain Base64) - UpdateDialog filters out update-source comment line from notes - Download progress shows "正在下载更新…" instead of percentage/source - version 1.8.1 -> 1.8.2 (code 18 -> 19) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -13,8 +13,8 @@ android {
|
||||
applicationId = "com.a2i.forwarder"
|
||||
minSdk = 34
|
||||
targetSdk = 36
|
||||
versionCode = 18
|
||||
versionName = "1.8.1"
|
||||
versionCode = 19
|
||||
versionName = "1.8.2"
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
|
||||
@@ -250,17 +250,26 @@ class UpdateChecker(private val context: Context) {
|
||||
return false
|
||||
}
|
||||
|
||||
/** 从 release notes 的 HTML 注释里解析额外更新源(Base64 编码,兼容明文)。 */
|
||||
/** 从 release notes 的 HTML 注释里解析额外更新源(加扰编码,兼容 Base64/明文)。 */
|
||||
private fun parseUpdateSource(body: String): String? {
|
||||
val raw = Regex("""update-source:\s*(\S+)""").find(body)?.groupValues?.getOrNull(1) ?: return null
|
||||
// 尝试 Base64 解码;解码失败则原样返回(兼容明文)
|
||||
return runCatching { String(java.util.Base64.getDecoder().decode(raw), Charsets.UTF_8) }
|
||||
.getOrNull() ?: raw
|
||||
// 加扰格式:Base64(反转(XOR(明文, key)))。尝试完整解扰,失败则依次尝试 Base64/明文。
|
||||
val b64 = runCatching { java.util.Base64.getDecoder().decode(raw) }.getOrNull() ?: return raw
|
||||
val unscrambled = unscramble(b64)
|
||||
return unscrambled ?: runCatching { String(b64, Charsets.UTF_8) }.getOrNull() ?: raw
|
||||
}
|
||||
|
||||
/** 解扰:反转字节 → XOR 每字节 → UTF-8 字符串。 */
|
||||
private fun unscramble(data: ByteArray): String? = runCatching {
|
||||
val key = SCRAMBLE_KEY
|
||||
val reversed = data.reversedArray()
|
||||
String(ByteArray(reversed.size) { i -> ((reversed[i].toInt() and 0xFF) xor (key[i % key.size].toInt() and 0xFF)).toByte() }, Charsets.UTF_8)
|
||||
}.getOrNull()
|
||||
|
||||
companion object {
|
||||
const val GITHUB_API = "https://api.github.com/repos/lsxf/a2i/releases/latest"
|
||||
const val CHECK_INTERVAL_MS = 24L * 60 * 60 * 1000
|
||||
private val SCRAMBLE_KEY = byteArrayOf(0x4B, 0x6F, 0x72, 0x6E) // "Korn"
|
||||
}
|
||||
|
||||
fun formatSize(bytes: Long): String {
|
||||
|
||||
@@ -55,7 +55,11 @@ fun UpdateDialog(info: UpdateInfo, onDismiss: () -> Unit) {
|
||||
.heightIn(max = 240.dp)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
val notes = info.releaseNotes.ifBlank { "(作者未提供更新说明)" }
|
||||
val notes = info.releaseNotes
|
||||
.lineSequence()
|
||||
.filterNot { it.contains("update-source") }
|
||||
.joinToString("\n")
|
||||
.ifBlank { "(作者未提供更新说明)" }
|
||||
Text(notes, style = MaterialTheme.typography.bodyMedium)
|
||||
}
|
||||
if (downloading) {
|
||||
@@ -66,7 +70,7 @@ fun UpdateDialog(info: UpdateInfo, onDismiss: () -> Unit) {
|
||||
)
|
||||
Spacer(Modifier.size(4.dp))
|
||||
Text(
|
||||
"$progress%",
|
||||
"正在下载更新…",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user