fix(core,bridge,ios): voice_join survives audio-engine failure; iOS log file
User report from iPhone: 'mute / continuous / PTT buttons missing'
with NO error popup. Root cause: voice_join's audio-engine startup
was failing silently, and the failure propagated out as a hard
error \u2014 which means SessionEvent::VoiceState(true) was never sent
to Dart even though the server-side channel move had already
succeeded. Dart's _inChannel stayed false; every control gated on
_inChannel disappeared while the channel tree continued to show
the user as joined.
This commit makes voice_join lenient on audio-engine failures so
the UI state matches the server-side reality, and also gives iOS
a writable log file so diagnostics from device builds are
recoverable for the first time.
core/chanora_core/src/lib.rs::voice_join
* ensure_audio_running's error is now logged + emitted as
SessionEvent::AudioStopped, but does NOT abort voice_join.
The server move at step 1 already succeeded; failing the
Dart-visible promise here would leave the UI in a phantom
'in-channel visually but no controls' state. After this
commit:
- mic / headset / settings appear in the AppBar
- PTT button appears at the bottom
- status chip shows live audio state ('Mic on/off')
- if audio actually failed (mic permission denied,
no input device, CoreAudio rejecting stream config)
the user can retry by switching modes / channels;
BridgeEvent::AudioStopped wires _audioStarted=false
in Dart so audio-stats poll is honest about the
engine state.
crates/chanora_bridge/src/api.rs::log_file_path
* iOS now writes the log to /home/milkice/Documents/chanora.log
(Documents is the standard user-visible iOS sandbox dir).
* Android remains None pending the bridge JNI init wiring
a writable path (P1 follow-up).
apps/chanora_flutter/ios/Runner/Info.plist
* Adds UIFileSharingEnabled + LSSupportsOpeningDocumentsInPlace
so the Documents directory shows up under 'On My iPhone \u2192
Chanora' in the Files.app. The user can now copy chanora.log
out for support without needing Xcode \u2192 Devices and
Simulators \u2192 Download Container.
Workspace tests: 78/0/1 unchanged.
flutter build ios --release --no-codesign: 22.7 s clean
(Runner.app 29.9 MB).
This commit is contained in:
@@ -72,5 +72,14 @@
|
|||||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
</array>
|
</array>
|
||||||
|
<!-- Make Chanora's Documents folder visible to the Files.app and
|
||||||
|
accessible via iTunes / Finder file-sharing. We write
|
||||||
|
diagnostic logs (chanora.log) into Documents/ so users can
|
||||||
|
export them for support. Both keys are required for the
|
||||||
|
"On My iPhone -> Chanora" listing to appear in Files.app. -->
|
||||||
|
<key>UIFileSharingEnabled</key>
|
||||||
|
<true/>
|
||||||
|
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||||
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -918,8 +918,36 @@ impl ChanoraSession {
|
|||||||
self.emit_voice_state(false).await;
|
self.emit_voice_state(false).await;
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
// 2. Bring the audio engine up.
|
// 2. Bring the audio engine up. Tolerate failure: the
|
||||||
self.ensure_audio_running().await?;
|
// server-side channel move has ALREADY succeeded (step
|
||||||
|
// 1), so the user is in the channel from every other
|
||||||
|
// peer's perspective. Failing voice_join hard here would
|
||||||
|
// leave the UI in a phantom "you're in the channel
|
||||||
|
// visually but no controls" state because the calling
|
||||||
|
// Dart code wouldn't receive the VoiceState(true) event
|
||||||
|
// that gates the AppBar mute icons + the on-screen PTT
|
||||||
|
// button. Honest behaviour: surface the audio error
|
||||||
|
// once on the event channel (via the AudioStopped event
|
||||||
|
// consumers already handle), then continue so the UI
|
||||||
|
// matches reality — user is in the channel, but mic /
|
||||||
|
// speakers may be silent until they resolve the audio
|
||||||
|
// error (e.g. grant mic permission, plug in a working
|
||||||
|
// device).
|
||||||
|
if let Err(audio_err) = self.ensure_audio_running().await {
|
||||||
|
warn!(
|
||||||
|
target: "chanora_core",
|
||||||
|
error = %audio_err,
|
||||||
|
channel_id,
|
||||||
|
"voice_join: server move succeeded but audio engine \
|
||||||
|
failed to start; continuing with no-audio in-channel \
|
||||||
|
state so the UI matches the server-side state"
|
||||||
|
);
|
||||||
|
// Tell subscribers the audio engine is not running so
|
||||||
|
// any audio-stats poll / level meter renders correctly.
|
||||||
|
// The voice_join itself still resolves Ok below so the
|
||||||
|
// UI gains the channel + mute + PTT controls.
|
||||||
|
let _ = self.events_tx.send(SessionEvent::AudioStopped);
|
||||||
|
}
|
||||||
// 3. Belt-and-braces: if the server replied Ok but never
|
// 3. Belt-and-braces: if the server replied Ok but never
|
||||||
// actually moved us (legacy server, command processed
|
// actually moved us (legacy server, command processed
|
||||||
// but rolled back later, etc.) the snapshot poll catches
|
// but rolled back later, etc.) the snapshot poll catches
|
||||||
|
|||||||
@@ -180,8 +180,31 @@ fn log_file_path() -> Option<std::path::PathBuf> {
|
|||||||
.join("chanora.log"),
|
.join("chanora.log"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
#[cfg(target_os = "ios")]
|
||||||
{
|
{
|
||||||
|
// iOS sandbox: write the log to the app's Documents
|
||||||
|
// directory so it persists across launches and can be
|
||||||
|
// pulled via Xcode -> Devices and Simulators -> Download
|
||||||
|
// Container, OR via Files.app on the device (the app
|
||||||
|
// appears under "On My iPhone" once we declare
|
||||||
|
// UIFileSharingEnabled + LSSupportsOpeningDocumentsInPlace
|
||||||
|
// in Info.plist — done in a follow-up).
|
||||||
|
//
|
||||||
|
// HOME on iOS resolves to the app sandbox root; Documents
|
||||||
|
// is the standard user-visible subdirectory.
|
||||||
|
let home = std::env::var_os("HOME")?;
|
||||||
|
Some(
|
||||||
|
std::path::PathBuf::from(home)
|
||||||
|
.join("Documents")
|
||||||
|
.join("chanora.log"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
{
|
||||||
|
// Android log file location is set up via the bridge's
|
||||||
|
// Java-side init that writes the chosen path into an env
|
||||||
|
// var (not currently wired; P1 follow-up). For now we
|
||||||
|
// return None and rely on logcat.
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user