Files
fusion/scripts/mobile-run-android.sh
gsxdsm 91c99dd93e fix(mobile): resolve Android status-bar overlap via safe-area plugin
Android WebView returns 0 for env(safe-area-inset-top) by default, so the
CSS-only header inset had no effect on edge-to-edge devices (API 35+).

- Add @capacitor-community/safe-area@^7.0.0 (Capacitor 7 compatible); it
  patches the webview so env(safe-area-inset-*) report real values. Enabled
  natively (no JS init), so it works in remote/live mode too.
- capacitor.config.ts: declare SafeArea plugin (initialViewportFitCover).
- mobile-run-android.sh: idempotently enable EdgeToEdge in MainActivity after
  cap add (android/ is generated/gitignored), required by the plugin.
2026-06-16 18:03:41 -07:00

152 lines
6.2 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# FNXC:MobileAndroidRun 2026-06-16-17:30:
# Convenience deploy script for the Fusion Capacitor Android app. Codifies the
# environment the native build needs so a contributor can go from "connected
# phone" to "app installed" in one command, without re-discovering toolchain
# requirements each time.
#
# Requirements encoded here (learned during the first manual deploy):
# - Capacitor 7's :capacitor-android library compiles at source release 21, so
# Gradle MUST run under a JDK 21 toolchain. JDK 17 fails with
# "invalid source release: 21". We pin JAVA_HOME to Homebrew openjdk@21.
# - The Android SDK lives at the Homebrew cmdline-tools root, not the default
# ~/Library/Android/sdk. We export ANDROID_HOME/ANDROID_SDK_ROOT and write
# android/local.properties (sdk.dir=...) so Gradle resolves the SDK.
# - The target device connects over network ADB (Tailscale), which drops
# between commands. We re-run `adb connect` for FUSION_ANDROID_DEVICE right
# before deploy so Capacitor can see the device as a valid target.
# - When FUSION_SERVER_URL is set, the webview loads the live backend
# (assets + API) from that origin instead of the bundled static client.
# This is the working path until the mobile shell host-context wiring lands
# (see shell-host.ts detectShellHostContext: it has no Capacitor branch, so
# a bundled build self-identifies as a plain browser and calls /api against
# its own static origin -> "API returned HTML instead of JSON").
#
# Usage:
# FUSION_ANDROID_DEVICE=100.96.156.40:5555 \
# FUSION_SERVER_URL=http://100.97.197.105:4040 \
# pnpm mobile:run:android
#
# Env vars:
# FUSION_ANDROID_DEVICE adb target id (host:port for network adb, or serial).
# If unset, Capacitor auto-selects the only device.
# FUSION_SERVER_URL Optional. If set, the app loads from this backend URL
# (remote/live mode). If unset, ships the bundled client.
# ANDROID_HOME Optional override for the SDK root.
# JAVA_HOME Optional override for the JDK 21 home.
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
MOBILE_DIR="$REPO_ROOT/packages/mobile"
# --- Resolve Android SDK root ------------------------------------------------
if [[ -z "${ANDROID_HOME:-}" ]]; then
for candidate in \
"/opt/homebrew/share/android-commandlinetools" \
"$HOME/Library/Android/sdk" \
"/usr/local/share/android-commandlinetools"; do
if [[ -d "$candidate" ]]; then
ANDROID_HOME="$candidate"
break
fi
done
fi
if [[ -z "${ANDROID_HOME:-}" || ! -d "$ANDROID_HOME" ]]; then
echo "[mobile:run:android] Could not locate the Android SDK. Set ANDROID_HOME." >&2
exit 1
fi
export ANDROID_HOME
export ANDROID_SDK_ROOT="$ANDROID_HOME"
# --- Resolve JDK 21 ----------------------------------------------------------
if [[ -z "${JAVA_HOME:-}" ]]; then
for candidate in \
"/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home" \
"/usr/local/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home"; do
if [[ -d "$candidate" ]]; then
JAVA_HOME="$candidate"
break
fi
done
fi
if [[ -z "${JAVA_HOME:-}" || ! -x "$JAVA_HOME/bin/java" ]]; then
echo "[mobile:run:android] JDK 21 not found. Install with: brew install openjdk@21" >&2
echo "[mobile:run:android] Or set JAVA_HOME to a JDK 21 home." >&2
exit 1
fi
export JAVA_HOME
export PATH="$JAVA_HOME/bin:$ANDROID_HOME/platform-tools:$PATH"
echo "[mobile:run:android] ANDROID_HOME=$ANDROID_HOME"
echo "[mobile:run:android] JAVA_HOME=$JAVA_HOME ($("$JAVA_HOME/bin/java" -version 2>&1 | head -1))"
# --- Ensure the Android platform project exists ------------------------------
if [[ ! -d "$MOBILE_DIR/android" ]]; then
echo "[mobile:run:android] Android project missing; adding it (cap add android)..."
(cd "$MOBILE_DIR" && npx cap add android)
fi
# Gradle reads the SDK location from local.properties.
printf "sdk.dir=%s\n" "$ANDROID_HOME" > "$MOBILE_DIR/android/local.properties"
# FNXC:MobileShell 2026-06-16-18:40:
# The Android project is generated (gitignored), so re-apply the edge-to-edge
# enablement that @capacitor-community/safe-area needs in MainActivity. Idempotent:
# only rewrites when EdgeToEdge is not already wired. Without this the status bar
# overlaps the app top on Android 15+ (API 35+).
MAIN_ACTIVITY="$MOBILE_DIR/android/app/src/main/java/com/fusion/mobile/MainActivity.java"
if [[ -f "$MAIN_ACTIVITY" ]] && ! grep -q "EdgeToEdge" "$MAIN_ACTIVITY"; then
echo "[mobile:run:android] Patching MainActivity for edge-to-edge (safe-area insets)..."
cat > "$MAIN_ACTIVITY" <<'JAVA'
package com.fusion.mobile;
import android.os.Bundle;
import androidx.activity.EdgeToEdge;
import com.getcapacitor.BridgeActivity;
// FNXC:MobileShell 2026-06-16-18:40:
// Enable Android edge-to-edge so @capacitor-community/safe-area passes status-bar
// insets to the WebView as env(safe-area-inset-*). Re-applied by
// scripts/mobile-run-android.sh because android/ is generated (gitignored).
public class MainActivity extends BridgeActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
}
}
JAVA
fi
# --- Build web client --------------------------------------------------------
echo "[mobile:run:android] Building dashboard web client..."
pnpm --filter @fusion/dashboard build
RUN_ENV=()
if [[ -n "${FUSION_SERVER_URL:-}" ]]; then
echo "[mobile:run:android] Remote mode: app will load from $FUSION_SERVER_URL"
RUN_ENV+=("FUSION_LIVE_RELOAD=true" "FUSION_SERVER_URL=$FUSION_SERVER_URL")
fi
# --- Reconnect network ADB device right before deploy ------------------------
# FNXC:MobileAndroidRun 2026-06-16-17:55: Network ADB (Tailscale) drops on idle,
# so reconnect AFTER the web build (which takes seconds) and immediately before
# `cap run`, otherwise Capacitor sees no device and rejects the target id.
DEVICE="${FUSION_ANDROID_DEVICE:-}"
if [[ -n "$DEVICE" ]]; then
echo "[mobile:run:android] Reconnecting adb device $DEVICE..."
adb connect "$DEVICE" || true
sleep 1
fi
echo "[mobile:run:android] Attached devices:"
adb devices
cd "$MOBILE_DIR"
if [[ -n "$DEVICE" ]]; then
env "${RUN_ENV[@]}" npx cap run android --target "$DEVICE"
else
env "${RUN_ENV[@]}" npx cap run android
fi