Revert "feat(video_calls): added system ui for call and navigation to the app"

This reverts commit ea5fced456.
This commit is contained in:
misha
2026-06-18 14:41:56 +03:00
parent ea5fced456
commit e3c22f8ac8
4 changed files with 178 additions and 65 deletions

View File

@@ -14,6 +14,7 @@ import flutter_foreground_task
private var voipDiagnosticsChannel: FlutterMethodChannel?
private let voipDiagnosticsStorageKey = "EatvioVoipDiagnostics"
private let voipDiagnosticsChannelName = "com.eatvioapp/voip_diagnostics"
private let appUrlScheme = "eatvio"
override func application(
_ application: UIApplication,
@@ -74,6 +75,37 @@ import flutter_foreground_task
open url: URL,
options: [UIApplication.OpenURLOptionsKey: Any] = [:]
) -> Bool {
if url.scheme == appUrlScheme {
let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
let queryItems = components?.queryItems ?? []
let conversationUuid = queryItems.first { $0.name == "conversationUuid" }?.value?
.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
let callKitId = queryItems.first { $0.name == "callKitId" }?.value?
.trimmingCharacters(in: .whitespacesAndNewlines)
recordVoipDiagnostic(
"accepted_call_url_opened",
data: [
"callKitId": callKitId ?? "",
"hasCallKitId": callKitId != nil,
"conversationUuid": conversationUuid,
"url": url.absoluteString,
]
)
if let callKitId, !callKitId.isEmpty, !conversationUuid.isEmpty {
voipDiagnosticsChannel?.invokeMethod(
"acceptedVoipCall",
arguments: [
"id": callKitId,
"handle": conversationUuid,
"extra": [
"conversationUuid": conversationUuid
],
]
)
}
return true
}
return YXLSdk.shared.handleOpen(
url,
sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String
@@ -108,6 +140,75 @@ import flutter_foreground_task
return nil
}
private func conversationUuid(fromCall call: Call) -> String? {
let extra = call.data.extra as? [String: Any] ?? [:]
for key in ["conversationUuid", "conversation_uuid", "conversation_id", "chat_uuid", "uuid"] {
if let value = extra[key] as? String {
let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)
if !trimmed.isEmpty {
return trimmed
}
}
}
let handle = call.data.handle.trimmingCharacters(in: .whitespacesAndNewlines)
if !handle.isEmpty {
return handle
}
return nil
}
private func openAppForAcceptedCall(_ call: Call) {
guard let conversationUuid = conversationUuid(fromCall: call), !conversationUuid.isEmpty else {
recordVoipDiagnostic(
"accepted_call_missing_conversation_uuid",
data: ["callKitId": call.data.uuid]
)
return
}
var components = URLComponents()
components.scheme = appUrlScheme
components.host = "video-call"
components.queryItems = [
URLQueryItem(name: "callKitId", value: call.data.uuid),
URLQueryItem(name: "conversationUuid", value: conversationUuid)
]
guard let url = components.url else {
recordVoipDiagnostic(
"accepted_call_url_build_failed",
data: [
"callKitId": call.data.uuid,
"conversationUuid": conversationUuid,
]
)
return
}
recordVoipDiagnostic(
"accepted_call_open_app_requested",
data: [
"callKitId": call.data.uuid,
"conversationUuid": conversationUuid,
"url": url.absoluteString,
"appState": UIApplication.shared.applicationState.rawValue,
]
)
UIApplication.shared.open(url, options: [:]) { [weak self] success in
self?.recordVoipDiagnostic(
success ? "accepted_call_open_app_succeeded" : "accepted_call_open_app_failed",
data: [
"callKitId": call.data.uuid,
"conversationUuid": conversationUuid,
"url": url.absoluteString,
]
)
}
}
private func sanitizedDiagnosticValue(_ value: Any?) -> Any {
guard let value = value else {
return ""
@@ -345,6 +446,18 @@ import flutter_foreground_task
func onAccept(_ call: Call, _ action: CXAnswerCallAction) {
recordVoipDiagnostic(
"callkit_accept_native",
data: [
"callKitId": call.data.uuid,
"conversationUuid": conversationUuid(fromCall: call) ?? "",
"data": call.data.toJSON(),
]
)
voipDiagnosticsChannel?.invokeMethod("acceptedVoipCall", arguments: call.data.toJSON())
DispatchQueue.main.async { [weak self] in
self?.openAppForAcceptedCall(call)
}
action.fulfill()
}

View File

@@ -91,6 +91,14 @@
<string>yx5399b03636104c6d920b87c1ad329254</string>
</array>
</dict>
<dict>
<key>CFBundleURLName</key>
<string>Eatvio</string>
<key>CFBundleURLSchemes</key>
<array>
<string>eatvio</string>
</array>
</dict>
</array>
</dict>
</plist>

View File

@@ -30,7 +30,6 @@ class VideoCallCallKitCoordinator {
final VideoCallSessionCubit? _videoCallSessionCubit;
StreamSubscription<CallEvent?>? _eventSubscription;
AppLifecycleListener? _lifecycleListener;
final Map<String, CallKitParams> _callKitParamsById = {};
final Set<String> _acceptHandledCallKitIds = {};
final Set<String> _suppressedEndCallKitIds = {};
@@ -50,17 +49,16 @@ class VideoCallCallKitCoordinator {
if (Platform.isIOS) {
_voipDiagnosticsChannel.setMethodCallHandler((call) async {
if (call.method == 'voipDiagnosticsUpdated') {
_scheduleNativeVoipDiagnosticsDrain();
switch (call.method) {
case 'voipDiagnosticsUpdated':
_scheduleNativeVoipDiagnosticsDrain();
case 'acceptedVoipCall':
await _handleNativeAcceptedVoipCall(call.arguments);
}
});
await _firebaseApi.registerPushToken();
_lifecycleListener = AppLifecycleListener(
onResume: () => unawaited(_openActiveCallKitCallIfNeeded()),
);
WidgetsBinding.instance.addPostFrameCallback((_) {
_scheduleNativeVoipDiagnosticsDrain();
unawaited(_openActiveCallKitCallIfNeeded());
});
return;
}
@@ -83,9 +81,7 @@ class VideoCallCallKitCoordinator {
void dispose() {
unawaited(_eventSubscription?.cancel());
_lifecycleListener?.dispose();
_eventSubscription = null;
_lifecycleListener = null;
_callKitParamsById.clear();
_initialized = false;
}
@@ -289,53 +285,6 @@ class VideoCallCallKitCoordinator {
return _paramsForCallId(id);
}
/// When the user opens the app from the system CallKit UI (e.g. taps "Итвио"),
/// navigate to the in-app call screen for an already-accepted active call.
Future<void> _openActiveCallKitCallIfNeeded() async {
if (!Platform.isIOS) return;
if (_cubit.state.connectPhase != VideoCallConnectPhase.idle) return;
final activeCalls = await FlutterCallkitIncoming.activeCalls();
if (activeCalls.isEmpty) return;
for (final params in activeCalls) {
_cacheCallKitParams(params);
if (!params.isAccepted) continue;
final extra = params.extra ?? <String, dynamic>{};
final conversationUuid = _parseConversationUuid(
extra,
handle: params.handle,
);
if (conversationUuid.isEmpty) continue;
await _deliverIncomingAccept(
callKitId: params.id,
conversationUuid: conversationUuid,
counterpartName:
_parseString(extra, const [
'counterpartName',
'caller_name',
'callerName',
]) ??
params.nameCaller,
counterpartAvatarUrl:
_parseString(extra, const [
'counterpartAvatarUrl',
'avatar',
'avatarUrl',
'caller_avatar',
]) ??
params.avatar,
localParticipantName: _parseString(extra, const [
'localParticipantName',
'local_participant_name',
]),
);
return;
}
}
Future<void> _onNativeAcceptCallHandle(Map<dynamic, dynamic> body) async {
final extra = Map<String, dynamic>.from(body);
final conversationUuid = _parseConversationUuid(extra);
@@ -368,6 +317,57 @@ class VideoCallCallKitCoordinator {
);
}
Future<void> _handleNativeAcceptedVoipCall(Object? arguments) async {
if (!Platform.isIOS) return;
final data = _stringKeyedMap(arguments);
final extra = _stringKeyedMap(data['extra']);
final callKitId = _parseString(data, const ['id', 'uuid']);
if (callKitId == null) {
log(
'VideoCallCallKitCoordinator: native acceptedVoipCall without callKitId, data=$data',
name: 'VideoCallCallKit',
);
return;
}
final conversationUuid = _parseConversationUuid(
extra,
handle: _parseString(data, const ['handle']),
);
if (conversationUuid.isEmpty) {
log(
'VideoCallCallKitCoordinator: native acceptedVoipCall without conversationUuid, data=$data',
name: 'VideoCallCallKit',
);
return;
}
await _deliverIncomingAccept(
callKitId: callKitId,
conversationUuid: conversationUuid,
counterpartName:
_parseString(extra, const [
'counterpartName',
'caller_name',
'callerName',
]) ??
_parseString(data, const ['nameCaller']),
counterpartAvatarUrl:
_parseString(extra, const [
'counterpartAvatarUrl',
'avatar',
'avatarUrl',
'caller_avatar',
]) ??
_parseString(data, const ['avatar']),
localParticipantName: _parseString(extra, const [
'localParticipantName',
'local_participant_name',
]),
);
}
Future<void> _handleAccept(String callKitId) async {
if (_acceptHandledCallKitIds.contains(callKitId)) return;
@@ -387,14 +387,6 @@ class VideoCallCallKitCoordinator {
return;
}
if (Platform.isIOS) {
log(
'VideoCallCallKitCoordinator: iOS accept received (keeping CallKit UI), callKitId=$callKitId, conversationUuid=$conversationUuid',
name: 'VideoCallCallKit',
);
return;
}
await _deliverIncomingAccept(
callKitId: callKitId,
conversationUuid: conversationUuid,

View File

@@ -1,6 +1,6 @@
name: eatvio_app
description: A new Flutter project.
version: 1.3.27+115
version: 1.3.27+114
publish_to: 'none'
environment: