Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 109 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,34 +73,34 @@ Run in dev mode:
npm run tauri:dev
```

## iOS Support (WIP)
## Mobile Support (WIP)

iOS support is currently in progress.
Mobile support is currently in progress for iOS and Android.

- Current status: mobile layout runs, remote backend flow is wired, and iOS defaults to remote backend mode.
- Current status: mobile layout runs, remote backend flow is wired, and mobile builds default to remote backend mode.
- Current limits: terminal and dictation remain unavailable on mobile builds.
- Desktop behavior is unchanged: macOS/Linux/Windows remain local-first unless remote mode is explicitly selected.

### iOS + Tailscale Setup (TCP)
### Mobile + Tailscale Setup (TCP)

Use this when connecting the iOS app to a desktop-hosted daemon over your Tailscale tailnet.
Use this when connecting the mobile app (iOS/Android) to a desktop-hosted daemon over your Tailscale tailnet.

1. Install and sign in to Tailscale on both desktop and iPhone (same tailnet).
1. Install and sign in to Tailscale on desktop and your mobile device (same tailnet).
2. On desktop CodexMonitor, open `Settings > Server`.
3. Keep `Remote provider` set to `TCP (wip)`.
4. Set a `Remote backend token`.
5. Start the desktop daemon with `Start daemon` (in `Mobile access daemon`).
6. In `Tailscale helper`, use `Detect Tailscale` and note the suggested host (for example `your-mac.your-tailnet.ts.net:4732`).
7. On iOS CodexMonitor, open `Settings > Server`.
7. On mobile CodexMonitor, open `Settings > Server`.
8. Set `Connection type` to `TCP`.
9. Enter the desktop Tailscale host and the same token.
10. Tap `Connect & test` and confirm it succeeds.

Notes:

- The desktop daemon must stay running while iOS is connected.
- The desktop daemon must stay running while mobile clients are connected.
- If the test fails, confirm both devices are online in Tailscale and that host/token match desktop settings.
- If you want to use Orbit instead of Tailscale TCP, switch `Connection type` to `Orbit` on iOS and use your desktop Orbit websocket URL/token.
- If you want to use Orbit instead of Tailscale TCP, switch `Connection type` to `Orbit` on mobile and use your desktop Orbit websocket URL/token.

### iOS Prerequisites

Expand Down Expand Up @@ -130,7 +130,7 @@ Options:
- `--skip-build` to reuse the current app bundle.
- `--no-clean` to preserve `src-tauri/gen/apple/build` between builds.

### Run on USB Device
### Run on iOS USB Device

List discoverable devices:

Expand Down Expand Up @@ -162,6 +162,105 @@ If signing is not ready yet, open Xcode from the script flow:
./scripts/build_run_ios_device.sh --open-xcode
```

### Android Prerequisites

- Android Studio installed with Android SDK + SDK Platform + SDK Platform Tools.
- Android command line tools installed and `adb` available in `PATH`.
- JDK 17 configured for Gradle/Android builds.
- Rust Android targets installed:

```bash
rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android
```

- Android environment variables configured (typical setup):

```bash
export ANDROID_HOME="$HOME/Library/Android/sdk"
export ANDROID_SDK_ROOT="$ANDROID_HOME"
export NDK_HOME="$ANDROID_HOME/ndk/<installed-version>"
```

Android scripts auto-detect common SDK paths and the latest installed NDK, but explicit env vars are recommended for reproducible CI/dev setups.

### Initialize Android Project Files

```bash
./scripts/init_android.sh
```

### Run on Android (Dev)

List connected Android devices/emulators:

```bash
./scripts/build_run_android.sh --list-devices
```

Run dev mode on default/first available device:

```bash
./scripts/build_run_android.sh
```

Run dev mode on a specific device:

```bash
./scripts/build_run_android.sh --device "<device id or name>"
```

Optional:

- Use `--host <lan-ip>` for physical-device testing when the dev server is on your local network.
- Use `--open` to open Android Studio.
- Use `--release` for release-mode run.

### Build Android Artifacts (APK/AAB)

```bash
./scripts/build_android.sh
```

Common options:

- `--target aarch64 --target x86_64` for specific ABIs.
- `--split-per-abi` for split outputs.
- `--apk-only` or `--aab-only` to limit artifact types.
- `--debug` for debug build artifacts.

Build outputs are generated under `src-tauri/gen/android/app/build/outputs/`.

### Install on Android Phone

Prerequisites on phone:

1. Enable `Developer options`.
2. Enable `USB debugging`.
3. Connect phone via USB and authorize this computer on the phone prompt.

Verify the device is visible:

```bash
./scripts/build_run_android.sh --list-devices
```

Build and install debug APK:

```bash
./scripts/build_android.sh --apk-only --debug
./scripts/install_android_apk.sh --device "<device-id>"
```

The installer script also launches the app after install.

Optional wireless adb (after first USB authorization):

```bash
adb -s "<device-id>" tcpip 5555
adb connect "<phone-ip>:5555"
./scripts/install_android_apk.sh --device "<phone-ip>:5555"
```

### iOS TestFlight Release (Scripted)

Use the end-to-end script to archive, upload, configure compliance, assign beta group, and submit for beta review.
Expand Down
12 changes: 12 additions & 0 deletions docs/android-support-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Android Support Implementation Plan

- [ ] Confirm Android toolchain prerequisites and environment variables (`ANDROID_HOME`, `ANDROID_SDK_ROOT`, `JAVA_HOME`, `adb`, `emulator`).
- [ ] Add Android-specific Tauri config override (`src-tauri/tauri.android.conf.json`) with Android bundle identifier.
- [ ] Align backend mobile defaults so Android uses the same remote-first behavior as iOS.
- [ ] Add Android bootstrap script to initialize Tauri Android project files when missing.
- [ ] Add Android simulator/device run script for development (`tauri android dev`) with device listing support.
- [ ] Add Android release build script (`tauri android build`) with ABI and artifact options (APK/AAB, split per ABI).
- [ ] Update server/mobile UX copy that is iOS-only so Android users receive correct guidance.
- [ ] Document Android prerequisites and run/build workflows in `README.md`.
- [ ] Add or update frontend/backend tests covering Android/mobile detection and defaults.
- [ ] Run validation (`npm run lint`, `npm run test`, `npm run typecheck`, `cd src-tauri && cargo check`) and resolve integration issues.
10 changes: 9 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,15 @@
"pretauri:dev:win": "npm run sync:material-icons",
"tauri:dev:win": "npm run doctor:win && tauri dev --config src-tauri/tauri.windows.conf.json",
"pretauri:build:win": "npm run sync:material-icons",
"tauri:build:win": "npm run doctor:win && tauri build --config src-tauri/tauri.windows.conf.json"
"tauri:build:win": "npm run doctor:win && tauri build --config src-tauri/tauri.windows.conf.json",
"pretauri:android:init": "npm run sync:material-icons",
"tauri:android:init": "tauri android init",
"pretauri:android:dev": "npm run sync:material-icons",
"tauri:android:dev": "tauri android dev",
"pretauri:android:build": "npm run sync:material-icons",
"tauri:android:build": "tauri android build",
"pretauri:android:run": "npm run sync:material-icons",
"tauri:android:run": "tauri android run"
},
"dependencies": {
"@pierre/diffs": "^1.0.6",
Expand Down
41 changes: 41 additions & 0 deletions scripts/android_env.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/usr/bin/env bash

ensure_android_env() {
local sdk_home="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}"
if [[ -z "$sdk_home" ]]; then
for candidate in "$HOME/Library/Android/sdk" "$HOME/Android/Sdk"; do
if [[ -d "$candidate" ]]; then
sdk_home="$candidate"
break
fi
done
fi

if [[ -z "$sdk_home" ]]; then
return
fi

export ANDROID_HOME="$sdk_home"
export ANDROID_SDK_ROOT="$sdk_home"

local platform_tools_dir="$sdk_home/platform-tools"
if [[ -d "$platform_tools_dir" && ":$PATH:" != *":$platform_tools_dir:"* ]]; then
export PATH="$platform_tools_dir:$PATH"
fi

local cmdline_tools_latest="$sdk_home/cmdline-tools/latest/bin"
if [[ -d "$cmdline_tools_latest" && ":$PATH:" != *":$cmdline_tools_latest:"* ]]; then
export PATH="$cmdline_tools_latest:$PATH"
fi

if [[ -z "${NDK_HOME:-}" ]]; then
local ndk_dir="$sdk_home/ndk"
if [[ -d "$ndk_dir" ]]; then
local latest_ndk=""
latest_ndk="$(ls -1 "$ndk_dir" 2>/dev/null | sort | tail -n 1)"
if [[ -n "$latest_ndk" && -d "$ndk_dir/$latest_ndk" ]]; then
export NDK_HOME="$ndk_dir/$latest_ndk"
fi
fi
fi
}
159 changes: 159 additions & 0 deletions scripts/build_android.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
#!/usr/bin/env bash
set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT_DIR"
source "$ROOT_DIR/scripts/android_env.sh"

declare -a TARGETS=()
DEBUG_MODE=0
SPLIT_PER_ABI=0
APK_ONLY=0
AAB_ONLY=0
OPEN_ANDROID_STUDIO=0
CI_MODE=1
SKIP_INIT=0

usage() {
cat <<'EOF'
Usage: scripts/build_android.sh [options]

Builds Android artifacts (APK/AAB) using Tauri.

Options:
--target <abi> Add target ABI (aarch64, armv7, i686, x86_64), repeatable
--debug Build debug artifacts
--split-per-abi Build split APK/AAB per ABI
--apk-only Build APKs only
--aab-only Build AABs only
--open Open Android Studio
--no-ci Allow interactive prompts
--skip-init Skip Android project initialization check
-h, --help Show this help
EOF
}

while [[ $# -gt 0 ]]; do
case "$1" in
--target)
TARGETS+=("${2:-}")
shift 2
;;
--debug)
DEBUG_MODE=1
shift
;;
--split-per-abi)
SPLIT_PER_ABI=1
shift
;;
--apk-only)
APK_ONLY=1
shift
;;
--aab-only)
AAB_ONLY=1
shift
;;
--open)
OPEN_ANDROID_STUDIO=1
shift
;;
--no-ci)
CI_MODE=0
shift
;;
--skip-init)
SKIP_INIT=1
shift
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unknown option: $1" >&2
usage >&2
exit 1
;;
esac
done

if [[ "$APK_ONLY" -eq 1 && "$AAB_ONLY" -eq 1 ]]; then
echo "--apk-only and --aab-only cannot be used together." >&2
exit 1
fi

resolve_npm() {
if command -v npm >/dev/null 2>&1; then
command -v npm
return
fi

for candidate in /opt/homebrew/bin/npm /usr/local/bin/npm; do
if [[ -x "$candidate" ]]; then
echo "$candidate"
return
fi
done

if [[ -n "${NVM_DIR:-}" && -s "${NVM_DIR}/nvm.sh" ]]; then
# shellcheck source=/dev/null
. "${NVM_DIR}/nvm.sh"
if command -v npm >/dev/null 2>&1; then
command -v npm
return
fi
fi

return 1
}

if [[ "$SKIP_INIT" -eq 0 ]]; then
init_cmd=("$ROOT_DIR/scripts/init_android.sh")
if [[ "$CI_MODE" -eq 0 ]]; then
init_cmd+=(--no-ci)
fi
"${init_cmd[@]}"
fi

ensure_android_env

NPM_BIN="$(resolve_npm || true)"
if [[ -z "$NPM_BIN" ]]; then
echo "Unable to find npm in PATH or common install locations." >&2
echo "Install Node/npm, or run from a shell where npm is available." >&2
exit 1
fi

cmd=("$NPM_BIN" run tauri -- android build)
if [[ "$DEBUG_MODE" -eq 1 ]]; then
cmd+=(--debug)
fi
if [[ "${#TARGETS[@]}" -gt 0 ]]; then
cmd+=(--target "${TARGETS[@]}")
fi
if [[ "$SPLIT_PER_ABI" -eq 1 ]]; then
cmd+=(--split-per-abi)
fi
if [[ "$APK_ONLY" -eq 1 ]]; then
cmd+=(--apk true --aab false)
fi
if [[ "$AAB_ONLY" -eq 1 ]]; then
cmd+=(--apk false --aab true)
fi
if [[ "$OPEN_ANDROID_STUDIO" -eq 1 ]]; then
cmd+=(--open)
fi
if [[ "$CI_MODE" -eq 1 ]]; then
cmd+=(--ci)
fi

"${cmd[@]}"

if [[ -d "src-tauri/gen/android/app/build/outputs" ]]; then
echo
echo "Android artifacts:"
find src-tauri/gen/android/app/build/outputs -type f \
\( -name '*.apk' -o -name '*.aab' \) | sort
fi
Loading