fix(android): APK包名统一gca-v0.1.0 + 应用名加版本 + Gitea manifest联调
- APK: app-debug.apk → gca-v0.1.0-debug.apk - App名: Yuzu GCA Test → Yuzu GCA v0.1.0 - MainActivity: 真实manifest拉取+JSON解析+SHA256真实比对 - Gitea Release #6: gca-v0.1.0.apk + manifest.json - 删除旧Expo modules/ 文件
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -45,3 +45,4 @@ android/build/
|
||||
*.jks
|
||||
*.keystore
|
||||
local.properties
|
||||
screen.png
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,6 +1,8 @@
|
||||
apply plugin: "com.android.application"
|
||||
apply plugin: "org.jetbrains.kotlin.android"
|
||||
|
||||
archivesBaseName = "gca-v0.1.0"
|
||||
|
||||
android {
|
||||
namespace "dev.yuzu.gca"
|
||||
compileSdk 35
|
||||
@@ -9,7 +11,7 @@ android {
|
||||
applicationId "dev.yuzu.gca"
|
||||
minSdk 24
|
||||
targetSdk 35
|
||||
versionCode 5
|
||||
versionCode 6
|
||||
versionName "0.1.0"
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
|
||||
<application
|
||||
android:label="Yuzu GCA Test"
|
||||
android:label="Yuzu GCA v0.1.0"
|
||||
android:allowBackup="false"
|
||||
android:supportsRtl="true"
|
||||
android:usesCleartextTraffic="true"
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:label="Yuzu GCA Test"
|
||||
android:label="Yuzu GCA v0.1.0"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
@@ -60,7 +60,7 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
appendLog("[1/4] 拉取 manifest...")
|
||||
try {
|
||||
val manifestJson = httpGet("https://git.childish-ghost.com/LukeMackin/Yuzu-GCA/releases/download/manifest/manifest.json")
|
||||
val manifestJson = httpGet("https://git.childish-ghost.com/LukeMackin/Yuzu-GCA/releases/download/v0.1.0-dav-android-6/manifest.json")
|
||||
appendLog(" manifest: ${manifestJson.take(200)}...")
|
||||
} catch (e: Exception) {
|
||||
appendLog(" ⚠️ 网络不可用: ${e.message}")
|
||||
@@ -69,25 +69,50 @@ class MainActivity : AppCompatActivity() {
|
||||
return@withContext
|
||||
}
|
||||
|
||||
appendLog("[2/4] 解析版本")
|
||||
appendLog(" 远程: v0.2.0-dav-android-1 > 本地: 0.1.0 → 需要更新 ✅")
|
||||
appendLog("[2/4] 解析 manifest JSON...")
|
||||
var manifestSha256 = ""
|
||||
var manifestApkUrl = ""
|
||||
try {
|
||||
val manifestJson = httpGet("https://git.childish-ghost.com/LukeMackin/Yuzu-GCA/releases/download/v0.1.0-dav-android-6/manifest.json")
|
||||
// 简易 JSON 解析(不用 Gson,直接字符串提取)
|
||||
val sha256Field = "\"sha256\": \""
|
||||
val sha256Start = manifestJson.indexOf(sha256Field) + sha256Field.length
|
||||
val sha256End = manifestJson.indexOf("\"", sha256Start)
|
||||
manifestSha256 = manifestJson.substring(sha256Start, sha256End)
|
||||
|
||||
appendLog("[3/4] 下载 APK...")
|
||||
val urlField = "\"url\": \""
|
||||
val urlStart = manifestJson.indexOf(urlField) + urlField.length
|
||||
val urlEnd = manifestJson.indexOf("\"", urlStart)
|
||||
manifestApkUrl = manifestJson.substring(urlStart, urlEnd)
|
||||
|
||||
appendLog(" SHA256: ${manifestSha256.take(16)}...")
|
||||
appendLog(" APK URL: ${manifestApkUrl.take(50)}...")
|
||||
appendLog(" 需要更新 ✅")
|
||||
} catch (e: Exception) {
|
||||
appendLog(" ⚠️ manifest 解析失败: ${e.message}")
|
||||
appendLog("=== OTA 测试中止 ===")
|
||||
return@withContext
|
||||
}
|
||||
|
||||
appendLog("[3/4] 下载 APK: ${manifestApkUrl.take(40)}...")
|
||||
val apkFile = File(cacheDir, "test-update.apk")
|
||||
try {
|
||||
val apkUrl = "https://git.childish-ghost.com/LukeMackin/Yuzu-GCA/releases/download/v0.1.0-dav-android-1/app-debug.apk"
|
||||
downloadFile(apkUrl, apkFile)
|
||||
appendLog(" 下载完成: ${apkFile.length()} bytes")
|
||||
downloadFile(manifestApkUrl, apkFile)
|
||||
appendLog(" 下载完成: ${apkFile.length()} bytes ✅")
|
||||
} catch (e: Exception) {
|
||||
appendLog(" ⚠️ APK未上传: ${e.message}")
|
||||
appendLog(" 但下载+校验代码路径已验证")
|
||||
appendLog(" ❌ 下载失败: ${e.message}")
|
||||
return@withContext
|
||||
}
|
||||
|
||||
appendLog("[4/4] SHA256 校验...")
|
||||
if (apkFile.exists()) {
|
||||
val sha256 = computeSha256(apkFile)
|
||||
appendLog(" SHA256: $sha256")
|
||||
appendLog(" ✅ 校验通过(原始字节计算,与Kotlin MessageDigest一致)")
|
||||
val actualSha256 = computeSha256(apkFile)
|
||||
if (actualSha256 == manifestSha256) {
|
||||
appendLog(" ✅ SHA256 校验通过!")
|
||||
appendLog(" ${actualSha256.take(32)}...")
|
||||
} else {
|
||||
appendLog(" ❌ SHA256 校验失败!")
|
||||
appendLog(" 期望: ${manifestSha256.take(32)}...")
|
||||
appendLog(" 实际: ${actualSha256.take(32)}...")
|
||||
}
|
||||
|
||||
appendLog("=== OTA 测试完成 ===")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Yuzu GCA</string>
|
||||
<string name="app_name">Yuzu GCA v0.1.0</string>
|
||||
</resources>
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
"builds": {
|
||||
"android": {
|
||||
"type": "apk",
|
||||
"tag": "v0.1.0-dav-android-1",
|
||||
"url": "https://git.childish-ghost.com/LukeMackin/Yuzu-GCA/releases/download/v0.1.0-dav-android-1/gca-0.1.0.apk",
|
||||
"sha256": "PLACEHOLDER",
|
||||
"size": 0,
|
||||
"tag": "v0.1.0-dav-android-6",
|
||||
"url": "https://git.childish-ghost.com/LukeMackin/Yuzu-GCA/releases/download/v0.1.0-dav-android-6/gca-v0.1.0.apk",
|
||||
"sha256": "6cd6bbfe07e8b86b06bc328b7b08138dfb00defc71c8bcc98fbb256b2153aba6",
|
||||
"size": 3223411,
|
||||
"minVersion": "0.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
package dev.yuzu.gca
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageInstaller
|
||||
import android.content.pm.PackageManager
|
||||
import expo.modules.kotlin.modules.Module
|
||||
import expo.modules.kotlin.modules.ModuleDefinition
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.security.DigestInputStream
|
||||
import java.security.MessageDigest
|
||||
|
||||
class PackageInstallerModule : Module() {
|
||||
override fun definition() = ModuleDefinition {
|
||||
Name("PackageInstallerModule")
|
||||
|
||||
AsyncFunction("installApk") { filePath: String, expectedSha256: String ->
|
||||
installApk(filePath, expectedSha256)
|
||||
}
|
||||
}
|
||||
|
||||
private fun installApk(filePath: String, expectedSha256: String) {
|
||||
val context: Context = appContext.reactContext ?: return
|
||||
val apkFile = File(filePath)
|
||||
if (!apkFile.exists()) throw Exception("安装包不存在")
|
||||
|
||||
if (!context.packageManager.canRequestPackageInstalls()) {
|
||||
throw SecurityException("REQUEST_INSTALL_PACKAGES 权限未授予")
|
||||
}
|
||||
|
||||
val packageInstaller = context.packageManager.packageInstaller
|
||||
val sessionParams = PackageInstaller.SessionParams(
|
||||
PackageInstaller.SessionParams.MODE_FULL_INSTALL
|
||||
)
|
||||
|
||||
val sessionId = packageInstaller.createSession(sessionParams)
|
||||
val session = packageInstaller.openSession(sessionId)
|
||||
|
||||
try {
|
||||
// 边写入边计算 SHA256(原子化,避免 TOCTOU)
|
||||
val sha256 = MessageDigest.getInstance("SHA-256")
|
||||
FileInputStream(apkFile).use { input ->
|
||||
val digestStream = DigestInputStream(input, sha256)
|
||||
session.openWrite("package", 0, apkFile.length()).use { output ->
|
||||
digestStream.copyTo(output)
|
||||
session.fsync(output)
|
||||
}
|
||||
}
|
||||
|
||||
val actualSha256 = sha256.digest().joinToString("") { "%02x".format(it) }
|
||||
if (actualSha256 != expectedSha256) {
|
||||
throw SecurityException("SHA256校验失败")
|
||||
}
|
||||
|
||||
val launchIntent = context.packageManager.getLaunchIntentForPackage(context.packageName)
|
||||
?: throw Exception("无法获取启动Intent")
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
context, 0, launchIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
|
||||
session.commit(pendingIntent.intentSender)
|
||||
} finally {
|
||||
session.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"platform": "android",
|
||||
"modules": ["PackageInstallerModule"]
|
||||
}
|
||||
Reference in New Issue
Block a user