diff --git a/ota/expo-manifest.json b/ota/expo-manifest.json
deleted file mode 100644
index e4e96fd..0000000
--- a/ota/expo-manifest.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "id": "ca582f57-9d65-4831-9d8a-1df3939264c7",
- "assets": [
-
- ],
- "createdAt": "2026-07-20T15:48:27.579Z",
- "metadata": {
-
- },
- "runtimeVersion": "0.1.0",
- "launchAsset": {
- "contentType": "application/javascript",
- "key": "bundle",
- "url": "https://git.childish-ghost.com/LukeMackin/Yuzu-GCA/releases/download/v0.1.0-dav-android-10/index.android.bundle"
- }
-}
\ No newline at end of file
diff --git a/packages/client-android/android/app/src/main/assets/index.html b/packages/client-android/android/app/src/main/assets/index.html
new file mode 100644
index 0000000..33db4d8
--- /dev/null
+++ b/packages/client-android/android/app/src/main/assets/index.html
@@ -0,0 +1,47 @@
+
+
+
+Yuzu GCA
+version v0.1.0-dav-android-8
+
+
+
+
diff --git a/packages/client-android/android/app/src/main/java/dev/yuzu/gca/MainActivity.kt b/packages/client-android/android/app/src/main/java/dev/yuzu/gca/MainActivity.kt
index 6f9fecd..191e716 100644
--- a/packages/client-android/android/app/src/main/java/dev/yuzu/gca/MainActivity.kt
+++ b/packages/client-android/android/app/src/main/java/dev/yuzu/gca/MainActivity.kt
@@ -7,6 +7,8 @@ import android.graphics.Color
import android.net.Uri
import android.os.Bundle
import android.provider.Settings
+import android.webkit.WebView
+import android.webkit.WebViewClient
import android.widget.Button
import android.widget.LinearLayout
import android.widget.ScrollView
@@ -29,87 +31,30 @@ class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- val scroll = ScrollView(this)
- val layout = LinearLayout(this).apply {
- orientation = LinearLayout.VERTICAL
- setPadding(24, 24, 24, 24)
- }
-
- val title = TextView(this).apply {
- text = "Yuzu GCA"
- textSize = 20f
- setPadding(0, 0, 0, 4)
- }
- layout.addView(title)
-
- val versionInfo = TextView(this).apply {
- text = "v0.1.0-dav-android-8-debug"
- textSize = 12f
- setTextColor(Color.GRAY)
- setPadding(0, 0, 0, 12)
- }
- layout.addView(versionInfo)
-
- val channelLabel = TextView(this).apply {
- text = "选择更新渠道:"
- textSize = 13f
- setPadding(0, 8, 0, 4)
- }
- layout.addView(channelLabel)
-
- // 渠道按钮行
- val channelRow = LinearLayout(this).apply {
- orientation = LinearLayout.HORIZONTAL
- setPadding(0, 0, 0, 12)
- }
-
- fun makeChannelBtn(label: String, channel: String): Button {
- return Button(this).apply {
- text = label
- textSize = 12f
- setOnClickListener {
- selectedChannel = channel
- highlightChannel(channelRow, this)
+ val wv = WebView(this).apply {
+ settings.javaScriptEnabled = true
+ settings.domStorageEnabled = true
+ settings.allowFileAccess = true
+ webViewClient = object : WebViewClient() {
+ override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
+ if (url.startsWith("http")) { view.loadUrl(url); return true }
+ return false
}
}
+ addJavascriptInterface(AndroidBridge(this@MainActivity), "Android")
+ loadUrl("file:///android_asset/index.html")
}
-
- val btnStable = makeChannelBtn("Stable", "stable")
- val btnBeta = makeChannelBtn("Beta", "beta")
- val btnNightly = makeChannelBtn("Nightly", "nightly")
-
- channelRow.addView(btnStable)
- channelRow.addView(btnBeta)
- channelRow.addView(btnNightly)
- layout.addView(channelRow)
- highlightChannel(channelRow, btnStable)
-
- logView = TextView(this).apply {
- textSize = 12f
- setTextColor(Color.LTGRAY)
- }
- layout.addView(logView)
-
- val btn = Button(this).apply {
- text = "检查更新"
- setOnClickListener {
- val ch = selectedChannel
- CoroutineScope(Dispatchers.IO).launch { doOtaTest(ch) }
- }
- }
- layout.addView(btn)
-
- scroll.addView(layout)
- setContentView(scroll)
-
- appendLog("渠道: stable | 点击[检查更新]开始")
+ setContentView(wv)
+ appendLog("WebView 热更新模式启动")
}
- private fun highlightChannel(row: LinearLayout, active: Button) {
- for (i in 0 until row.childCount) {
- val b = row.getChildAt(i) as Button
- b.isSelected = (b == active)
+ inner class AndroidBridge(private val ctx: MainActivity) {
+ @android.webkit.JavascriptInterface
+ fun checkUpdate(channel: String) {
+ CoroutineScope(Dispatchers.IO).launch { doOtaTest(channel) }
}
+ @android.webkit.JavascriptInterface
+ fun log(msg: String) { appendLog(msg) }
}
private fun appendLog(msg: String) {
diff --git a/packages/client-android/index.js b/packages/client-android/index.js
new file mode 100644
index 0000000..9f552f6
--- /dev/null
+++ b/packages/client-android/index.js
@@ -0,0 +1,43 @@
+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");
+
+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; });