Skip to main content

Android

A Java shell sits around the Zig core: a plain Activity, a SurfaceView and the platform gesture detectors. The core cross-compiles to aarch64-linux-android and renders raw Vulkan (src/gpu_vk.zig) onto the view's ANativeWindow. The chrome above it is Compose.

The JNI natives (Java_org_beetlebug_lookout_Lookout_*) live in the core itself (src/jni_android.zig, compiled in with -Dbackend=vk), so the whole native side ships as two Zig archives.

Units at the JNI boundary are logical points. Java divides pixels by DisplayMetrics.density, and the core derives its pixel density from the surface pixels and the size in points that resize() states.

Before you build

  • JDK 17, the Android SDK (platform 35, build-tools 35, NDK 27.2.12479018, CMake 3.22.1) and Zig 0.16.
  • Set sdk.dir in local.properties and ANDROID_NDK in the environment.

Building and running

cd android
./gradlew assembleDebug # -> app/build/outputs/apk/debug/app-debug.apk

$ANDROID_HOME/emulator/emulator -avd <avd> -gpu host &
adb install -r app/build/outputs/apk/debug/app-debug.apk
adb shell am start -n org.beetlebug.lookout/.LookoutActivity
adb logcat -s lookout

Gradle's buildZigCore<Variant> task runs build-libs.sh before the CMake link, with the NDK that ndkVersion resolves. A debug APK compiles the core -Doptimize=Debug for fast iteration; release gets ReleaseFast. Override with -PcoreOpt=<mode>.

Only arm64-v8a is wired today, which covers Apple-silicon emulators and real arm64 devices. Add ABIs in abiFilters in app/build.gradle.

What each gesture does

One finger pans, a pinch zooms about the focal point, a twist rotates past an 18 degree dead zone, a double tap zooms in, a tap identifies, and a long press steps through day, dusk and night.

Adding your own charts

The app ships a baked cell (assets/charts/US5MD1MC.pmtiles, Annapolis) and copies it to internal storage at the first launch. Bake your own with tile57 bake <cell.000> -o out/ and drop out/tiles/<cell>.pmtiles into assets/charts/, or add a folder in the Charts tab.

Where the code lives

FileRole
Lookout.javaThe handle-based Java binding to the C ABI
LookoutView.javaSurfaceView, the gesture detectors, and a Choreographer loop that runs only when the scene is dirty
LookoutActivity.ktThe Activity: chart assets, then the Compose chrome over the view
ChartScreen.ktThe chrome layout
ChartController.ktThe one handle and every call into it
Hud.ktThe readouts, the compass and the pick report
SettingsSheet.ktThe mariner form
app/jni/src/CMakeLists.txtliblookout_jni.so: the two Zig archives plus vulkan, log and android
build-libs.shCross-compiles the core into app/jni/prebuilt/<abi>/

What to watch out for

The emulator needs Hypervisor.framework, which means a real Mac and not a nested VM, and it needs a display. A headless box can build the APK but cannot run it.