#!/usr/bin/env bash # Point an Android device at this machine's Metro dev server, permanently. # # The app is a plain `expo run:android` debug build (no expo-dev-client), so it # looks for the packager at localhost:8081 unless told otherwise. Over USB that # works via `adb reverse`; over Wi-Fi it doesn't, and the app fails with # "Unable to load script". Writing `debug_http_host` into the app's own # SharedPreferences fixes it for good: the setting lives in the app's data dir # and survives app restarts, reboots and adb reconnects. # # Usage: # scripts/point-device.sh # auto-detect device and host IP # scripts/point-device.sh 192.168.1.50:5555 # explicit device # scripts/point-device.sh 192.168.1.50:5555 192.168.1.10 # explicit host IP # # Re-run this after the dev machine's IP changes — and update # EXPO_PUBLIC_SERVER_URL in .env to match, since the API calls use that. set -euo pipefail PKG="com.waseel.app" PORT="8081" DEVICE="${1:-}" HOST_IP="${2:-}" if [ -z "$DEVICE" ]; then mapfile -t TARGETS < <(adb devices | awk 'NR>1 && $2=="device" {print $1}') if [ "${#TARGETS[@]}" -eq 0 ]; then echo "No device attached. Connect one first (adb connect :)." >&2 exit 1 fi # One phone commonly shows up as several transports at once (a TCP address # plus one or two auto-reconnecting mDNS entries). Group by hardware serial # so that isn't mistaken for two phones, and prefer the ip:port form, which # is the stable one to keep addressing. declare -A BY_SERIAL=() for target in "${TARGETS[@]}"; do serial="$(adb -s "$target" shell getprop ro.serialno 2>/dev/null | tr -d '\r\n')" [ -z "$serial" ] && serial="$target" if [ -z "${BY_SERIAL[$serial]:-}" ] || [[ "$target" == *:* && "${BY_SERIAL[$serial]}" != *:* ]]; then BY_SERIAL["$serial"]="$target" fi done if [ "${#BY_SERIAL[@]}" -gt 1 ]; then echo "More than one phone attached; pass the one you want:" >&2 for serial in "${!BY_SERIAL[@]}"; do printf ' %s (serial %s)\n' "${BY_SERIAL[$serial]}" "$serial" >&2 done exit 1 fi for serial in "${!BY_SERIAL[@]}"; do DEVICE="${BY_SERIAL[$serial]}"; done fi if [ -z "$HOST_IP" ]; then # The address this machine reaches the LAN on — the one the phone can see. HOST_IP="$(ip route get 1.1.1.1 2>/dev/null | grep -oP 'src \K[0-9.]+' | head -1)" fi if [ -z "$HOST_IP" ]; then echo "Could not determine this machine's LAN IP; pass it as the second argument." >&2 exit 1 fi echo "Device : $DEVICE" echo "Metro : $HOST_IP:$PORT" if ! adb -s "$DEVICE" shell "run-as $PKG id" >/dev/null 2>&1; then echo "Cannot run-as $PKG — the installed build isn't debuggable." >&2 echo "Rebuild with 'npx expo run:android' (a debug build), then re-run this." >&2 exit 1 fi # A running app holds its prefs in memory and rewrites the file on exit, so the # write only sticks if the app is stopped first. adb -s "$DEVICE" shell "am force-stop $PKG" PREFS="/data/data/$PKG/shared_prefs/${PKG}_preferences.xml" TMP="/data/local/tmp/waseel_prefs.xml" cat < /tmp/waseel_prefs.xml $HOST_IP:$PORT EOF adb -s "$DEVICE" push /tmp/waseel_prefs.xml "$TMP" >/dev/null # Piped through run-as so the file lands owned by the app uid, not shell. adb -s "$DEVICE" shell "cat $TMP | run-as $PKG sh -c 'cat > $PREFS'" adb -s "$DEVICE" shell "rm -f $TMP" rm -f /tmp/waseel_prefs.xml # Fallback path for USB sessions; harmless over Wi-Fi and not relied upon. adb -s "$DEVICE" reverse "tcp:$PORT" "tcp:$PORT" >/dev/null 2>&1 || true echo "Set debug_http_host:" adb -s "$DEVICE" shell "run-as $PKG cat $PREFS" | tr -d '\r' | grep debug_http_host # `am start` on the explicit activity, rather than `monkey`, which silently # drops the launch often enough to be untrustworthy in a script. adb -s "$DEVICE" shell "am start -n $PKG/.MainActivity" >/dev/null 2>&1 sleep 3 if adb -s "$DEVICE" shell dumpsys window 2>/dev/null | grep -q "mCurrentFocus.*$PKG"; then echo "Launched $PKG — now loading from $HOST_IP:$PORT." else echo "Wrote the setting, but $PKG didn't come to the foreground. Open it by hand." >&2 fi