fix(calls): Bildschirmübertragung ohne Absturz, Lautsprecher, Mikrofon im Hintergrund, zurück in den Anruf
Bernds Anruftest Handy↔PC (2026-10-07): - Teilen am Handy: App "verkleinerte sich", Anruf danach weg. Ursache: Android 14+ verlangt vor MediaProjection einen Vordergrunddienst vom Typ mediaProjection; flutter_webrtc startet keinen, der Manifest-Eintrag verwies auf eine nicht mehr existierende Klasse. Neu: CallService (microphone|mediaProjection) + CallForeground: Zustimmung → Dienst (wartet auf echten startForeground) → Mitschnitt. Emulator-Test (Android 16): mit Fix 1080x2400@30 läuft; Gegenprobe ohne Dienst wirft genau 'Media projections require a foreground service ...'. - Dienst läuft während jedes Anrufs: Mikrofon bleibt im Hintergrund aktiv, Benachrichtigung "Anruf läuft" führt zurück. Mikrofon-Typ nur mit Berechtigung, sonst schmalere Variante statt Dienst-Abbruch. - Handy hörte nichts: SDK schaltet Sprachanrufe auf die Hörmuschel. Jetzt Lautsprecher als Standard (wie Discord) + Umschalter in der Anrufleiste. - LiveKit-Teilen am Handy öffnete den PC-Quellen-Auswähler → System-Dialog. - Im offenen Chat (Handy) Leiste "Anruf läuft · Zurück". - Einstellungen → Sprache & Video: Mikrofontest mit Pegel (PC-Diagnose: das Standardgerät lieferte nur Stille – Voicemeeter/virtuelle Geräte). Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
c817de924a
commit
441cc6ef54
@@ -51,6 +51,9 @@ class _CallControls extends StatelessWidget {
|
||||
final VoidCallback onToggleScreen;
|
||||
final VoidCallback onHangUp;
|
||||
final PyramidTheme pt;
|
||||
// Handy: Lautsprecher/Hörmuschel statt „Ton aus“ (Platz in der Leiste).
|
||||
final bool isSpeakerOn;
|
||||
final VoidCallback? onToggleSpeaker;
|
||||
|
||||
const _CallControls({
|
||||
required this.isMuted,
|
||||
@@ -63,10 +66,15 @@ class _CallControls extends StatelessWidget {
|
||||
required this.onToggleScreen,
|
||||
required this.onHangUp,
|
||||
required this.pt,
|
||||
this.isSpeakerOn = true,
|
||||
this.onToggleSpeaker,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final platform = Theme.of(context).platform;
|
||||
final showSpeaker = onToggleSpeaker != null &&
|
||||
(platform == TargetPlatform.android || platform == TargetPlatform.iOS);
|
||||
return SafeArea(
|
||||
top: false,
|
||||
child: Container(
|
||||
@@ -77,7 +85,15 @@ class _CallControls extends StatelessWidget {
|
||||
children: [
|
||||
_VoiceCtrlBtn(icon: isMuted ? Icons.mic_off_rounded : Icons.mic_rounded, danger: isMuted, pt: pt, onTap: onToggleMic),
|
||||
const SizedBox(width: 16),
|
||||
_VoiceCtrlBtn(icon: isDeafened ? Icons.headset_off_rounded : Icons.headphones_rounded, danger: isDeafened, pt: pt, onTap: onToggleDeafen),
|
||||
if (showSpeaker)
|
||||
_VoiceCtrlBtn(
|
||||
icon: isSpeakerOn ? Icons.volume_up_rounded : Icons.phone_in_talk_rounded,
|
||||
active: isSpeakerOn,
|
||||
pt: pt,
|
||||
onTap: onToggleSpeaker!,
|
||||
)
|
||||
else
|
||||
_VoiceCtrlBtn(icon: isDeafened ? Icons.headset_off_rounded : Icons.headphones_rounded, danger: isDeafened, pt: pt, onTap: onToggleDeafen),
|
||||
const SizedBox(width: 16),
|
||||
_VoiceCtrlBtn(icon: isCameraOff ? Icons.videocam_off_rounded : Icons.videocam_rounded, active: !isCameraOff, pt: pt, onTap: onToggleCam),
|
||||
const SizedBox(width: 16),
|
||||
|
||||
@@ -17,6 +17,8 @@ class _UnifiedCallView extends StatefulWidget {
|
||||
final bool isScreenSharing;
|
||||
final Function(Offset)? onQualityMenu;
|
||||
final PyramidTheme pt;
|
||||
final bool isSpeakerOn;
|
||||
final VoidCallback? onToggleSpeaker;
|
||||
|
||||
const _UnifiedCallView({
|
||||
required this.participants,
|
||||
@@ -35,6 +37,8 @@ class _UnifiedCallView extends StatefulWidget {
|
||||
required this.isScreenSharing,
|
||||
this.onQualityMenu,
|
||||
required this.pt,
|
||||
this.isSpeakerOn = true,
|
||||
this.onToggleSpeaker,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -148,6 +152,8 @@ class _UnifiedCallViewState extends State<_UnifiedCallView> {
|
||||
onToggleScreen: widget.onToggleScreen,
|
||||
onHangUp: widget.onHangUp,
|
||||
pt: pt,
|
||||
isSpeakerOn: widget.isSpeakerOn,
|
||||
onToggleSpeaker: widget.onToggleSpeaker,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -3,6 +3,11 @@ part of '../voice_channel.dart';
|
||||
void _handleLiveKitScreenShare(BuildContext context, VoiceChannelService call) async {
|
||||
if (call.isScreenSharing) {
|
||||
await call.stopScreenShare();
|
||||
} else if (Theme.of(context).platform == TargetPlatform.android ||
|
||||
Theme.of(context).platform == TargetPlatform.iOS) {
|
||||
// Handy: ganzer Bildschirm über die System-Zustimmung (kein
|
||||
// Fenster-Auswähler wie am PC).
|
||||
await call.toggleScreenShare();
|
||||
} else {
|
||||
final source = await ScreenSharePicker.show(context);
|
||||
if (source != null) await call.startScreenShare(source.id);
|
||||
|
||||
@@ -58,6 +58,8 @@ class VoiceChannelView extends ConsumerWidget {
|
||||
isScreenSharing: voip.isScreensharing,
|
||||
onQualityMenu: (pos) => _showVoipQualityMenu(context, voip, pos),
|
||||
pt: pt,
|
||||
isSpeakerOn: voip.isSpeakerOn,
|
||||
onToggleSpeaker: voip.toggleSpeaker,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -86,6 +88,8 @@ class VoiceChannelView extends ConsumerWidget {
|
||||
isScreenSharing: call.isScreenSharing,
|
||||
onQualityMenu: (pos) => _showLiveKitQualityMenu(context, call, pos),
|
||||
pt: pt,
|
||||
isSpeakerOn: call.isSpeakerOn,
|
||||
onToggleSpeaker: call.toggleSpeaker,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user