fix(calls): Bildschirm teilen sichtbar, Verbindung trotz SDK-Wettlauf, Sprech-Ring; Trenner in Gruppen

Bernds Test 2026-10-08 (Streamen ging weder am PC noch am Handy, Ton kam
nicht an, Trenner blieb manchmal stehen) – mit neuem Live-Anruftest
(zwei Testkonten im selben Prozess) nachgestellt und behoben:

- Bildschirm teilen: Die Stromzuordnung rief ein SDK-getFeeds() auf, das es
  nicht gibt; der Fehler wurde geschluckt → Übertragungen wurden nie
  angezeigt und beim Sender nie als aktiv erkannt. Jetzt typisierte
  Zugriffe (remote/localScreenSharingStream, 'stopped' beachtet).
- Verbindungsaufbau: matrix-SDK verwirft Kandidaten, die vor der fertig
  verarbeiteten Einladung bzw. während des Annehmens eintreffen; unter
  Windows wirft getRemoteDescription zudem statt null zu liefern. Pyramid
  puffert alle Kandidaten der Gegenseite und reicht sie direkt an die
  Verbindung nach. Test: vorher nie verbunden, jetzt ~0,5 s (angerufen)
  bzw. ~2,7 s (anrufend), Ton beide Richtungen, Teilen beide Richtungen.
- SDK-Aufrufe in eigener Zone: harmlose Plugin-Fehler (Lautsprecher unter
  Windows) werden protokolliert statt durchzuschlagen; Ergebnisse/Fehler
  per Completer über die Zonengrenze (sonst ewiges Warten).
- Sprech-Ring auch bei 1:1-Anrufen (Pegel aus WebRTC-Statistiken) – zeigt,
  ob das eigene Mikrofon Ton liefert und ob die Gegenseite ankommt; großes
  Bild springt nicht mehr bei jedem Sprechwechsel.
- „Neue Nachrichten“ mitten in einer Gruppe (mehrere Nachrichten desselben
  Absenders) blieb bis zum erneuten Öffnen stehen: Inline-Trenner jetzt nur
  mit Ungelesenem, blendet aus und trägt den Sprungschlüssel. Live-Test.
- „Anruf läuft“-Leiste nicht schon beim Klingeln.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Bernd Steckmeister
2026-10-08 11:05:46 +02:00
co-authored by Claude Opus 5.5
parent 5485b6d449
commit 8074928505
12 changed files with 793 additions and 97 deletions
+8 -1
View File
@@ -87,7 +87,14 @@ class _UnifiedCallViewState extends State<_UnifiedCallView> {
Widget build(BuildContext context) {
final pt = widget.pt;
final all = _sorted(widget.participants);
final hero = all.firstWhereOrNull((p) => p.id == _heroId) ?? (all.isNotEmpty ? all.first : null);
// Ohne gewählten Hero nicht nach „spricht gerade“ wählen – sonst springt
// das große Bild bei jedem Sprechwechsel hin und her.
final stable = [...widget.participants]..sort((a, b) {
int r(_ParticipantData p) => p.isScreenShare ? 0 : (p.hasVideo ? 1 : 2);
return r(a).compareTo(r(b));
});
final hero = all.firstWhereOrNull((p) => p.id == _heroId) ??
(stable.isNotEmpty ? stable.first : null);
if (widget.isConnecting) {
return Scaffold(
@@ -151,6 +151,7 @@ class VoiceChannelView extends ConsumerWidget {
name: voip.currentCall?.room.getLocalizedDisplayname() ?? 'Remote',
videoWidget: hasRemoteVideo ? rtc.RTCVideoView(voip.remoteRenderer, objectFit: rtc.RTCVideoViewObjectFit.RTCVideoViewObjectFitContain) : null,
isMuted: false,
isSpeaking: voip.isRemoteSpeaking,
hasVideo: hasRemoteVideo,
isScreenShare: isRemoteScreen,
avatarColor: _avatarColors[1],
@@ -165,6 +166,7 @@ class VoiceChannelView extends ConsumerWidget {
name: 'Du',
videoWidget: hasLocalVideo ? rtc.RTCVideoView(voip.localRenderer, objectFit: rtc.RTCVideoViewObjectFit.RTCVideoViewObjectFitContain) : null,
isMuted: voip.isMicMuted,
isSpeaking: voip.isLocalSpeaking,
hasVideo: hasLocalVideo,
isScreenShare: isLocalScreen,
avatarColor: _avatarColors[0],