fix: 审查修复—cleartext+package.json main+dead HTML+comment

This commit is contained in:
LukeMackin
2026-07-22 18:21:42 +08:00
parent ce7204b016
commit 09fde0501f
7 changed files with 40 additions and 226 deletions

View File

@@ -4,7 +4,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<application
<application android:usesCleartextTraffic="true"
android:label="Yuzu GCA v0.1.0"
android:allowBackup="false"
android:supportsRtl="true"

View File

@@ -1,48 +0,0 @@
<!DOCTYPE html>
<html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width">
<style>
body{font-family:monospace;background:#0f172a;color:#e2e8f0;padding:16px;margin:0}
h1{font-size:18px;color:#38bdf8}
.btn{display:block;width:100%;padding:14px;margin:8px 0;border:none;border-radius:8px;font-size:16px;cursor:pointer}
.btn-check{background:#1e40af;color:#fff}
.btn-download{background:#15803d;color:#fff}
.log{background:#1e293b;padding:12px;border-radius:8px;margin-top:12px;max-height:300px;overflow-y:auto;font-size:12px;white-space:pre-wrap}
.info{color:#94a3b8;font-size:12px;margin:4px 0}
</style></head><body>
<h1>Yuzu GCA</h1>
<div class="info">version <span id="ver">v0.1.0-dav-android-5</span></div>
<button class="btn btn-check" onclick="checkUpdate()">检查更新</button>
<button class="btn btn-download" onclick="applyUpdate()" id="btnApply" disabled>下载更新</button>
<div id="apkLink" style="display:none;margin-top:8px;font-size:12px;color:#38bdf8"></div>
<div class="log" id="log"></div>
<script>
var dlUrl='',dlVer='',dlSha='';
var channel=localStorage.getItem('gca_channel')||'stable';
function log(m){var l=document.getElementById('log');l.innerText+='[V5]'+m+'\n';l.scrollTop=l.scrollHeight}
function checkUpdate(){
log('channel: '+channel);
var manifestUrl=Android.fetchText('version.txt').trim();
if(!manifestUrl.startsWith('http')){log('ERR: invalid version.txt');return}
var j=JSON.parse(Android.fetchManifest(channel));
if(j.error){log('ERR: '+j.error);return}
var h5=j.h5;log('latest: '+h5.latest+' min: '+h5.min_version);
var apk=j.apk;if(apk){var m=(document.getElementById('ver').innerText||'').match(/(\d+)$/);if(m){var curCode=parseInt(m[0]);if(apk.versionCode>curCode){log('APK UPDATE:v'+apk.version);document.getElementById('apkLink').innerHTML='<a href=\"'+apk.url+'\">Download APK v'+apk.version+'</a>';document.getElementById('apkLink').style.display='block'}}}
var v=(h5.min_version||'').replace(/[^0-9]/g,''),my=(document.getElementById('ver').innerText||'').replace(/[^0-9]/g,'');
if(parseInt(my||'0')<parseInt(v||'0')){log('ERR: APK too old (need v'+h5.min_version+')');return}
dlUrl=h5.url;dlSha=h5.sha256||'';
if(!dlSha||/^(0{64}|TBD)/.test(dlSha)){log('ERR: channel no build');return}
if(h5.latest!==document.getElementById('ver').innerText&&h5.latest!=='v'+document.getElementById('ver').innerText){
dlVer=h5.latest;document.getElementById('btnApply').disabled=false;log('*** NEW VERSION ***')}
else log('up to date')
}
function applyUpdate(){
log('downloading...');
var p=dlUrl.split('/h5/');var rel=p.length>1?'h5/'+p[1]:dlUrl;
var t=Android.fetchText(rel);
if(typeof t!=='string'||t.startsWith('ERROR:')){log('ERR: '+t);return}
var h=Android.sha256(t);
if(h!==dlSha){log('SHA256 MISMATCH!');return}
Android.saveCache(t);log('OK! restart app to apply')
}
</script></body></html>

View File

@@ -1,46 +0,0 @@
<!DOCTYPE html>
<html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width">
<style>
body{font-family:monospace;background:#0f172a;color:#e2e8f0;padding:16px;margin:0}
h1{font-size:18px;color:#38bdf8}
.btn{display:block;width:100%;padding:14px;margin:8px 0;border:none;border-radius:8px;font-size:16px;cursor:pointer}
.btn-check{background:#1e40af;color:#fff}
.btn-download{background:#15803d;color:#fff}
.log{background:#1e293b;padding:12px;border-radius:8px;margin-top:12px;max-height:300px;overflow-y:auto;font-size:12px;white-space:pre-wrap}
.info{color:#94a3b8;font-size:12px;margin:4px 0}
</style></head><body>
<h1>Yuzu GCA</h1>
<div class="info">version <span id="ver">v0.1.0-dav-android-5</span></div>
<button class="btn btn-check" onclick="checkUpdate()">检查更新</button>
<button class="btn btn-download" onclick="applyUpdate()" id="btnApply" disabled>下载更新</button>
<div class="log" id="log"></div>
<script>
var dlUrl='',dlVer='',dlSha='';
var channel=localStorage.getItem('gca_channel')||'stable';
function log(m){document.getElementById('log').innerText+=m+'\n'}
function checkUpdate(){
log('channel: '+channel);
var manifestUrl=Android.fetchText('version.txt').trim();
if(!manifestUrl.startsWith('http')){log('ERR: invalid version.txt');return}
var j=JSON.parse(Android.fetchManifest(channel));
if(j.error){log('ERR: '+j.error);return}
var h5=j.h5;log('latest: '+h5.latest+' min: '+h5.min_version);
var v=(h5.min_version||'').replace(/[^0-9]/g,''),my=(document.getElementById('ver').innerText||'').replace(/[^0-9]/g,'');
if(parseInt(my||'0')<parseInt(v||'0')){log('ERR: APK too old (need v'+h5.min_version+')');return}
dlUrl=h5.url;dlSha=h5.sha256||'';
if(!dlSha||/^0{64}$/.test(dlSha)){log('ERR: channel no build');return}
if(h5.latest!==document.getElementById('ver').innerText&&h5.latest!=='v'+document.getElementById('ver').innerText){
dlVer=h5.latest;document.getElementById('btnApply').disabled=false;log('*** NEW VERSION ***')}
else log('up to date')
}
function applyUpdate(){
log('downloading...');
var p=dlUrl.split('/h5/');var rel=p.length>1?'h5/'+p[1]:dlUrl;
var t=Android.fetchText(rel);
if(typeof t!=='string'||t.startsWith('ERROR:')){log('ERR: '+t);return}
var h=Android.sha256(t);
if(h!==dlSha){log('SHA256 MISMATCH!');return}
Android.saveCache(t);log('OK! restart app to apply')
}
</script></body></html>

View File

@@ -1,76 +1,23 @@
package dev.yuzu.gca
import android.os.Bundle
import android.util.Log
import android.webkit.WebView
import androidx.appcompat.app.AppCompatActivity
import java.io.File
import java.net.HttpURLConnection
import java.net.URL
/**
* Minimal MainActivity 鈥?loads React Native dev server.
* Run `npx expo start ` and APK connects to it.
* No OTA, no Bridge, no complexity.
*/
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val cacheFile = File(filesDir, "gca_hot.html")
val wv = WebView(this).apply {
WebView(this).apply {
settings.javaScriptEnabled = true
settings.domStorageEnabled = true
settings.allowFileAccess = true
addJavascriptInterface(Bridge(this@MainActivity), "Android")
if (cacheFile.exists()) {
val html = cacheFile.readText()
loadDataWithBaseURL("file:///android_asset/", html, "text/html", "UTF-8", null)
Log.i(TAG, "Loaded cached HTML (${cacheFile.length()}B)")
} else {
val html = assets.open("frame.html").bufferedReader().readText()
loadDataWithBaseURL("file:///android_asset/", html, "text/html", "utf-8", null)
Log.i(TAG, "Loaded frame.html (${html.length}B)")
}
}
setContentView(wv)
// Load Expo dev server on local network
// Change to your PC's LAN IP (e.g. 192.168.x.x)
loadUrl("http://10.0.2.2:8081")
}.also { setContentView(it) }
}
inner class Bridge(private val ctx: MainActivity) {
@android.webkit.JavascriptInterface
fun saveCache(html: String) {
if (html.length > 500_000) { Log.w(TAG, "Cache too large"); return }
File(ctx.filesDir, "gca_hot.html").writeText(html)
Log.i(TAG, "Saved ${html.length}B")
}
@android.webkit.JavascriptInterface
fun clearCache() { File(ctx.filesDir, "gca_hot.html").delete() }
@android.webkit.JavascriptInterface
fun sha256(input: String): String {
val md = java.security.MessageDigest.getInstance("SHA-256")
return md.digest(input.toByteArray(Charsets.UTF_8)).joinToString("") { "%02x".format(it) }
}
@android.webkit.JavascriptInterface
fun fetchManifest(channel: String): String {
val url = "https://git.childish-ghost.com/LukeMackin/Yuzu-GCA/raw/branch/main/ota/manifest.json"
return try {
val conn = URL(url).openConnection() as HttpURLConnection
conn.connectTimeout = 10000; conn.readTimeout = 10000
val raw = conn.inputStream.bufferedReader().readText()
org.json.JSONObject(raw).getJSONObject(channel).toString()
} catch (e: Exception) { """{"error":"${e.message}"}""" }
}
@android.webkit.JavascriptInterface
fun fetchText(path: String): String {
val safe = path.replace("..", "").replace("..", "").replace("\\", "/")
val url = "https://git.childish-ghost.com/LukeMackin/Yuzu-GCA/raw/branch/main/ota/$safe"
return try {
URL(url).openConnection().apply { connectTimeout = 10000; readTimeout = 10000 }
.getInputStream().bufferedReader().readText()
} catch (e: Exception) { "ERROR: ${e.message}" }
}
}
companion object { private const val TAG = "GCA" }
}