feat(video_calls): removed system ui, fixed wrong app initializing

This commit is contained in:
misha
2026-06-18 15:01:21 +03:00
parent e3c22f8ac8
commit 7493870a3d
2 changed files with 80 additions and 6 deletions

View File

@@ -13,6 +13,7 @@ import flutter_foreground_task
private var voipRegistry: PKPushRegistry?
private var voipDiagnosticsChannel: FlutterMethodChannel?
private let voipDiagnosticsStorageKey = "EatvioVoipDiagnostics"
private let pendingAcceptedVoipCallKey = "EatvioPendingAcceptedVoipCall"
private let voipDiagnosticsChannelName = "com.eatvioapp/voip_diagnostics"
private let appUrlScheme = "eatvio"
@@ -43,11 +44,18 @@ import flutter_foreground_task
)
voipDiagnosticsChannel = channel
channel.setMethodCallHandler { [weak self] call, result in
guard call.method == "drainVoipDiagnostics" else {
result(FlutterMethodNotImplemented)
guard let self else {
result(nil)
return
}
result(self?.drainVoipDiagnostics() ?? [])
switch call.method {
case "drainVoipDiagnostics":
result(self.drainVoipDiagnostics())
case "drainPendingAcceptedVoipCall":
result(self.drainPendingAcceptedVoipCall())
default:
result(FlutterMethodNotImplemented)
}
}
}
@@ -159,6 +167,32 @@ import flutter_foreground_task
return nil
}
private func storePendingAcceptedCall(_ call: Call) {
let payload = sanitizedDiagnosticValue(call.data.toJSON()) as? [String: Any] ?? [:]
UserDefaults.standard.set(payload, forKey: pendingAcceptedVoipCallKey)
}
private func drainPendingAcceptedVoipCall() -> [String: Any]? {
let defaults = UserDefaults.standard
guard let payload = defaults.dictionary(forKey: pendingAcceptedVoipCallKey) else {
return nil
}
defaults.removeObject(forKey: pendingAcceptedVoipCallKey)
return payload
}
private func bringAppToForeground() {
DispatchQueue.main.async {
for scene in UIApplication.shared.connectedScenes {
guard let windowScene = scene as? UIWindowScene else { continue }
for window in windowScene.windows {
window.makeKeyAndVisible()
}
}
self.window?.makeKeyAndVisible()
}
}
private func openAppForAcceptedCall(_ call: Call) {
guard let conversationUuid = conversationUuid(fromCall: call), !conversationUuid.isEmpty else {
recordVoipDiagnostic(
@@ -197,6 +231,8 @@ import flutter_foreground_task
]
)
bringAppToForeground()
UIApplication.shared.open(url, options: [:]) { [weak self] success in
self?.recordVoipDiagnostic(
success ? "accepted_call_open_app_succeeded" : "accepted_call_open_app_failed",
@@ -446,6 +482,7 @@ import flutter_foreground_task
func onAccept(_ call: Call, _ action: CXAnswerCallAction) {
storePendingAcceptedCall(call)
recordVoipDiagnostic(
"callkit_accept_native",
data: [
@@ -456,6 +493,7 @@ import flutter_foreground_task
)
voipDiagnosticsChannel?.invokeMethod("acceptedVoipCall", arguments: call.data.toJSON())
DispatchQueue.main.async { [weak self] in
self?.bringAppToForeground()
self?.openAppForAcceptedCall(call)
}
action.fulfill()

View File

@@ -14,6 +14,7 @@ import 'package:flutter/widgets.dart';
import 'package:flutter_callkit_incoming/entities/call_event.dart';
import 'package:flutter_callkit_incoming/entities/call_kit_params.dart';
import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
class VideoCallCallKitCoordinator {
@@ -57,8 +58,10 @@ class VideoCallCallKitCoordinator {
}
});
await _firebaseApi.registerPushToken();
await _drainPendingNativeAcceptedVoipCall();
WidgetsBinding.instance.addPostFrameCallback((_) {
_scheduleNativeVoipDiagnosticsDrain();
unawaited(_drainPendingNativeAcceptedVoipCall());
});
return;
}
@@ -137,6 +140,29 @@ class VideoCallCallKitCoordinator {
}
}
Future<void> _drainPendingNativeAcceptedVoipCall() async {
if (!Platform.isIOS) return;
try {
final pending = await _voipDiagnosticsChannel.invokeMethod(
'drainPendingAcceptedVoipCall',
);
if (pending == null) return;
await _handleNativeAcceptedVoipCall(pending);
} on PlatformException catch (e, st) {
log(
'VideoCallCallKitCoordinator: failed to drain pending native accept: $e',
name: 'VideoCallCallKit',
stackTrace: st,
);
} catch (e, st) {
log(
'VideoCallCallKitCoordinator: failed to process pending native accept: $e',
name: 'VideoCallCallKit',
stackTrace: st,
);
}
}
Future<void> _onCallKitEvent(CallEvent? event) async {
if (event == null) return;
unawaited(_captureCallKitEventToSentry(event));
@@ -424,7 +450,9 @@ class VideoCallCallKitCoordinator {
_suppressedEndCallKitIds.add(callKitId);
try {
await FlutterCallkitIncoming.endCall(callKitId);
if (!Platform.isIOS) {
await FlutterCallkitIncoming.endCall(callKitId);
}
final binding = WidgetsBinding.instance;
final completer = Completer<void>();
@@ -439,6 +467,10 @@ class VideoCallCallKitCoordinator {
);
});
await completer.future;
if (Platform.isIOS) {
await FlutterCallkitIncoming.endCall(callKitId);
}
} finally {
unawaited(
Future<void>.delayed(const Duration(seconds: 2), () {
@@ -454,8 +486,12 @@ class VideoCallCallKitCoordinator {
String? counterpartName,
String? counterpartAvatarUrl,
String? localParticipantName,
}) {
return VideoCallNotificationNavigator.openIncomingCall(
}) async {
if (Platform.isIOS) {
FlutterNativeSplash.remove();
}
await VideoCallNotificationNavigator.openIncomingCall(
conversationUuid: conversationUuid,
counterpartName: counterpartName,
counterpartAvatarUrl: counterpartAvatarUrl,