Skip to content

feat(android): native phone app for Remote Access - #297

Open
BigWebstas wants to merge 29 commits into
johannesjo:mainfrom
BigWebstas:feat/android-phone-app
Open

BigWebstas wants to merge 29 commits into
johannesjo:mainfrom
BigWebstas:feat/android-phone-app

Conversation

@BigWebstas

@BigWebstas BigWebstas commented Oct 5, 2026 •

Copy link
Copy Markdown

Description

Adds a native Android app (android/, Kotlin/Compose) for Remote Access, plus the desktop routes it relies on. This replaces #292 and adds everything built since then.

Android app

  • Connecting:
    • Connect by QR code or link and pair with the desktop PIN.
    • Link several computers, each with its own pairing, and switch between them quickly.
    • Optionally wait for a VPN before connecting, and skip that wait on the home Wi-Fi.
  • Agent list: minimized tasks, pull to refresh, and a home-screen widget that totals agents across computers. Usage meters, including Claude credit usage, appear when the desktop serves /api/mobile/usage (added in feat(git): commit dialog, plus phone commit and usage routes #298); otherwise they stay hidden.
  • Terminal:
    • Uses the desktop's colors and look presets, wraps long lines, and follows reliably (optionally always jumping to the latest output).
    • Has quick keys, quick replies and voice input.
    • An opt-in mode sizes the PTY to the phone.
  • Task actions: per-file diffs, notes, new tasks, and merging with readiness checks. Closing a task shows the same warnings as the desktop's Close Task dialog.
  • Built-in chat tasks: transcript, approvals and questions, send and stop.
  • Notifications: optional alerts when an agent needs input, errors or finishes. This runs as a foreground service and is off by default.
  • Backup: back up and restore the phone's data.
  • Look and feel: Obsidian dark and light themes, splash screen, themed icon, predictive back, animations and haptics.
  • Release builds: shrunk with R8 to about 2 MB. A CI workflow (.github/workflows/android.yml) builds, tests and releases the app.

Desktop support

  • New routes:
    • GET /api/mobile/tasks/:id/diff, POST /api/mobile/tasks/:id/close, GET /api/mobile/tasks/:id/readiness and POST /api/mobile/tasks/:id/merge.
    • All are renderer-backed like notes. Reading works with a view-only token; changes need pairing.
    • Without force, close and merge answer 409 with their warnings instead of acting.
  • Phone web UI: gets the same diff review and task actions.
  • view-size WebSocket message: a paired phone may size a terminal while viewing it.
    • The desktop pane's size is remembered and restored on release or disconnect.
    • Only the phone that set the size can hand it back.
  • Terminal history:
    • Subscribing sends a rendered snapshot of the screen plus the full scrollback, taken from the existing main-process headless mirror (@xterm/addon-serialize).
    • This replaces the raw byte replay, which agents that redraw often fill with repaints of a single screen.
    • Exited agents still get the raw replay.
  • Minimized tasks appear in the phone's agent list.

Overlap with my other PRs

Build and install instructions are in android/README.md.

Screenshots

Agent list (Ember) Agent list (light) Terminal Terminal (Noir)
Agent list with usage meters, dark Ember look Agent list in the light look Terminal with quick keys and quick replies Terminal in the Noir look
Changes Commit Looks Home-screen widget
Changes tab with an expanded diff Commit dialog with Stage all and Unstage all Settings with the desktop look presets Home-screen widget with agent totals and usage

Issues Resolved

None directly. Related to #192 (installing on Android): this offers a native app instead of the PWA.

Check List

  • New functionality includes testing.
  • New functionality has been documented in the README if applicable. (The app has its own android/README.md.)

🤖 Generated with Claude Code

BigWebstas and others added 27 commits September 29, 2026 17:36
A Kotlin/Compose client for the Connect Phone API in android/.

- Connect by QR code or link, pair with the desktop PIN, and link
  several computers, each with its own pairing
- Agent list with minimized tasks, usage meters (when the desktop
  serves /api/mobile/usage), pull to refresh, and a home-screen widget
- Swipe sideways between open tasks, with the position in the header
- Terminal: desktop colors, lazy rendering, reliable follow (optionally
  always jump to the latest), quick keys, quick replies, voice input,
  and an opt-in mode that sizes the PTY to the phone
- Changes tab with per-file diffs, notes, new tasks, and closing tasks
  with the desktop's warnings
- Built-in chat tasks: transcript, approvals and questions, send / stop
- Optional notifications when an agent needs input, errors or finishes
- Obsidian dark and light themes, splash, themed icon, predictive back,
  animations and haptics; release builds shrink with R8

Desktop support:
- POST /api/mobile/tasks/:id/close (paired) and
  GET /api/mobile/tasks/:id/diff (view-only and paired), renderer-backed
- `view-size` socket message: a paired phone may size a terminal while
  viewing it; the desktop's size returns on release or disconnect
- Subscribing sends a rendered snapshot with up to 1000 lines of history
  (@xterm/addon-serialize over the existing headless mirror)
- Minimized tasks appear in the phone's agent list

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Settings -> Widget now sets how opaque the home-screen widget's card is
(100/75/50/25%) and what color it is (Obsidian, Slate, Light). The fill
and border are one color faded together, and each card carries text
colors that stay readable, so a light card gets dark text.

Both settings push to placed widgets immediately instead of waiting for
the next publish, and Settings previews the real card at the chosen
transparency.

RemoteViews can only set a background from a resource, so the stops are
shape drawables: three colors by four stops. A contrast test keeps each
palette's text on its own card at or above the 4.5:1 WCAG AA floor; it
caught Slate's timestamp color at 4.1:1, now lightened.
Add a Diff tab to the phone UI and merge/close actions behind a
read-only readiness dialog.

Diffs come from the renderer's own loadTaskDiff rather than the MCP
coordinator: Coordinator.tasks only tracks delegated sub-tasks, so the
coordinator route would 404 for phone-created and ordinary desktop
tasks. Readiness reuses the desktop's pure buildMergeReadiness so both
surfaces agree on what blocks a merge.

Read routes (diff, readiness) follow notes and accept the view-only
token; merge and close run real git and require the PIN-elevated paired
token. All task ids are rejected at the boundary when they name a
prototype-chain key.
Add a Merge action beside Close on the agent screen, opening a dialog
that renders the desktop's own merge-readiness checks read-only and
gates the confirm button on them.

A blocked verdict disables merging; a warning does not, so someone away
from their desk can still merge with eyes open. canMerge defaults to
false when the field is missing, so an unreadable reply can never
enable a git write.
The phone app now offers the same 15 looks as the desktop instead of its
own Obsidian/Light pair. Appearance in Settings picks a look with a live
swatch, and the theme (follow system, always dark, always light) chooses the
tone, with the dark and light look remembered separately so a theme switch
brings the matching look with it.

LookPalettes.kt is generated from the sources the desktop already keeps its
looks in: src/lib/look.ts for ids, labels, order and tone, src/styles.css for
the colors and radius scale, and src/lib/theme.ts for the terminal ANSI
palettes and look pairings. The generator resolves each preset's full
cascade (no phone-side fallbacks), flattens radial-gradients to their middle
stop, and falls back to the muted Noir ANSI set for dark looks the desktop
leaves on xterm defaults. LookPresetsTest pins the default Obsidian values
so a new desktop theme cannot quietly change the out-of-the-box appearance.

npm run generate:android-looks rewrites the file and
npm run check:android-looks fails when it drifts; the Kotlin tests run the
same check.
…/android-phone-app

Brings the desktop's 15 look presets to the phone (Settings → Appearance),
with LookPalettes.kt generated from src/lib/look.ts, src/styles.css and
src/lib/theme.ts so the two stay in step. Touches Screens.kt alongside the
phone diff review and task lifecycle work already on this branch.
A task with git isolation 'none' edits the project folder in place, so
it has no branch or worktree of its own. handleGetDiff fell through to
loadTaskDiff, which diffed the project folder against the base branch:
the phone either showed a git error or, worse, listed unrelated work
already sitting in the repo as if the task had made it.

Answer with an empty diff and an `unsupported` flag instead, and have
both phone clients say why there is nothing to compare. The flag is
optional, so an older desktop that omits it still renders its diff.

The Kotlin client read the response as a Pair, which had no room for a
third field; fetchDiff now returns a TaskDiff parsed in one place.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Header Switch menu plus latency in the Agents subtitle when
  multiple computers are saved.
- Terminal Stop button, Material icons for stop/history/mic and
  larger arrow quick keys (-> takes Tab's slot, / restored).
- Send-quick-replies-immediately setting and per-agent local
  prompt history with a Recent-messages dialog.
- Custom connection labels, editable in Settings > Computers.
Add a Home Wi-Fi setting under "Wait for VPN". When the phone is on that
network, connect without waiting for a VPN. Reading the Wi-Fi name needs
location access, which the app requests when the name is set.

Rename VpnMonitor to NetworkMonitor, which now also tracks the Wi-Fi name,
and consolidate the VPN wait check into waitsForVpn.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Test and build the app when android/ changes. Pushing an android-v* tag
publishes a signed APK as a GitHub release, separate from desktop v*
releases and never marked latest. Release signing and the version come
from the environment, so the keystore stays out of the repo.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add a Commit button next to Merge on a task. Its dialog lists the
uncommitted files and which are staged, offers Stage all and Unstage all,
and commits what is staged with a message, through the desktop's
/api/mobile/tasks/:taskId/commit route. Reading works with the view-only
token; staging and committing need pairing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The main-process screen mirror kept only 1000 lines of scrollback, and
phones opening an agent terminal are sent that mirror, so long sessions
showed a truncated history. Match the desktop renderer's 10k lines.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lines still too wide for the phone at the smallest font now wrap at word
boundaries, so the agent terminal no longer needs horizontal panning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oesn-t-fully-populate-f4255f

Phone terminal: full history and word wrap
The task pager kept syncing its settled page into the screen while it
animated out, so the list change from the close sent it to a
neighbouring task's terminal. Only follow the pager while an agent
screen is open.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Save computers, settings and prompt history to a zip, optionally
encrypted with a password (AES-256-GCM, PBKDF2 key). Restore from
Settings or the connect screen; it validates the whole file before
writing and restarts the app. Automatic backups write daily or weekly
to a picked folder, keep the latest 10, and hold their password sealed
by an Android Keystore key.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The widget sums agents from every saved computer, polling the others'
agent lists once a minute over HTTP while the app is connected, and
says how many answered ("3 working · 2 comps"). It also gains the app
icon by its name, a status dot colored by what needs you most, and a
divider above the usage meters.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RemoteViews inflates only whitelisted view classes, and a plain View is
not one, so launchers showed "Can't load widget".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oesn-t-fully-populate-f4255f

Android: backup/restore, close-task fix, multi-computer widget
# Conflicts:
#	src/remote/AgentDetail.client.test.tsx
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t windows

Logins billed per use get no five-hour or weekly window from the usage
endpoint, only an extra_usage block, so the parser returned nothing and
the status bar stayed hidden. Read that block into a spend field and
show it as "$used / $limit" in the bar and popover.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mirror the desktop spend field so per-use logins show "$used / $limit"
instead of nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Android hides the Wi-Fi name from background apps, so a name read while
only the notification service ran stayed blank and the app kept waiting
for a VPN at home. Re-read it whenever the app comes on screen, and ask
for "Allow all the time" location so agent notifications also connect
on the home Wi-Fi in the background.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replace the spend field (minor units) with the CreditUsage type that
fork/extras already ships (standard units plus usedPercent), so the
branches merge cleanly and the phone reads one contract. The bar still
shows credits inline when a login has no rate-limit windows, and the
Android card and widget read creditUsage.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
BigWebstas added a commit to BigWebstas/parallel-code that referenced this pull request Oct 5, 2026
Poll each other saved computer for its usage alongside its agents, and
group the widget's meters under each computer's label with a full-width
divider beneath it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
BigWebstas added a commit to BigWebstas/parallel-code that referenced this pull request Oct 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant