ReviewOS

also looking at this

craft-native/craft

feat(android): the Bluetooth scan pair in Zig — ratchet 56 → 54

#186
Merged glennmichael123 wants to merge feat/android-bluetooth into main
7 files +377 -3
9 days ago

The third listener shim. ScanCallback is an abstract Java class, so the object lives in CraftNative and every result forwards to Zig — with the device name still nullable, so the ?: "Unknown" default stays one decision in one place rather than two that have to agree.

Why Bluetooth and not motion

The motion pair looked like the easier next step and is not. sendEvent("craftMotionUpdate", ...) carries FloatArray values, and JSONObject prints a Float through numberToString — which falls through to Float.toString for anything non-integral. Reproducing that exactly is the Double.toString problem the calendar, database and widget modules all decline to guess at, and getting it wrong would be silently wrong numbers rather than a failure.

Bluetooth's payload is two strings and an int: {"id": address, "name": name ?: "Unknown", "rssi": rssi}.

The scan can not start and still resolve true (#185)

bluetoothScanner?.startScan(bleScanCallback)
activity.runOnUiThread { ...craftBleResolve(true)... }

bluetoothScanner is null when the device has no adapter or when Bluetooth is switched off. The ?. skips the scan, the resolve fires anyway, and nothing distinguishes "scanning, nothing found" from "never started". Bluetooth being off is not an edge case, and the page has no way to tell the user to turn it on.

Reproduced rather than corrected: rejecting on the Zig path alone would mean the same call succeeds or fails depending on which implementation served it, and the difference would only show up as a missing rejection.

The version check lives next to the call that needs it

BLUETOOTH_SCAN arrived in API 31, and below that the shim does not ask for it at all. requestBluetoothScanPermission is in the holder because that is where ActivityCompat.requestPermissions is called — putting the guard on the Zig side would leave a call that throws on older releases one refactor away.

One deliberate difference, written down

result.device.name requires BLUETOOTH_CONNECT on API 31+, which this scan does not request — so it can throw SecurityException inside the callback. The shim lets that escape a framework callback; the holder catches it and drops the result.

That is the only place the two implementations differ, and it is the smaller of the two behaviours.

Testing

zig build test:android passes with the ratchet at 54. The mutation fired: replacing the "Unknown" default with an empty string fails a named test. Both libcraft.so targets still build with no NDK.

7 changed files on the files tab, with 0 review threads.

Sign in to comment on this pull request.