Skip to content

SPLIT v0.2.1 — the watch app opens

Pre-release
Pre-release

Choose a tag to compare

@GPTmadeit GPTmadeit released this 10 Aug 03:41
· 10 commits to main since this release

The watch app did not open in v0.1.0 or v0.2.0. It does now. Releases can also finally be installed as updates. Both problems were reproduced and fixed against a Wear OS 6 emulator rather than reasoned about.

Important

Upgrading from v0.1.0 or v0.2.0? Uninstall first.

adb uninstall com.carlb.split

Those builds were each signed with a different throwaway key, so Android rejects this one with INSTALL_FAILED_UPDATE_INCOMPATIBLE. This is a one-time break — from v0.2.1 onward every build shares one signing key and updates install in place, keeping your settings and logged strings.


Fixed

The watch app crashed instantly on launch

RecoilGate registered the accelerometer at SENSOR_DELAY_FASTEST (0 µs). Since Android 12 any sampling rate above 200 Hz requires HIGH_SAMPLING_RATE_SENSORS, which was never declared — so registerListener threw straight out of the foreground service:

SecurityException: To use the sampling rate of 0 microseconds, app needs to
declare the normal permission HIGH_SAMPLING_RATE_SENSORS
    at RecoilGate.start(RecoilGate.kt:55)
    at TimerEngine.openMic(TimerEngine.kt:95)
    at TimerService.onStartCommand(TimerService.kt:82)

START_STICKY then had the system recreate the service into the identical crash, so the app opened and vanished, forever.

Four changes, because the permission alone would only fix today's instance:

  • HIGH_SAMPLING_RATE_SENSORS is declared.
  • RecoilGate.start() can no longer throw — it falls back to a slower sampling rate and reports itself unavailable rather than dying.
  • The accelerometer is started only when the recoil gate is enabled. It ships off by default, so a disabled optional feature was taking down the whole timer.
  • START_NOT_STICKY, and startForeground() happens before anything that can fail.

Releases could not be installed as updates

Every CI run generated a fresh ~/.android/debug.keystore, so each release carried a different signer and Android refused the upgrade. Builds now use a committed signing key, verified identical across both apps.

That key is deliberately public — its credentials are the Android SDK's standard debug constants and the file is in the repository. It exists so upgrades work, not to prove anything about origin. Verify downloads against SHA256SUMS.txt.

Added

  • Emulator smoke test in CI — installs both APKs, launches them, fails if the process dies or logs a fatal exception. Both apps compiled, passed 42 unit tests and passed Android Lint while crash-looping; no static check could see this.
  • Signing-key stability check in CI — fails the build if the APK signer drifts, which is what silently broke updates last time.

Changed

  • README badge now distinguishes emulator launch verified from live fire tested (still no).
  • New troubleshooting entries for INSTALL_FAILED_UPDATE_INCOMPATIBLE and the app closing on launch.

Removed / Breaking

Nothing removed. No API or wire-format change — a v0.2.1 watch and a v0.2.0 phone still interoperate. The signing key change is not a code break but does require the one-time uninstall above.


Install

Install the watch APK on the watch and the phone APK on the phone. They share an applicationId, so putting both on one device replaces one with the other.

adb -s <watch-serial> install -r split-wear-v0.2.1-debug.apk
adb -s <phone-serial> install -r split-mobile-v0.2.1-debug.apk

adb devices lists serials. Verify your download:

sha256sum -c SHA256SUMS.txt

What was actually verified

Check Result
./gradlew build (debug + release, both modules) pass
Unit tests 42 / 42
Android Lint no issues on :wear and :mobile
ktlint 0 violations
Watch app launches on Wear OS 6 (API 36) emulator pass — process alive, no crash
Foreground service holds the mic pass — isForeground=true, types=0x80
Timer arms, sounds the tone, runs the clock pass
Phone app launches pass — no crash
In-place update over a previous v0.2.1 install pass
Both APKs share one signing key pass (df77d1c4…)
Old-key upgrade correctly rejected reproduced, hence the uninstall note

Known limitations

No live fire has ever been recorded through this code, and it has not run on a physical watch. An emulator has no microphone worth the name and no gunshots.

The four detector constants remain reasoned estimates, not measurements:

Constant Value Basis
Clip-run threshold 3 samples estimate
Echo blanking 60 ms estimate
Recoil window ±40 ms estimate
Recoil threshold 18 m/s² estimate

Also unchanged: no instrumented tests beyond the launch smoke check; the browser prototype cannot truly pin an unprocessed audio source; compileSdk stays pinned at 36 until platform 37 ships.

A detection accuracy report from a real range session is still the most valuable thing anyone can contribute.

Full changelog: v0.2.0...v0.2.1