Skip to content

Companion App

aaalllexxx edited this page Jun 29, 2026 · 2 revisions

Companion App

The Companion (com.enpaf.companion) is an Expo-Go-style loader for ENPAF. It pushes a freshly built APK to a real device over Wi-Fi — no USB cable, no browser — by scanning a QR code, and gives you focused tooling for the develop → install loop.

It's itself an ENPAF app (in companion/), so it doubles as a large real-world example of the framework.

The flow

  Dev machine                         Phone (Companion app)
  ───────────                         ─────────────────────
  paf serve                           scan QR / paste URL
    ├─ serves dist/*.apk over LAN  ─► download APK over Wi-Fi
    ├─ prints enpaf://debug?url=…      ├─ install via PackageInstaller
    │   &token=…&ssid=…  + QR          └─ launch
    └─ one-time token gate
  1. Build an app: paf build apk (or --release).
  2. Start the server: paf serve (default port 8000). It serves the APK from dist/ on your LAN, generates a one-time token, and prints an enpaf://debug?url=…&token=…&ssid=… link as a QR code.
  3. In the Companion, scan the QR (or paste the URL). It downloads the APK over Wi-Fi and installs it through the Android PackageInstaller API.

Tabs

The Companion has four focused tabs — no sensor playgrounds, just what helps while developing:

Connect

The loader and home screen:

  • Scan dev-server QR or paste/enter the build URL.
  • Paste URL — reads the clipboard and auto-detects an enpaf://debug… link or an http(s)://…apk URL.
  • Install-permission banner — if the OS blocks installs from this app, a one-tap button opens the right settings screen.
  • This device — your Wi-Fi SSID + IP, and a warning if your network doesn't match the dev server's.
  • Reachability check before install.
  • Auto-reload watch — polls the build URL and alerts you when the APK changes on the server (so you can reinstall the latest build quickly).
  • Favorites — pin dev-server URLs for one-tap reinstall.
  • Recent builds — reinstall / copy URL / remove.

Logs

Timestamped download & install activity and errors, with copy/clear — the first place to look when a build won't load.

Device

A diagnostics report of the phone your app will run on: manufacturer/model, Android version + API level, ABI, screen, RAM, storage, WebView version, and install-permission status — with Copy report for bug reports. Also a connection tester: enter a build URL to measure reachability, round-trip latency (ms), size, and Last-Modified (real headers, fetched from Python).

Settings

Biometric lock (fingerprint/face on launch), install-permission status + a shortcut to the system screen, clear history/log, and an About panel.

Requirements

  • Phone and dev machine on the same Wi-Fi network.
  • The Companion needs REQUEST_INSTALL_PACKAGES (declared in its enpaf.json) to install downloaded APKs.
  • Install progress/outcome surface via the install_status event (and the Logs tab).

Building the Companion yourself

cd companion
paf build apk --release

The release-signed APK lands in companion/dist/. Prebuilt Companion APKs are attached to the repository's Releases.

Updating on a device: the build's versionCode is derived from enpaf.json version (major*10000 + minor*100 + patch). Bump version before rebuilding, or Android won't install the new APK over the old one — see Troubleshooting.

Notes for contributors

A couple of Chaquopy/WebView specifics the Companion exercises (and that apply to any ENPAF app):

  • Camera in the WebView: getUserMedia needs both the runtime CAMERA grant and a WebView that grants onPermissionRequest and sets setMediaPlaybackRequiresUserGesture(false) (so the preview <video> autoplays). The framework's generated MainActivity does both. Bundle JS libraries (e.g. the QR scanner) locally — the dev LAN often has no internet, so CDN scripts won't load.
  • Use from android.provider import Settings, not import android.provider.Settings as Settings (Chaquopy can't resolve the bare import android the aliased form triggers).
  • Native callbacks for abstract Java classes (e.g. BroadcastReceiver, BiometricPrompt.AuthenticationCallback) are implemented in MainActivity (Java) and forwarded to Python — Chaquopy's dynamic_proxy supports interfaces only. See Architecture.

Clone this wiki locally