diff --git a/desktop/src-tauri/src/engine.rs b/desktop/src-tauri/src/engine.rs index 222de17..9cc1e75 100644 --- a/desktop/src-tauri/src/engine.rs +++ b/desktop/src-tauri/src/engine.rs @@ -719,5 +719,13 @@ impl CallEngine { pub async fn stop(self) { self.running.store(false, Ordering::SeqCst); self.transport.close().await.ok(); + // On Android, the Oboe capture/playout streams live inside the + // wzp-native cdylib as a process-global singleton. Explicitly stop + // them here so the mic + speaker are released between calls, matching + // the desktop behaviour where dropping _audio_handle tears down CPAL. + #[cfg(target_os = "android")] + { + crate::wzp_native::audio_stop(); + } } } diff --git a/desktop/src-tauri/src/lib.rs b/desktop/src-tauri/src/lib.rs index dac8aa5..fef0dc5 100644 --- a/desktop/src-tauri/src/lib.rs +++ b/desktop/src-tauri/src/lib.rs @@ -17,9 +17,9 @@ mod engine; #[cfg(target_os = "android")] mod wzp_native; -// CallEngine is only referenced from the non-Android connect/disconnect/etc -// commands; the Android stubs return errors directly. -#[cfg(not(target_os = "android"))] +// CallEngine has a unified impl on both targets now — the Android branch of +// CallEngine::start() routes audio through the standalone wzp-native cdylib +// (loaded via the wzp_native module below), the desktop branch uses CPAL. use engine::CallEngine; use serde::Serialize; @@ -91,7 +91,6 @@ struct CallStatus { } struct AppState { - #[cfg(not(target_os = "android"))] engine: Mutex>, signal: Arc>, } @@ -229,7 +228,6 @@ fn get_app_info() -> Result { }) } -#[cfg(not(target_os = "android"))] #[tauri::command] async fn connect( state: tauri::State<'_, Arc>, @@ -265,7 +263,6 @@ async fn connect( } } -#[cfg(not(target_os = "android"))] #[tauri::command] async fn disconnect(state: tauri::State<'_, Arc>) -> Result { let mut engine_lock = state.engine.lock().await; @@ -277,7 +274,6 @@ async fn disconnect(state: tauri::State<'_, Arc>) -> Result>) -> Result { let engine_lock = state.engine.lock().await; @@ -288,7 +284,6 @@ async fn toggle_mic(state: tauri::State<'_, Arc>) -> Result>) -> Result { let engine_lock = state.engine.lock().await; @@ -299,7 +294,6 @@ async fn toggle_speaker(state: tauri::State<'_, Arc>) -> Result>) -> Result { let engine_lock = state.engine.lock().await; @@ -343,62 +337,6 @@ async fn get_status(state: tauri::State<'_, Arc>) -> Result>, - _app: tauri::AppHandle, - _relay: String, - _room: String, - _alias: String, - _os_aec: bool, - _quality: String, -) -> Result { - Err("audio backend not yet wired on Android (step 3)".into()) -} - -#[cfg(target_os = "android")] -#[tauri::command] -async fn disconnect(_state: tauri::State<'_, Arc>) -> Result { - Ok("not connected".into()) -} - -#[cfg(target_os = "android")] -#[tauri::command] -async fn toggle_mic(_state: tauri::State<'_, Arc>) -> Result { - Err("not connected".into()) -} - -#[cfg(target_os = "android")] -#[tauri::command] -async fn toggle_speaker(_state: tauri::State<'_, Arc>) -> Result { - Err("not connected".into()) -} - -#[cfg(target_os = "android")] -#[tauri::command] -async fn get_status(_state: tauri::State<'_, Arc>) -> Result { - Ok(CallStatus { - active: false, - mic_muted: false, - spk_muted: false, - participants: vec![], - encode_fps: 0, - recv_fps: 0, - audio_level: 0, - call_duration_secs: 0.0, - fingerprint: String::new(), - tx_codec: String::new(), - rx_codec: String::new(), - }) -} - // ─── Signaling commands — platform independent ─────────────────────────────── struct SignalState { @@ -516,7 +454,6 @@ pub fn run() { tracing_subscriber::fmt().init(); let state = Arc::new(AppState { - #[cfg(not(target_os = "android"))] engine: Mutex::new(None), signal: Arc::new(Mutex::new(SignalState { transport: None, fingerprint: String::new(), signal_status: "idle".into(),