Earpiece switcher and overlay (#3347)
* Add a global control for toggling earpiece mode This will be used by Element X to show an earpiece toggle button in the header. * Add an earpiece overlay * Fix header The header needs to be passed forward as a string to some components and as a bool (hideHeader) to others. Also use a enum instead of string options. * fix top clipping with header * hide app bar in pip * revert android overlay app_bar * Modernize AppBarContext * Style header icon color as desired and switch earpice/speaker icon * fix initial selection when using controlled media * Add "Back to video" button * fix tests * remove dead code * add snapshot test * fix back to video button * Request capability to learn the room name We now need the room name in order to implement the mobile (widget-based) designs with the app bar. * Test the CallViewModel output switcher directly --------- Co-authored-by: Timo <toger5@hotmail.de>
This commit is contained in:
@@ -93,6 +93,7 @@ import {
|
||||
import { observeSpeaker$ } from "./observeSpeaker";
|
||||
import { shallowEquals } from "../utils/array";
|
||||
import { calculateDisplayName, shouldDisambiguate } from "../utils/displayname";
|
||||
import { type MediaDevices } from "./MediaDevices";
|
||||
|
||||
// How long we wait after a focus switch before showing the real participant
|
||||
// list again
|
||||
@@ -1246,6 +1247,51 @@ export class CallViewModel extends ViewModel {
|
||||
this.scope.state(),
|
||||
);
|
||||
|
||||
/**
|
||||
* Whether audio is currently being output through the earpiece.
|
||||
*/
|
||||
public readonly earpieceMode$: Observable<boolean> = combineLatest(
|
||||
[
|
||||
this.mediaDevices.audioOutput.available$,
|
||||
this.mediaDevices.audioOutput.selected$,
|
||||
],
|
||||
(available, selected) =>
|
||||
selected !== undefined && available.get(selected.id)?.type === "earpiece",
|
||||
).pipe(this.scope.state());
|
||||
|
||||
/**
|
||||
* Callback to toggle between the earpiece and the loudspeaker.
|
||||
*
|
||||
* This will be `null` in case the target does not exist in the list
|
||||
* of available audio outputs.
|
||||
*/
|
||||
public readonly audioOutputSwitcher$: Observable<{
|
||||
targetOutput: "earpiece" | "speaker";
|
||||
switch: () => void;
|
||||
} | null> = combineLatest(
|
||||
[
|
||||
this.mediaDevices.audioOutput.available$,
|
||||
this.mediaDevices.audioOutput.selected$,
|
||||
],
|
||||
(available, selected) => {
|
||||
const selectionType = selected && available.get(selected.id)?.type;
|
||||
|
||||
// If we are in any output mode other than spaeker switch to speaker.
|
||||
const newSelectionType =
|
||||
selectionType === "speaker" ? "earpiece" : "speaker";
|
||||
const newSelection = [...available].find(
|
||||
([, d]) => d.type === newSelectionType,
|
||||
);
|
||||
if (newSelection === undefined) return null;
|
||||
|
||||
const [id] = newSelection;
|
||||
return {
|
||||
targetOutput: newSelectionType,
|
||||
switch: () => this.mediaDevices.audioOutput.select(id),
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
public readonly reactions$ = this.reactionsSubject$.pipe(
|
||||
map((v) =>
|
||||
Object.fromEntries(
|
||||
@@ -1336,6 +1382,7 @@ export class CallViewModel extends ViewModel {
|
||||
// A call is permanently tied to a single Matrix room and LiveKit room
|
||||
private readonly matrixRTCSession: MatrixRTCSession,
|
||||
private readonly livekitRoom: LivekitRoom,
|
||||
private readonly mediaDevices: MediaDevices,
|
||||
private readonly encryptionSystem: EncryptionSystem,
|
||||
private readonly connectionState$: Observable<ECConnectionState>,
|
||||
private readonly handsRaisedSubject$: Observable<
|
||||
|
||||
Reference in New Issue
Block a user