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:
Bernd Steckmeister
2026-10-08 00:01:29 +02:00
co-authored by Claude Opus 5.5
parent c817de924a
commit 441cc6ef54
18 changed files with 750 additions and 3 deletions
@@ -0,0 +1,52 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:integration_test/integration_test.dart';
import 'package:pyramid/core/call_foreground.dart';
/// Android 14+: Bildschirmübertragung darf die App nicht abstürzen lassen
/// (Bernds Test 2026-10-07: „Teilen“ → App weg → Anruf verloren).
///
/// Ablauf wie im Anruf: Zustimmung → Anruf-Dienst mit mediaProjection →
/// Mitschnitt. Der System-Dialog muss bestätigt werden (auf dem Emulator per
/// adb, siehe docs/TESTS_UND_BENCHMARKS.md).
///
/// flutter test integration_test/android_screen_capture_test.dart -d emulator-5554
///
/// PYRAMID_SCREEN_NEGATIVE=1 (per --dart-define) überspringt den Dienst –
/// Gegenprobe: dann stürzt die App wie früher ab.
const _negative = bool.fromEnvironment('PYRAMID_SCREEN_NEGATIVE');
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets(
'Bildschirmübertragung startet ohne Absturz',
(tester) async {
await tester.pumpWidget(const MaterialApp(home: Scaffold()));
if (_negative) {
expect(await Helper.requestCapturePermission(), isTrue);
} else {
expect(
await CallForeground.prepareScreenCapture('test'),
isTrue,
reason: 'Zustimmung oder Dienststart fehlgeschlagen',
);
}
final stream = await navigator.mediaDevices
.getDisplayMedia({'video': true, 'audio': false});
expect(stream.getVideoTracks(), isNotEmpty);
await Future.delayed(const Duration(seconds: 3));
for (final t in stream.getTracks()) {
await t.stop();
}
await stream.dispose();
await CallForeground.screenCaptureStopped('test');
await CallForeground.callEnded('test');
},
skip: !Platform.isAndroid,
timeout: const Timeout(Duration(minutes: 3)),
);
}