简体中文 · English
An on-device AI assistant for Android. It calls device capabilities directly from the conversation — files, media store, calendar, Bluetooth/Wi-Fi, sensors, NFC, screen, shell, databases — and renders the results inline as rich cards: charts, images, maps, audio/video, tables, and widget previews.
- Frontend:
web/— React 19 +@assistant-ui/react+@mariozechner/pi-ai, bundled with Vite and shipped inside the APK for offline use. - Native layer:
app/— a Kotlin full-screen WebView shell, the tool registry, and permission / system-API bridging. - Model: any OpenAI-compatible endpoint. Requests are proxied through native OkHttp (to bypass WebView CORS) and streamed back over SSE; a single turn allows up to 200 tool rounds.
- Version:
0.28.7(versionCode 44),applicationId = com.dc.andagent.app/build.gradle.ktsis authoritative.
User ⇄ WebView (React + assistant-ui + pi-ai)
│ AndroidBridge (JS ⇄ Kotlin)
├─ llmFetchStart/llmCancel → OkHttp → OpenAI-compatible endpoint (SSE stream back)
└─ toolExec(name, argsJson) → Kotlin tool registry → system APIs / ADB / Termux / QuickJS
The WebView loads https://appassets.androidplatform.net/assets/web/index.html through WebViewAssetLoader: /assets/** maps to APK assets and /local/** is a read-only mapping to the app's external files directory (media playback, with Range/206 support and path-traversal protection). All JS/CSS/wasm is local — no CDN — but LLM requests and map cards still require network access.
MainActivity.kt registers the native tool catalog. Each implements the Tool interface in tools/Tool.kt (JSON Schema parameters plus a destructive marker):
| Domain | Count | Representative tools |
|---|---|---|
| Device / system info | 31 | get_time, device_info, battery_detail, clipboard_read/write, notify_send, app_list, open_uri, telephony_info, audio_state, torch, hotspot |
| Files / media store / image editing | 28 | file_list/read/write/search/download, file_copy/move/delete, album_photos, image_edit/preview/collage, saf_tree_*, storage_scan |
| Calendar / alarms / timers / cron | 14 | calendar_list/events/add/delete, alarm_set, timer_set, countdown, cron_create/list/toggle/delete/run_now |
| Calls / location / recording / contacts / SMS | 8 | phone_call, sms_send/read, location_get, mic_record, contacts_search, provider_query |
| Cellular signal / data quota | 4 | cell_info (RSRP/RSRQ/SS-RS* per SIM, LTE real bandwidth, NR bands), cell_scan (requestNetworkScan, needs a system permission — degrades honestly), nr_bandwidth (NR-ARFCN→MHz, FR1/FR2, SA/NSA), data_policy (Data Saver, NetworkPolicyManager probe, NetworkStatsManager usage, quota projection) |
| Bluetooth / Wi-Fi | 11 | bt_scan/connect/toggle, wifi_connect/saved_list/forget/toggle, wifi_info |
| Local VPN (VpnService, no root) | 7 | vpn_start/stop/status (DNS-only tun, one system consent), vpn_split (per-app allow/deny list), vpn_hosts (hosts-level ad blocking, remote list import), vpn_traffic (per-app usage via NetworkStatsManager), vpn_diag (DNS log, blocked ranking, pcap export) |
| Sensors / health | 4 | sensor_list/read, steps_today/history |
| Screen capture / recording (MediaProjection) | 6 | screen_capture, screen_record, screen_record_stop, screen_record_list, screen_record_h264, screenshot_quick |
| Termux / shell | 4 | termux_setup, shell_exec, pkg_install, apt_status |
| QuickJS scripting | 8 | js_eval, script_save/run/list/..., json_fix |
| SQLite | 8 | db_create/exec/query/..., db_gui |
| Document generation | 3 | docx_create, pptx_create, xlsx_create |
| Home-screen widgets | 2 | widget_generate, widget_save_and_pin |
| NFC | 16 | nfc_status/read/write_text/write_uri/write_raw/hce_status (NDEF+HCE), nfc_mf_dump, nfc_mf_write_block, nfc_ul_read, nfc_ul_write_page, nfc_keys_manage (MIFARE Classic/Ultralight deep read-write), nfc_iso_dep_info, nfc_iso_dep_transceive (ISO-DEP APDUs: transit/access/DESFire/eMRTD, privacy-gated), nfc_felica_info, nfc_felica_transceive, nfc_felica_read_blocks (FeliCa: Suica/PASMO style cards) |
| Embedded web page in chat | 1 | web_embed |
| ZIP archives | 5 | zip_create/list/extract/add/test |
| Email (SMTP) | 3 | email_send, email_verify, email_draft |
| F-Droid / APK install | 3 | fdroid_search, fdroid_download, app_install |
| Web search / read | 2 | web_search, web_read |
| Intents | 3 | intent_send, intent_resolve, intent_broadcast |
| WebView DevTools | 5 | devtools_status/debugging/eval/console/screenshot |
| Layered memory / LiveLoop | — | Core and archived memory, continuous background work |
| Camera (capture / manual / depth) | 9 | camera_capture (manual ISO/shutter/EV/focus, ultrawide/tele/macro lens switch, optical zoom ratio), camera_raw (RAW_SENSOR → DNG + Bayer preview), camera_depth (DEPTH16 PNG/colormap/PLY point cloud), camera_stream (ImageAnalysis luma/motion/QR frame analytics), camera_extension (OEM night/HDR/bokeh), camera_record, camera_scan, camera_info, camera_overlay_* |
| Camera overlay / scheduled work | — | Floating preview, chat camera card, cron management |
Other features:
- Rich rendering: Markdown fenced blocks dispatch
chart(ECharts),photos,image,kv,geo,audio,video,camera, andwidgetcards, plus highlight.js syntax highlighting and GFM tables. - Script engine: QuickJS runs on a dedicated thread (64 MB memory limit, native interrupt-based timeout) and exposes the
andagent.tool/docx/pptx/xlsx/db/logJS API. Scripts live in the app's externalscripts/directory with path-traversal checks. - Home-screen widgets: declarative
WidgetSpec v2with STATIC / HTTP / SYSTEM / SCRIPT data sources and WorkManager-scheduled refreshes. - Database browser:
dbgui/provides a read-only SQL UI built on sql.js + Grid.js. - Document generation: hand-written OOXML in pure Kotlin for docx/pptx/xlsx.
- Layered memory: core memories stay in context; relevant archived memories are retrieved as needed.
- LiveLoop: agent work can continue across scheduled wakeups and resume with stored context.
- Scheduled tasks: cron tasks can be created, paused, enabled, and inspected in Settings.
- Camera overlay: camera preview can float over the conversation for multimodal tasks.
- Multi-session drawer, reasoning display, voice input, conversation export, dynamic permissions: implemented in the frontend and bridge layers.
| Item | Requirement |
|---|---|
| JDK | 17 |
| Android SDK / compileSdk | SDK 34 |
| NDK / CMake | 26.1.10909125 / 3.22.1 (native code in app/src/main/cpp) |
| Gradle | 8.9 (use the bundled ./gradlew; no installation needed) |
| Node.js | 20+; only needed to rebuild the frontend |
| Device | Android 8.0+ (minSdk 26); embedded Termux is arm64-v8a only |
Create local.properties at the repository root pointing to your Android SDK (already ignored by .gitignore):
sdk.dir=/Users/<you>/Library/Android/sdk./gradlew assembleDebug # output: app/build/outputs/apk/debug/app-debug.apk
adb install -r app/build/outputs/apk/debug/app-debug.apk
./gradlew test # JVM unit tests (JUnit4 + Robolectric)preBuild automatically runs buildWeb (npm ci + npm run build), writing output to app/src/main/assets/web and app/src/main/assets/dbgui. If npm is missing or installation/build fails, the task prints a message and exits with exit 0, falling back to the pre-built assets committed to the repository — so an Android-only environment can still produce an APK.
To work on the frontend separately:
cd web
npm ci
npm run dev # Vite dev server with a fake AndroidBridge
npm run build # esbuild bundles pi-ai + tsc --noEmit + Vite + dbgui + copy sql-wasm.wasmnpm run dev loads web/src/devMockBridge.ts (dev only; stripped from production builds) so the chat flow works on a PC: tools return placeholder JSON that you can override in localStorage["andagent.dev.mock.tools"], and LLM requests go straight from the browser to the configured endpoint (CORS must allow the dev origin). Native-only surfaces (secure KV, widgets, Termux, screen capture, SSH credentials) report "App only" instead of pretending to work. Set VITE_ANDAGENT_MOCK_BRIDGE=0 to preview without a bridge — then tool execution and the LLM proxy are unavailable and settings fall back to localStorage. Debuggable APKs can be inspected with chrome://inspect.
On first launch, if no model is configured, Connect a model opens automatically.
Choose a provider preset or a custom OpenAI-compatible endpoint, enter your key,
and fetch the model list or type a model ID. Test and save sends a short
request to verify tool calling (a small provider charge may apply). If listing
fails, enter the model ID manually and test again, or Save without testing.
Ollama and LM Studio allow an empty key; replace localhost with your computer's
LAN IP when connecting from the phone.
Choose Later to skip the first-run guide. The chat header's model button, the unconfigured banner, and Settings › Model configuration always reopen it. Existing valid configurations bypass onboarding. Provider presets include public endpoint addresses; selected model settings and API keys are saved only on the device. This setup flow follows Mika's model connection flow and the local iOS-Agent implementation.
Alternatively, put your settings in config.json in the phone's Download directory:
{
"baseUrl": "https://your-openai-compatible-endpoint/v1",
"apiKey": "your-api-key",
"model": "your-model-id"
}adb push config.json /sdcard/Download/config.json
# Android 11+ can deny direct reads of the public Downloads dir. Keep an
# identical copy in the app's private external files dir (no permission needed):
adb push config.json /sdcard/Android/data/com.dc.andagent/files/config.jsonSettingsStore looks for config.json on startup and whenever the WebView
reads settings: public Download/ (File API, then MediaStore), then the app's
private external files dir. systemPrompt is optional and overrides the
built-in prompt. Values from the file take precedence over
SharedPreferences; reopening Settings re-imports them. On Android 13+ the
public Download/ copy is usually unreadable without "All files access", so
after editing it, re-run the second adb push to sync the private copy (or
grant all-files access, which makes the public file authoritative). Keep
config.json out of version control — it holds a plaintext credential. If no
config is present, use the model setup guide instead;
values are stored only in local SharedPreferences, never in web assets.
If you switch to the in-app setup, update or remove an existing config file to
prevent it from overriding your saved settings on the next settings read.
| Field | Notes |
|---|---|
| Base URL | OpenAI-compatible endpoint; do not append /chat/completions |
| API Key | Read from Download/config.json, or entered in Settings |
| Model ID | Model identifier from your provider |
| System Prompt | Defines the assistant's identity and behavior; optional in the config file |
android:usesCleartextTraffic="true" also allows self-hosted http:// endpoints.
The manifest declares many permissions, but most are requested on demand via PermissionBroker when a tool is actually invoked; only media-read and microphone (RECORD_AUDIO) permissions are requested at startup. The voice-input button uses getUserMedia() inside the WebView, so WebChromeClient.onPermissionRequest bridges the WebView's audio-capture request to the same runtime grant. The destructive marker denotes write operations the model should confirm with the user first, and the frontend highlights those tool cards with a "write" badge.
| Capability | Prerequisite |
|---|---|
| Regular tools | The corresponding runtime permission (calendar, location, microphone, notifications, media, phone/SMS, etc.) |
| Screenshots / recording | Grant the system MediaProjection prompt once (Settings → Screen capture) |
| Wi-Fi / Bluetooth advanced control | Toggle from the system settings pages (no silent toggling) |
| Step history | Health Connect and its permission grant |
| NFC read / write | NFC-capable device; foreground dispatch, confirmation before writing |
External Termux RUN_COMMAND |
Termux installed and authorized; grant via adb shell pm grant com.dc.andagent com.termux.permission.RUN_COMMAND |
| Embedded Termux userland | arm64-v8a device; see below |
Caveats: targetSdk = 28 retains the legacy storage/permission model and is not suitable for Play Store distribution. The Release workflow produces an installable debug-signed APK; it has no production signing key or minification. CI runs web and Android builds and tests.
app/
build.gradle.kts # Android config, buildWeb / downloadBootstrap tasks
src/main/java/com/dc/andagent/
MainActivity.kt # WebView shell and native tool registry
bridge/ # AgentBridge / DbGuiBridge / PermissionBroker
tools/ # Tool implementations (30+ files)
script/ # QuickJS engine, JS API, js_eval
shell/ # Termux env/installer, memfd exec
widget/ # Widget spec, renderer, data sources, refresh scheduling
docs/ # Hand-written OOXML generators
ui/SettingsStore.kt # SharedPreferences settings
src/main/cpp/ # memexec / scriptinterrupt native code (CMake)
src/main/assets/
web/ # Committed pre-built frontend output
dbgui/ # Read-only SQLite browser (incl. sql-wasm.wasm)
bootstrap/ # Embedded Termux bootstrap-aarch64.zip
vendor/ # scrcpy-server + license
libs/README.md # QuickJS vendor jar / .so provenance and hashes
web/
src/ # App / agent (tool schemas) / bridge / cards
dbgui/ # Separate entry point for the database browser
scripts/build-pi.mjs # esbuild pre-bundles pi-ai → pi-bundle.js
scripts/rewrite_termux_bootstrap.py
ATTRIBUTION.md # Termux code adaptation and license notes
ROADMAP-TODO.md # Progress, backlog, and milestones
- Implement
Toolinapp/src/main/java/com/dc/andagent/tools/(name/description/parameters/destructive/execute). - Register it in the
tools.putAll(mapOf(...))block inMainActivity.kt. - Add the matching JSON Schema in
web/src/agent.ts— both sides must stay in sync, otherwise the model cannot see or call the tool. - If a new permission is required, update
AndroidManifest.xmland request it throughPermissionBroker.ensure(...).
Bridge protocol: JS → Kotlin via window.AndroidBridge.* (toolExec, llmFetchStart, getSettings, requestPermissions, ...); Kotlin → JS via a single JSON message to window.__andagent.receive(json), with event types such as llm.headers/chunk/chunks/done/error, tool.result, permission.result, and notice.
termux_setup installs app/src/main/assets/bootstrap/bootstrap-aarch64.zip into the
app-private files/usr directory. The archive was derived from the official bootstrap
by rewriting text file paths only: /data/data/com.termux →
/data/user/0/com.dc.andagent. ELF binaries are byte-for-byte unchanged.
Current archive SHA-256: 62a92f2f6c9433f721232b24f11be2d09b76149608122694364a6957f2d69809.
Original official archive SHA-256: 5a1429b6c68ee3bdc17b5fd6e47ba42933ec924cd7283fe74992574e1d5badae.
Installation validates dash and bash as the ready gate; pkg list-installed is a
soft probe reported in the backend status, so a broken apt/dpkg no longer disables
the shell backend. shell_exec tries the persistent bash session, direct exec,
memfd in order, then falls back to the Termux App bridge; each failure
carries the underlying stderr. ELF files still contain the official compile-time
paths, so TermuxEnv writes an APT_CONFIG file that redirects every apt
directory to the embedded prefix (including Acquire::https::CaInfo → the bundled
cert.pem and DPkg::Options --instdir/--admindir), sets DPKG_ADMINDIR and
SSL_CERT_FILE/CURL_CA_BUNDLE, and creates the apt/lists, apt/archives,
sources.list.d and solver directories the bootstrap archive omits. Because .deb
archives hardcode /data/data/com.termux/... install paths, dpkg unpacks through a
symlinked shadow dir (files/data/data/com.termux/files/usr → the embedded prefix).
apt update, pkg install and pkg files have been verified end to end on a device.
./gradlew downloadBootstrap verifies the official archive hash and runs
scripts/rewrite_termux_bootstrap.py. After updating the upstream version, refresh both
the official and derived hashes and re-check the residual-path list.
This maintenance task depends on GNU sha256sum, so macOS needs coreutils installed;
normal builds always package the checked-in archive and do not need this task.
The full porting record and residual path list are in /tmp/andagent-r5-run.md.
AndAgent is licensed under Apache-2.0. Third-party attribution:
THIRD-PARTY-NOTICES.md— consolidated component versions, licenses, sources, and copyright noticesATTRIBUTION.md— Termux code adaptation (MIT / Apache-2.0 / GPLv3 notes) and the derived bootstrapapp/libs/README.md— QuickJS Android wrapper 3.2.0 (Apache-2.0) and per-ABI SHA-256 hashesapp/src/main/assets/dbgui/THIRD_PARTY_LICENSES.txt— sql.js, Grid.jsapp/src/main/assets/vendor/scrcpy-LICENSE.txt— scrcpy server 2.7
Progress, backlog, and milestones are tracked in ROADMAP-TODO.md.
Star history chart will be added after the public release.
GitHub automatically lists contributors. Contributions are welcome; see CONTRIBUTING.md.











