Skip to content

Wave 4 Android polish: testable HID logic, unit tests + CI#1

Merged
Peterc3-dev merged 1 commit into
masterfrom
wave4-android
May 30, 2026
Merged

Wave 4 Android polish: testable HID logic, unit tests + CI#1
Peterc3-dev merged 1 commit into
masterfrom
wave4-android

Conversation

@Peterc3-dev

Copy link
Copy Markdown
Owner

Summary

A proportionate polish pass on PhoneKB. The app is a hand-built Java project (aapt2 + javac + d8 + apksigner via build.sh) — no Gradle — so this focuses on testable logic, CI, and build robustness rather than restyling working UI code.

  • Extracted pure HID logic out of MainActivity into a new framework-independent class com.phonekb.HidMap: the charToHid keymap, the modifier bitmask (modByte), and the mouse-delta clamp (clampDelta). MainActivity now delegates to it. Behavior is unchanged — the values match the original inline code exactly (the original used literal 2 for shift, which is MOD_SHIFT/0x02).
  • Added 13 JUnit 4 unit tests (test/com/phonekb/HidMapTest.java) covering the keymap, shifted symbols, the quirky digit row (0 = usage 39), unmapped chars, the modifier bitmask, and delta saturation. They run on a plain JVM — no Android SDK, emulator, or device.
  • Added run-tests.sh — a self-contained runner that fetches JUnit into .testlibs/ (gitignored) then compiles and runs the suite.
  • Added .github/workflows/ci.yml (on: [push, pull_request]): a unit-test job (JDK 17 + run-tests.sh) and a build-APK job (JDK 17 + Android SDK + build.sh, uploading the APK as an artifact).
  • Hardened build.sh: it auto-detects the newest installed build-tools/platform (overridable via BUILD_TOOLS_VERSION / COMPILE_SDK) instead of hardcoding 36.1.0 / android-34, and falls back to the JDK's jar when zip is missing. This is what was making the build non-portable.
  • Documented test + CI in the README; gitignored .testlibs/.

Verified locally vs. only in CI

Verified locally on this machine:

  • run-tests.sh — compiles and runs, 13/13 tests pass on OpenJDK 17.
  • build.sh — runs end to end against a real Android platform jar (build-tools 36.0.0, platform android-36.1) and produces a signed 25K PhoneKB.apk. apksigner verify passes and Lcom/phonekb/HidMap; is confirmed present in the DEX, so the refactor compiles and ships.
  • bash -n syntax check on both scripts; YAML of the workflow parses.

Only CI will verify (NOT run locally):

  • The build-apk workflow job on GitHub's ubuntu-latest runner. The local build used a different SDK (auto-detected 36.x); CI pins build-tools;34.0.0 + platforms;android-34, so the exact runner toolchain and android-actions/setup-android@v3 step are unverified until the first CI run. This workflow is the first environment that builds the app from source in CI.
  • No emulator / instrumented tests were run; UI / Activity / Bluetooth code is not unit-tested (would need a device).

Notes

  • No secrets, signing keys, or developer-specific paths were added. build.sh already used $HOME (no hardcoded /home/raz); its debug keystore uses the standard Android android/android debug convention and is generated at build time, not committed.

🤖 Generated with Claude Code

Extract the framework-independent HID logic out of MainActivity into a
pure com.phonekb.HidMap class (charToHid keymap, modifier bitmask,
mouse-delta clamp) and delegate to it. Behavior is unchanged — values
match the original inline code exactly.

- Add 13 JUnit 4 tests for HidMap (run on a plain JVM, no Android SDK).
- Add run-tests.sh: self-contained test runner that fetches JUnit into
  .testlibs/ (gitignored) and compiles + runs the suite.
- Add .github/workflows/ci.yml: runs unit tests and builds a debug APK
  via build.sh on push/PR, uploading the APK as an artifact. This is the
  first CI that compiles the app from source.
- Harden build.sh: auto-detect newest installed build-tools/platform
  (overridable via BUILD_TOOLS_VERSION/COMPILE_SDK) instead of hardcoding
  36.1.0/android-34, and fall back to `jar` when `zip` is unavailable.
- Document test + CI in README; gitignore .testlibs/.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Peterc3-dev

Copy link
Copy Markdown
Owner Author

Independent verification — verdict: solid ✅

Reviewed wave4-android adversarially (build correctness, CI sanity, extraction fidelity, overclaiming, secrets). It holds up.

Extraction is genuine, not a fake stub. The diff moves the inline keymap/clamp/modifier logic out of MainActivity into a pure com.phonekb.HidMap byte-for-byte (2 literals → MOD_SHIFT = 0x02, identical; Math.max/min clamp → clampDelta, equivalent). MainActivity now actually delegates to it (lines 439-441, 539, 572) — it's not a dead parallel class. HidMap has zero Android imports, so it's legitimately JVM-testable.

Tests pass for real. Ran run-tests.sh locally on JDK 17 (same as CI): fetched JUnit 4.13.2 from Maven Central, compiled, OK (13 tests). Tests lock down the quirky bits correctly (HID '1'..'9'=30..38 but '0'=39; shifted symbols reuse digit usage IDs; é/€/NUL → {0,0}).

CI config is sane. No Gradle in this repo — it's a hand-rolled aapt2+javac+d8+apksigner pipeline, and the workflow matches it:

  • unit-tests job: JDK 17 temurin → run-tests.sh (verified reproducible).
  • build-apk job: android-actions/setup-android@v3 + sdkmanager 'platforms;android-34' 'build-tools;34.0.0', with COMPILE_SDK=android-34/BUILD_TOOLS_VERSION=34.0.0 — exactly the env vars build.sh reads. Artifact path build/PhoneKB.apk matches build.sh's output, and if-no-files-found: error fails loudly. No wrong gradle task, no wrong module path, Java version sane.

No secrets / no overclaim. Only credential is the documented throwaway debug keystore (pass:android, generated at build time, gitignored) — standard debug signing. README explicitly scopes the tests to pure logic and says UI/Activity/Bluetooth are NOT covered. Honest.

Minor nits (non-blocking):

  • The full build-apk job couldn't be executed here (no Android SDK in my env), so that leg is validated statically only — the unit-test leg is fully reproduced.
  • A stale PhoneKB.apk (25K) sits committed at repo root while build.sh outputs to gitignored build/. Pre-existing (not touched by this PR), but worth removing so the only APK is the CI-built one.

Net: faithful refactor + real green unit tests + a correct CI pipeline. Recommend merge.

@Peterc3-dev Peterc3-dev merged commit dee2a9d into master May 30, 2026
4 checks passed
@Peterc3-dev Peterc3-dev deleted the wave4-android branch May 30, 2026 20:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant