From c1a9e40d0aa79c9d6f0d8f2a2c882ef8060bfc8e Mon Sep 17 00:00:00 2001 From: LukeMackin Date: Wed, 22 Jul 2026 21:32:40 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=A6=BB=E7=BA=BFAPK=E2=80=94Android?= =?UTF-8?q?=20Bridge=E6=9C=AC=E5=9C=B0=E6=89=A7=E8=A1=8C(exec/listFiles/sy?= =?UTF-8?q?sinfo)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../android/app/src/main/assets/index.html | 73 ++++++++---------- .../main/java/dev/yuzu/gca/MainActivity.kt | 41 ++++++++++ s.png | Bin 0 -> 37874 bytes 3 files changed, 74 insertions(+), 40 deletions(-) create mode 100644 s.png diff --git a/packages/client-android/android/app/src/main/assets/index.html b/packages/client-android/android/app/src/main/assets/index.html index 6f41757..4bc6d98 100644 --- a/packages/client-android/android/app/src/main/assets/index.html +++ b/packages/client-android/android/app/src/main/assets/index.html @@ -11,48 +11,48 @@ h1{font-size:20px;color:#38bdf8;margin-bottom:4px} .btn{display:block;width:100%;padding:14px;margin:8px 0;border:none;border-radius:8px;font-size:15px;cursor:pointer;color:#fff} .btn-blue{background:#1e40af} .btn-green{background:#15803d} -.btn-red{background:#991b1b} .input{display:block;width:100%;padding:12px;margin:8px 0;border:none;border-radius:8px;font-size:14px;background:#0f172a;color:#e2e8f0} -.log{background:#0a0f1a;padding:12px;border-radius:8px;margin-top:8px;max-height:200px;overflow-y:auto;font-size:11px;white-space:pre-wrap;color:#22c55e;font-family:monospace} +.log{background:#0a0f1a;padding:12px;border-radius:8px;margin-top:8px;max-height:250px;overflow-y:auto;font-size:11px;white-space:pre-wrap;color:#22c55e;font-family:monospace} .tab{display:inline-block;padding:8px 16px;margin-right:4px;border-radius:6px 6px 0 0;background:#1e293b;color:#64748b;font-size:13px;cursor:pointer} .tab-on{background:#1e40af;color:#fff}

Yuzu GCA

-
Self-contained MCP Client
+
Self-contained — no Gateway needed
- Exec - Files - SysInfo + Exec + Files + SysInfo
-
Execute Command (Gateway)
- - - +
Shell Command (local)
+ +
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 06a3b6c..48b4ebe 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 @@ -1,8 +1,12 @@ 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.io.BufferedReader +import java.io.InputStreamReader class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { @@ -11,7 +15,44 @@ class MainActivity : AppCompatActivity() { settings.javaScriptEnabled = true settings.domStorageEnabled = true settings.allowUniversalAccessFromFileURLs = true + addJavascriptInterface(LocalBridge(), "Android") loadUrl("file:///android_asset/index.html") }.also { setContentView(it) } } + + inner class LocalBridge { + @android.webkit.JavascriptInterface + fun exec(cmd: String): String { + return try { + val p = Runtime.getRuntime().exec(arrayOf("sh", "-c", cmd)) + val out = BufferedReader(InputStreamReader(p.inputStream)).readText() + val err = BufferedReader(InputStreamReader(p.errorStream)).readText() + p.waitFor() + if (out.isNotEmpty()) out else err + } catch (e: Exception) { "ERR: ${e.message}" } + } + + @android.webkit.JavascriptInterface + fun listFiles(path: String): String { + return try { + val dir = File(path) + if (!dir.exists()) return "ERR: path not found" + if (!dir.isDirectory) return "ERR: not a directory" + dir.listFiles()?.joinToString("\n") { f -> + (if (f.isDirectory) "[DIR] " else "[FILE] ") + f.name + + (if (f.isFile) " (${f.length()}B)" else "") + } ?: "empty" + } catch (e: Exception) { "ERR: ${e.message}" } + } + + @android.webkit.JavascriptInterface + fun sysinfo(): String { + return """ + Device: ${android.os.Build.MODEL} + Android: ${android.os.Build.VERSION.RELEASE} (SDK ${android.os.Build.VERSION.SDK_INT}) + CPU: ${Runtime.getRuntime().availableProcessors()} cores + Memory: ${Runtime.getRuntime().maxMemory() / 1024 / 1024}MB max + """.trimIndent() + } + } } diff --git a/s.png b/s.png new file mode 100644 index 0000000000000000000000000000000000000000..c3e8d8536aeb1bca658c27cfdd5c391cb7b337b8 GIT binary patch literal 37874 zcmeI5PiquG6vS&|Sk_=b!T)&J1oRS6@(D~fF>x~Jrpdko;^0s}|Z#wa^ zw;W{n^|G_uJu}%%P4%nlH@&mt+>9_M;+>OtFNn6zj1r1Gfi8o z;{AaxwKLQv`X1XKYZ3KQOMNx$C9TJrMf}AtnhlR%@p>_=pU3vg_^ka{f7SLZbyt&U zo$J9+|J`2KV)>^17uWjPT3*CfSxS9Bz1J(T?zIkL@4oIawFX70AOR;}0g=Hmnpi+A zfCa*k zqK2p;YKR)5hNvNGh#I1Xs3B_jf2m=qlRjNP=J(9yne_X58_P3&h^6P*yOg>e&wMYP z>XFWSCE91&y{UbT`tzIGPR#7TRlg_N`sdpLr9M=jlh3CaGLSR|u!R1W0fbm$CJ-l4 zoa_&%sf1I&DP(WJDI^nc<`PZ;r;t&Bv8izwqf@XGcJ{<&9?yPP0M}i{c+7cZ9Gh{# z`56UrM8<#|kx{_;;ructkRu{TWG3Jgi~&x;DBu)u3g!V$AyFVlWG3Jga0=NU$d}+0 zk_k8koI+;;IR$bG<^oQ^C~!5&DBu)u3g&^M0&{)2`jAYxE2nT2tXee-y^$^!jNMV> z-;+O}F0TTmepqU9M4r9~o4_sHq zDVPa31!KSrG^4=Sl(DIKfKx~m$YPrbI0c+S_6KqbIE7>aP64OTnLti~oPxQ4Q!ol# zeJ~2Eox-97g)krB$>cigt16x0%H-zBIW{4 z!6=N=PnP;4&hMTNu$8@bwtYABXF6&voPEPE&a>Z6Fvr$lzysrfvp?X0$Kiq3Ld%}& dUEC|v8g}~&9?ST3j}sk6>muf=l)9-6{RJs8dH4VT literal 0 HcmV?d00001