Build 9e37201 confirmed on-device that Usage::VoiceCommunication +
MODE_IN_COMMUNICATION + speakerphoneOn=false routes Oboe playout to the
handset earpiece and the callback drains the ring correctly. Next step:
let the user flip speakerphoneOn at runtime so the existing Speaker
button actually switches audio routing instead of just gating writes.
- Cargo.toml (android target): pull in `jni = 0.21` and
`ndk-context = 0.1`. Both are already transitively in the lockfile
via Tauri/Wry, so this just promotes them to direct deps.
- desktop/src-tauri/src/android_audio.rs: new module. Grabs the JavaVM +
current Activity from `ndk_context::android_context()`, attaches a
JNI thread, calls `activity.getSystemService("audio")` to get the
AudioManager, and exposes `set_speakerphone(bool)` +
`is_speakerphone_on()` helpers that call the AudioManager method of
the same name. All gated behind `#[cfg(target_os = "android")]`.
- lib.rs: adds `mod android_audio;` (android only), two new Tauri
commands `set_speakerphone(on)` and `is_speakerphone_on()` — desktop
gets no-op stubs so the same frontend invoke() works everywhere.
Both registered in the invoke_handler.
- desktop/src/main.ts: the Speaker button (previously toggled the
playout-write gate via `toggle_speaker`) now calls `set_speakerphone`
and reads back the new routing state. Labels switched from
"Spk" / "Spk Off" to "Earpiece" / "Speaker" so users can't be
confused into thinking clicking turns audio off. pollStatus no longer
clobbers the spkBtn label based on engine spk_muted, since the two
concepts are now decoupled.
WIP because this has NOT been built or tested yet — committing at night
to save the work. Tomorrow: build #50 with this change, smoke-test the
Handset↔Speaker toggle, then move on to call history + last-contacts UI
and the Speaker-button mute bug on the other phone.