import AVFAudio import CallKit import Flutter import PushKit import UIKit import UserNotifications import YandexLoginSDK import flutter_callkit_incoming import flutter_foreground_task @main @objc class AppDelegate: FlutterAppDelegate, PKPushRegistryDelegate, CallkitIncomingAppDelegate { private var voipRegistry: PKPushRegistry? override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { GeneratedPluginRegistrant.register(with: self) SwiftFlutterForegroundTaskPlugin.setPluginRegistrantCallback { registry in GeneratedPluginRegistrant.register(with: registry) } let mainQueue = DispatchQueue.main voipRegistry = PKPushRegistry(queue: mainQueue) voipRegistry?.delegate = self voipRegistry?.desiredPushTypes = [PKPushType.voIP] if #available(iOS 10.0, *) { UNUserNotificationCenter.current().delegate = self } return super.application(application, didFinishLaunchingWithOptions: launchOptions) } @available(iOS 8.0, *) override func application( _ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void ) -> Bool { if YXLSdk.shared.processUserActivity(userActivity) { return true } return super.application( application, continue: userActivity, restorationHandler: restorationHandler ) } override public func application( _ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:] ) -> Bool { return YXLSdk.shared.handleOpen( url, sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String ) } private func conversationUuid(fromActionUrl actionUrl: String?) -> String? { guard let actionUrl = actionUrl, let components = URLComponents(string: actionUrl) else { return nil } let queryItems = components.queryItems ?? [] for key in ["uuid", "conversationUuid", "conversation_uuid"] { if let value = queryItems.first(where: { $0.name == key })?.value, !value.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { return value.trimmingCharacters(in: .whitespacesAndNewlines) } } let segments = components.path.split(separator: "/").map(String.init) for index in segments.indices where segments[index] == "video-call" { let nextIndex = segments.index(after: index) if segments.indices.contains(nextIndex), !segments[nextIndex].isEmpty { return segments[nextIndex] } } return nil } func pushRegistry( _ registry: PKPushRegistry, didUpdate credentials: PKPushCredentials, for type: PKPushType ) { let deviceToken = credentials.token.map { String(format: "%02x", $0) }.joined() SwiftFlutterCallkitIncomingPlugin.sharedInstance?.setDevicePushTokenVoIP(deviceToken) } func pushRegistry(_ registry: PKPushRegistry, didInvalidatePushTokenFor type: PKPushType) { SwiftFlutterCallkitIncomingPlugin.sharedInstance?.setDevicePushTokenVoIP("") } func pushRegistry( _ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void ) { guard type == .voIP else { completion() return } let dict = payload.dictionaryPayload let pushMarkers = [ dict["type"] as? String, dict["notification_type"] as? String, dict["event"] as? String, dict["action"] as? String, dict["push_type"] as? String, dict["call_type"] as? String, ] .compactMap { $0?.lowercased().replacingOccurrences(of: "-", with: "_") } if pushMarkers.contains(where: { $0.contains("video_call") }) && pushMarkers.contains(where: { $0.contains("end") || $0.contains("cancel") }) { SwiftFlutterCallkitIncomingPlugin.sharedInstance?.endAllCalls() completion() return } let nameCaller = (dict["nameCaller"] as? String) ?? (dict["counterpartName"] as? String) ?? (dict["counterpart_name"] as? String) ?? (dict["caller_name"] as? String) ?? (dict["callerName"] as? String) ?? (dict["caller"] as? String) ?? "" let conversationUuid = (dict["conversation_uuid"] as? String) ?? (dict["conversationUuid"] as? String) ?? (dict["conversation_id"] as? String) ?? (dict["chat_uuid"] as? String) ?? (dict["uuid"] as? String) ?? conversationUuid(fromActionUrl: dict["action_url"] as? String) ?? (dict["handle"] as? String) ?? "" let id = [ conversationUuid, dict["call_id"] as? String, dict["id"] as? String, dict["message_id"] as? String, ] .compactMap { $0?.trimmingCharacters(in: .whitespacesAndNewlines) } .first { !$0.isEmpty && UUID(uuidString: $0) != nil } ?? UUID().uuidString let isVideo = (dict["isVideo"] as? Bool) ?? (dict["is_video"] as? Bool) ?? true var extra: [String: Any] = [:] if !conversationUuid.isEmpty { extra["conversationUuid"] = conversationUuid } if let avatar = dict["caller_avatar"] as? String ?? dict["avatar"] as? String ?? dict["avatarUrl"] as? String ?? dict["avatar_url"] as? String ?? dict["counterpartAvatarUrl"] as? String ?? dict["counterpart_avatar_url"] as? String { extra["counterpartAvatarUrl"] = avatar } if let messageId = dict["message_id"] as? String { extra["messageId"] = messageId } if let roomName = dict["room_name"] as? String { extra["roomName"] = roomName } if let localName = dict["localParticipantName"] as? String ?? dict["local_participant_name"] as? String { extra["localParticipantName"] = localName } if let counterpartName = dict["counterpartName"] as? String { extra["counterpartName"] = counterpartName } else if let counterpartName = dict["counterpart_name"] as? String { extra["counterpartName"] = counterpartName } else if !nameCaller.isEmpty { extra["counterpartName"] = nameCaller } let data = flutter_callkit_incoming.Data( id: id, nameCaller: nameCaller, handle: conversationUuid.isEmpty ? nameCaller : conversationUuid, type: 0 ) data.appName = "Итвио" data.duration = 45000 data.handleType = "generic" data.supportsVideo = isVideo data.ringtonePath = "system_ringtone_default" data.configureAudioSession = false data.includesCallsInRecents = false data.extra = extra as NSDictionary SwiftFlutterCallkitIncomingPlugin.sharedInstance?.showCallkitIncoming( data, fromPushKit: true ) { completion() } } func onAccept(_ call: Call, _ action: CXAnswerCallAction) { action.fulfill() } func onDecline(_ call: Call, _ action: CXEndCallAction) { action.fulfill() } func onEnd(_ call: Call, _ action: CXEndCallAction) { action.fulfill() } func onTimeOut(_ call: Call) {} func didActivateAudioSession(_ audioSession: AVAudioSession) {} func didDeactivateAudioSession(_ audioSession: AVAudioSession) {} func providerDidReset() {} }