Skip to content

Templates: Bluetooth Classic peripheral codegen - #4

Merged
GenericJam merged 1 commit into
GenericJam:masterfrom
HeroesLament:migrate/bt
May 16, 2026
Merged

Templates: Bluetooth Classic peripheral codegen#4
GenericJam merged 1 commit into
GenericJam:masterfrom
HeroesLament:migrate/bt

Conversation

@HeroesLament

@HeroesLamentHeroesLament commented May 15, 2026

Copy link
Copy Markdown

Companion to GenericJam/mob#9 (Mob.Bt runtime).

Generates the Kotlin BroadcastReceivers, JNI native_* externs, and AndroidManifest permissions for the Mob.Bt + Mob.Bt.Hfp / Spp / Hid API.

See the commit message for full details.

cc @GenericJam

Companion to mob#5+ (Mob.Bt runtime). Generates the Kotlin
BroadcastReceivers, JNI native_* externs, and AndroidManifest
permissions for the Mob.Bt + Mob.Bt.Hfp / Spp / Hid API.
## What's generated
### priv/templates/.../AndroidManifest.xml.eex (+15 lines)
- BLUETOOTH_CONNECT / SCAN / ADVERTISE permissions
- uses-feature bluetooth=true (required)
- uses-feature bluetooth_le=false (not required for Classic)
### priv/templates/.../MobBridge.kt.eex (+541 lines)
16 @JvmStatic methods matching the Mob.Bt NIF surface:
- bt_list_paired, bt_start_discovery, bt_cancel_discovery
- bt_pair, bt_unpair, bt_disconnect
- bt_hfp_connect, bt_hfp_subscribe_vendor_at (with companyIdsJson),
bt_hfp_send_vendor_at, bt_hfp_start_sco, bt_hfp_stop_sco,
bt_hfp_send_audio
- bt_spp_connect, bt_spp_write
- bt_hid_connect, bt_hid_subscribe_raw
5 BroadcastReceivers handle the Android side:
- ACTION_FOUND / ACTION_DISCOVERY_STARTED / _FINISHED (discovery)
- ACTION_BOND_STATE_CHANGED (pairing)
- BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED (HFP)
- ACTION_VENDOR_SPECIFIC_HEADSET_EVENT (vendor AT)
- BluetoothDevice.ACTION_ACL_DISCONNECTED (cross-profile disconnect)
The vendor AT receiver registers IntentFilter categories ONLY for the
BT SIG company IDs the caller specified in companyIdsJson. Empty list
is valid — the receiver registers but no events route through.
### priv/templates/.../beam_jni.c.eex (+275 lines)
26 native_* JNI thunks invoking the matching mob_deliver_bt_* C exports.
## Coordination
Pair with mob's migrate/bt branch (Mob.Bt runtime). Either order
works at build time — the runtime short-circuits to :unsupported when
the matching MobBridge methodID isn't present — but the user-visible
feature only works end-to-end with both in place.
GenericJam added a commit that referenced this pull request May 16, 2026
Generates the Kotlin BroadcastReceivers, JNI native_* externs, and
AndroidManifest permissions for the Mob.Bt + Mob.Bt.Hfp / Spp / Hid
runtime API (companion to mob#9).
Includes follow-on review fixes: 3 new generator-test cases asserting
the BT permissions, JNI thunks, and external fn declarations land
correctly in the generated app.
@GenericJam
GenericJam merged commit 6d058fc into GenericJam:masterMay 16, 2026
1 check passed
GenericJam added a commit that referenced this pull request May 16, 2026
Ships HeroesLament's Mob.Bt template work (#4) to Hex so newly
generated apps include the BT runtime wiring out of the box.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
GenericJam added a commit that referenced this pull request May 17, 2026
Two bugs that surfaced in projects generated from the current master.
Both were originally fixed in 0.3.2 (see CHANGELOG) and reintroduced
when I resolved the import-block conflict during the PR #4 merge by
taking the union of both sides' imports — not realising the BT-PR
imports were the duplicates the 0.3.2 fix had specifically removed.
beam_jni.c.eex:
- Restored the closing `}` for nativeDeliverVendorUsbEvent before the
BT JNI thunks. Without it, clang sees nested function definitions
and rejects every JNIEXPORT that follows ("function definition is
not allowed here"). C compilation fails immediately.
MobBridge.kt.eex:
- Removed three duplicate imports (IntentFilter, ConcurrentHashMap,
AtomicInteger). Each now appears exactly once in the alphabetised
bottom-of-file import block. kotlinc was rejecting with
"Conflicting import" so gradleDebug failed.
Generated-output verification: `mix mob.new mob_plugin_fix_test`
produced a project whose MobBridge.kt has each import exactly once
and whose beam_jni.c has the closing brace before the BT thunks
section.
Note: generator tests grep rendered output but don't compile, which
is exactly why both regressions slipped through. Mentioned this gap
in 0.3.2's CHANGELOG — should consider a compile-check step in the
generator test suite as a follow-up.
Reported by external user on master 2026-05-17.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
GenericJam added a commit that referenced this pull request May 17, 2026
Two cheap generator-output checks for the regression classes that
slipped past every string-match assertion in 0.3.2 AND 0.3.4:
- `beam_jni.c has balanced braces`: counts `{` vs `}` in the
rendered C file. Catches "missing closing brace turns subsequent
function defs into nested-defs syntax error" class. Naive (would
miscount strings containing literal braces, but the JNI template
has no JSON or printf-with-braces in strings, so reliable here).
- `MobBridge.kt has no duplicate imports`: parses `^import ` lines
and asserts uniqueness. Catches "merge resolved by union of both
sides' imports, but one side's imports were duplicates of the
other's already-present-elsewhere block" — the exact shape that
bit us when merging PR #4 (post-0.3.2-dedup) by union-resolving
the import conflict.
Both validated red-then-green: temporarily reverted each fix,
confirmed the corresponding test fails with the right message, then
re-applied the fix and confirmed both go green.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
GenericJam added a commit that referenced this pull request May 17, 2026
Tier-1 structural lints (cheap, no toolchain) + tier-3 compile check
(NDK clang, skipped without NDK installed). Caught a real regression
during validation — a duplicate 276-line BT JNI block in
beam_jni.c.eex (also from the PR #4 union-merge fallout). Removed.
lib/mob_new/templates/lint.ex — 8 structural checks:
- balanced_braces / balanced_parens / balanced_brackets
- no_eex_leaks (detects `<%=`, `<% `, ` %>` survivors)
- unique_kotlin_imports / unique_swift_imports
- external_fun_jni_consistency (Kotlin externs ↔ C JNI thunks)
- check_kotlin / check_c / check_swift aggregate funs
test/mob_new/templates/lint_test.exs — 25 unit tests; red-then-green
validated each check.
test/mob_new/project_generator_test.exs:
- Refactored the existing brace-balance + import-uniqueness tests
to use Lint (single source of truth, cleaner failure messages).
- Added cross-file JNI/Kotlin consistency test.
- Added `clang -fsyntax-only` test tagged :requires_android_ndk
so it skips cleanly on systems without the NDK.
beam_jni.c.eex: dropped lines 533-808 (the duplicate BT block).
The first BT block (lines 256-531) is the canonical copy.
Caught by the clang test on first run — exactly the kind of bug
the test was meant to catch. Tier-3 paid for itself immediately.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for freeto 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.

2 participants

@HeroesLament@GenericJam