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" }
}

View File

@@ -1,43 +1,4 @@
var React = require("react");
var View = require("react-native").View;
var Text = require("react-native").Text;
var AppRegistry = require("react-native").AppRegistry;
var Updates = require("expo-updates");
import { registerRootComponent } from 'expo';
import App from './App';
function App() {
var [msg, setMsg] = React.useState("checking...");
React.useEffect(function() {
async function check() {
try {
var rv = Updates.runtimeVersion || "empty";
var ch = Updates.channel || "default";
var id = Updates.updateId || "none";
var emb = Updates.isEmbeddedLaunch ? "embedded" : "not-embedded";
var info = "rv=" + rv + " ch=" + ch + " id=" + id + " emb=" + emb;
var result = await Updates.checkForUpdateAsync();
if (result.isAvailable) {
info += "\nUPDATE AVAILABLE! downloading...";
await Updates.fetchUpdateAsync();
info += "\ndownloaded. Restart app.";
await Updates.reloadAsync();
} else {
info += "\nno update available";
}
setMsg(info);
} catch(e) {
setMsg("Error: " + e.message);
}
}
check();
}, []);
return React.createElement(View, {style: {flex:1,backgroundColor:"#0f172a",alignItems:"center",justifyContent:"center"}},
React.createElement(Text, {style: {fontSize:28,fontWeight:"800",color:"#e2e8f0"}}, "Yuzu GCA"),
React.createElement(Text, {style: {fontSize:14,color:"#64748b",marginTop:8}}, "v0.1.0-dav-android-10"),
React.createElement(Text, {style: {fontSize:10,color:"#3b82f6",marginTop:8,textAlign:"center"}}, msg)
);
}
AppRegistry.registerComponent("main", function() { return App; });
registerRootComponent(App);

View File

@@ -1,27 +1,27 @@
{
"name": "@yuzu-gca/client-android",
"version": "0.1.0",
"private": true,
"main": "expo-router/entry",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"build": "eas build -p android --profile preview",
"export": "expo export --platform android",
"prebuild": "expo prebuild --platform android"
},
"dependencies": {
"@yuzu-gca/shared": "workspace:*",
"expo": "~52.0.0",
"expo-constants": "~17.0.0",
"expo-file-system": "~18.0.0",
"expo-status-bar": "~2.0.0",
"expo-updates": "~26.0.0",
"react": "18.3.1",
"react-native": "0.76.5"
},
"devDependencies": {
"@types/react": "~18.3.0",
"typescript": "^5.5.0"
}
{
"name": "@yuzu-gca/client-android",
"version": "0.1.0",
"private": true,
"main": "index.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"build": "eas build -p android --profile preview",
"export": "expo export --platform android",
"prebuild": "expo prebuild --platform android"
},
"dependencies": {
"@yuzu-gca/shared": "workspace:*",
"expo": "~52.0.0",
"expo-constants": "~17.0.0",
"expo-file-system": "~18.0.0",
"expo-status-bar": "~2.0.0",
"expo-updates": "~26.0.0",
"react": "18.3.1",
"react-native": "0.76.5"
},
"devDependencies": {
"@types/react": "~18.3.0",
"typescript": "^5.5.0"
}
}