Fix bluetooth iOS issue (device auto switches from Bluetooth to speaker) (#3388)

* fix ios bluetooth

Signed-off-by: Timo K <toger5@hotmail.de>

* fix lints

Signed-off-by: Timo K <toger5@hotmail.de>

---------

Signed-off-by: Timo K <toger5@hotmail.de>
This commit is contained in:
Timo
2025-07-14 12:53:09 +02:00
committed by GitHub
parent dbcc0e2c18
commit 8458d198c9
4 changed files with 38 additions and 11 deletions

View File

@@ -1276,7 +1276,7 @@ export class CallViewModel extends ViewModel {
(available, selected) => {
const selectionType = selected && available.get(selected.id)?.type;
// If we are in any output mode other than spaeker switch to speaker.
// If we are in any output mode other than speaker switch to speaker.
const newSelectionType =
selectionType === "speaker" ? "earpiece" : "speaker";
const newSelection = [...available].find(

View File

@@ -264,6 +264,15 @@ class AudioOutput
class ControlledAudioOutput
implements MediaDevice<AudioOutputDeviceLabel, SelectedAudioOutputDevice>
{
// We need to subscribe to the raw devices so that the OS does update the input
// back to what it was before. otherwise we will switch back to the default
// whenever we allocate a new stream.
public readonly availableRaw$ = availableRawDevices$(
"audiooutput",
this.usingNames$,
this.scope,
);
public readonly available$ = combineLatest(
[controlledAvailableOutputDevices$.pipe(startWith([])), iosDeviceMenu$],
(availableRaw, iosDeviceMenu) => {
@@ -311,14 +320,17 @@ class ControlledAudioOutput
},
).pipe(this.scope.state());
public constructor(private readonly scope: ObservableScope) {
public constructor(
private readonly usingNames$: Observable<boolean>,
private readonly scope: ObservableScope,
) {
this.selected$.subscribe((device) => {
// Let the hosting application know which output device has been selected.
// This information is probably only of interest if the earpiece mode has
// been selected - for example, Element X iOS listens to this to determine
// whether it should enable the proximity sensor.
if (device !== undefined) {
logger.info("[controlled-output] setAudioDeviceSelect called:", device);
logger.info("[controlled-output] onAudioDeviceSelect called:", device);
window.controls.onAudioDeviceSelect?.(device.id);
// Also invoke the deprecated callback for backward compatibility
window.controls.onOutputDeviceSelect?.(device.id);
@@ -327,6 +339,9 @@ class ControlledAudioOutput
this.available$.subscribe((available) => {
logger.info("[controlled-output] available devices:", available);
});
this.availableRaw$.subscribe((availableRaw) => {
logger.info("[controlled-output] available raw devices:", availableRaw);
});
}
}
@@ -393,7 +408,7 @@ export class MediaDevices {
AudioOutputDeviceLabel,
SelectedAudioOutputDevice
> = getUrlParams().controlledAudioDevices
? new ControlledAudioOutput(this.scope)
? new ControlledAudioOutput(this.usingNames$, this.scope)
: new AudioOutput(this.usingNames$, this.scope);
public readonly videoInput: MediaDevice<DeviceLabel, SelectedDevice> =