feat(mobile): Android and web support (staging)
How this branch works
android/staging collects the work as cherry-picked commits from the android workspaces (all landed). The last three commits are the release tail: chore(mobile): Android release config and two fingerprint: commits (Android label + R8 rule, Expo web dependencies). Supersedes #9118.
What
apps/mobile runs on iOS, Android and web from one codebase. Each platform gets its most native UI; plain React Native is the fallback. iOS behaviour is unchanged.
- Rule (now in
apps/mobile/AGENTS.md): use an@expo/uiuniversal component or community drop-in first (SwiftUI on iOS, Jetpack Compose on Android, DOM on web); otherwiseX.ios.tsx/X.android.tsx/X.web.tsxbehind anX.types.tscontract;X.tsxis a React Native fallback that must work. - Android native: Google sign-in via Credential Manager, agent card as an ongoing notification (Kotlin), Material menus (
MenuView) for share access, file actions, composer mode and symbol buttons, Compose AlertDialog for the rename prompt, Material bottom sheet for attachments, header actions through atoolbarIconseam. - Web: Expo web target (react-native-web). Header buttons and menus through a
HeaderToolbarseam (36 screens), chat with token streaming and bottom pinning, diffs, Pages in an iframe, voice orb fallback, browser session cookie, terminal in an iframe, browser file chooser, browser WebRTC,backdrop-filterglass, in-app alert dialog, keyboard focus rings, reload-to-update gate. API accepts origins inMOBILE_WEB_ORIGINS(unset = none). - Web at
/agents: the web build is served atapp.superset.sh/agents, same-origin with the dashboard (shared sign-in cookie, no extra CORS). apps/web rewrites/agents/*in its proxy so the route keeps its own CSP; the sidebar and Home link to it, and/workspaces/[id]offers "Continue in browser". Off until set up: needs asuperset-agentsVercel project,VERCEL_AGENTS_PROJECT_IDandAGENTS_URL(production) secrets — steps indocs/agents-web.md. Without them the links stay hidden anddeploy-agentsskips. - Web on a computer: tab titles per screen; Escape closes sheets; sheets open as a dialog (desktop) or bottom sheet (narrow) over the screen they came from; Enter sends and Shift+Enter breaks the line; click-to-type terminal; search focuses on open and Return opens the top hit; trackpad scrolling in diffs; hover states; rows are real links (⌘-click / middle-click / copy link); workspace, chat and composer keep to a centred 720px column on wide screens; back after a deep link goes to the parent screen (all platforms).
- Android: choices (Branch, review comment Edit/Delete) open in a Compose bottom sheet; search sheet has a close button; empty Home no longer scrolls.
- Android composer: dictation (SpeechRecognizer; Web Speech on web), paste of images and files, PDF thumbnails, swipe-down; voice mode owns the Android audio route; update gate points to Google Play.
- All platforms: sent file attachments open in the platform viewer (QuickLook on iOS, ACTION_VIEW on Android, new tab on web).
- Shared fallback: the React Native composer is
Composer.tsx(Android, web, tests); iOS keeps the SwiftUI composer. - Dev setup and store:
scripts/android-dev.sh,.agents/skills/mobile-android-verification,play-store.config.js,scripts/play-listing.ts,plans/20261029-android-launch-checklist.md, manualdeploy-android.ymlworkflow.
⚠️ The last three commits start an iOS release
They change the iOS runtime fingerprint. Merging them makes deploy.yml build and submit a new iOS binary, and installed iOS builds stop taking OTA updates until they update. Drop all three to merge the Android and web code dark (web then needs its dependency commit back before it builds); every commit before them leaves the fingerprint unchanged.
Verified
- Tests: mobile 277, api 240, shared 1504, auth 88, all pass. Biome clean.
tsc: mobile 188 (pre-existing), api/shared 0.check:i18n: 0 missing in 16 locales. sherif clean. expo exportfor iOS, Android and web all succeed. iOS fingerprint unchanged by everything after the release tail.- iOS simulator, merged staging (review account, production): Home, workspace, native composer, attachments sheet, workspace "…" menu identical.
- Android emulator, merged staging: composer "+" Material sheet, system photo picker; signed-in header pass on the local stack (Home, workspace menu, files changed, jump to file, sessions, PR overflow and link, pages, version gate) all render and work.
- Web: production export boots; per-workspace verification on the local stack covers headers, chat streaming and scroll, Pages, attachments, uploads, alerts, focus rings, version gate, CORS with
MOBILE_WEB_ORIGINS.
Not verified / not done
- Push notifications (needs a Firebase project and an APNs key). A real Google/GitHub login round trip on Android (needs a Play-signed install); App Links wait for the Play signing key.
- Recognised speech on the Android emulator (the recogniser path ran, the emulator mic heard nothing); the voice sheet's mute toolbar (no live voice session).
- Found, pre-existing on every platform, left for separate PRs against main: native chat shows a streaming reply only when the turn ends (LegendList memo); the checks sheet's fix button was never wired; PR sheets drop explicit owner/repo; the voice failure alert shows raw OpenAI JSON.
and light.
Not verified / not done
- A real GitHub or Google login round trip on Android (native Google needs a Play-signed install). App Links wait for the Play signing key.
- Web:
Stack.Toolbarheader controls do not render on web (~40 screens); a hosted origin needs adding to the API CORS list and better-auth trusted origins; chat sessions, the voice orb (Rive) and the Pages webview are unverified on web. - Dictation and voice on Android, push notifications on both platforms, PR checks sheets on Android (no entry point yet).
Summary by cubic
Adds Android and web support to apps/mobile; iOS behavior is unchanged. Every iOS-only API now sits behind a typed platform contract picked by Metro, so Android and web resolve to React Native fallbacks instead of @expo/ui, native Expo modules, and Sign in with Apple.
Android
- React Native composer (shared fallback for web and tests) with mic dictation, audio-route ownership, clipboard paste, PDF thumbnails, and swipe-down; plus session tabs, quick keys, slash suggestions, a sheet header, glass surface, alert-prompt, and forward-swipe implementations.
- Native Google sign-in via Credential Manager; no Apple button on Android; social sign-in failures surface their error.
- Agent card as an ongoing notification with a stale "Not updating" form and per-row session navigation, plus a one-time notification permission request and a Chinese Intl fix for Hermes.
- Native Material surfaces:
MenuViewmenus, a ComposeAlertDialogrename prompt, a bottom-sheet attachments picker, and a Compose host theme. android-dev.shdev setup (one emulator per worktree, no stale Metro port reuse), a verification skill, Play listing config, and App Links intent filters.- Screen fixes: toolbar icons as vector drawables, a JS search field, sheet headers that never show route names, aligned page comment pins, Send-to-chat on the diff screen, top-aligned multiline fields, and an empty Home that no longer scrolls.
- Host-service requests get a 60 s deadline so Android cannot hang; Android keeps its query cache in a file; the update gate names Google Play. The home-screen label comes from the brand name and is stripped from the runtime fingerprint; an R8 keep rule preserves WorkManager's merger classes.
Web
- Header buttons and menus render through a
HeaderToolbarseam; chat markdown renders and code highlights via Shiki's JS engine; the chat list pins to the newest item only while the reader is at the bottom, streaming token by token. - The voice orb falls back to an animated pulse; pages and the terminal open in iframes; attachments use the browser file chooser; voice uses the browser's WebRTC;
Alert.alertdraws in a modal with all its buttons; the browser's session cookie carries auth; glass usesbackdrop-filter. Sent attachments open in the platform viewer — QuickLook, an Android intent, or a new tab. - Tabs take the screen's title and Escape closes a sheet; the composer sends on Enter, breaks the line on Shift+Enter, and closes on Escape. Upgrade and manage-plan buttons name the web host they open.
- The app exports under
/agents, whichapps/webserves on the dashboard origin, sharing its session cookie and its own CSP; CI deploys the export to its own Vercel project./agentsstays dark untilAGENTS_URLand that project exist.
Before merging
- The last three commits (Android release config, home-screen-label/fingerprint, Expo web dependencies) change the iOS runtime fingerprint: merging them builds and submits a new iOS binary, and installed iOS builds stop taking OTA updates until they update. Drop them to merge the Android work dark.