hiennguyen92 / flutter_callkit_incoming

Flutter Callkit Incoming
https://pub.dev/packages/flutter_callkit_incoming
MIT License
180 stars 312 forks source link

Application is not getting opened when i click on accept call IOS (Background or Terminated state) #605

Open gdhananjay opened 2 weeks ago

gdhananjay commented 2 weeks ago

I used this plugin for configuring notification call notification in android and ios. Android is working fine. IOS i received notification very well. The only problem i am facing is when i accept the call it is not opening app automatically in IOS. When i open app manually it starts call. But ideally it should launch app on click of accept call.

Note: I added checkAndNavigationCallingPage() in WidgetsBinding.instance.addPostFrameCallback

I am sure i missed upon some configuration param. I am using latest lib. Below is my code from AppDelegate.swift

Please help me on resolving this issue.

import UIKit
import Flutter
import flutter_local_notifications
import flutter_callkit_incoming
import CallKit
import AVFAudio
import PushKit

@main
@objc class AppDelegate: FlutterAppDelegate, PKPushRegistryDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
      // This is required to make any communication available in the action isolate.
      FlutterLocalNotificationsPlugin.setPluginRegistrantCallback { (registry) in
        GeneratedPluginRegistrant.register(with: registry)
      }

      // Added as per documentation of flutter local notification package
      if #available(iOS 10.0, *) {
        UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
      }

      GeneratedPluginRegistrant.register(with: self)

      //Setup VOIP (flutter_callkit_incomming)
      let mainQueue = DispatchQueue.main
      let voipRegistry: PKPushRegistry = PKPushRegistry(queue: mainQueue)
      voipRegistry.delegate = self
      voipRegistry.desiredPushTypes = [PKPushType.voIP]

    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }

   // As per flutter_callkit_incomming Start ///////////////////

    // Handle updated push credentials
    func pushRegistry(_ registry: PKPushRegistry, didUpdate credentials: PKPushCredentials, for type: PKPushType) {
        print(credentials.token)
        let deviceToken = credentials.token.map { String(format: "%02x", $0) }.joined()
        print(deviceToken)
        //Save deviceToken to your server
        SwiftFlutterCallkitIncomingPlugin.sharedInstance?.setDevicePushTokenVoIP(deviceToken)
    }

    func pushRegistry(_ registry: PKPushRegistry, didInvalidatePushTokenFor type: PKPushType) {
        print("didInvalidatePushTokenFor")
        SwiftFlutterCallkitIncomingPlugin.sharedInstance?.setDevicePushTokenVoIP("")
    }

    // Handle incoming pushes
    func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
        print("didReceiveIncomingPushWith")
        guard type == .voIP else { return }

        // Require by
        let id = payload.dictionaryPayload["id"] as? String ?? UUID().uuidString
        let nameCaller = (payload.dictionaryPayload["params"] as? [String: Any])? ["body"] as? String ?? ""
        let appName = (payload.dictionaryPayload["params"] as? [String: Any])? ["title"] as? String ?? ""
        // Phone number/Email/Any
        let handle = payload.dictionaryPayload["handle"] as? String ?? "generic"
        //Video call or audio call
        let isVideo = payload.dictionaryPayload["isVideo"] as? Int ?? 1
        //duration of ring
        let duration = payload.dictionaryPayload["duration"] as? Int ?? 30000
        // ringtone
        let ringtonePath = payload.dictionaryPayload["ringtonePath"] as? String ?? "system_ringtone_default"
        var args: NSDictionary = ["id": id, "nameCaller": nameCaller, "appName": appName, "handle": handle, "type": isVideo, "duration": duration, "ringtonePath": ringtonePath]
        let data = flutter_callkit_incoming.Data(args: args)
        data.extra =  payload.dictionaryPayload["params"] as? NSDictionary ?? [:]
        data.iconName = ""
        data.type = 1;
        print(data);
        SwiftFlutterCallkitIncomingPlugin.sharedInstance?.showCallkitIncoming(data, fromPushKit: true)

        //Make sure call completion()
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
            completion()
        }
    }

   ///// as per flutter_callkit_incomming END //////
}
anhkhoa1208py commented 2 weeks ago

same issue

PT10 commented 2 weeks ago

same issue

chauvansang1997 commented 2 weeks ago

Please check version Podfile.lock file.

gdhananjay commented 2 weeks ago

@chauvansang1997 , Thank you for your reply. Could you please elaborate more on where i did mistake.

Below is my Podfile.lock file version.

amruthamanoj commented 2 weeks ago

Facing same issue. any solution yet?

gdhananjay commented 2 weeks ago

Additional debug logs: When app is in background and when i click on accept call, its throwing below error. (This error is not there when i accept call from foreground)

-[RTIInputSystemClient remoteTextInputSessionWithID:performInputOperation:] perform input operation requires a valid sessionID. inputModality = Keyboard, inputOperation = dismissAutoFillPanel, customInfoType = UIUserInteractionRemoteInputOperations -[RTIInputSystemClient remoteTextInputSessionWithID:performInputOperation:] perform input operation requires a valid sessionID. inputModality = Keyboard, inputOperation = dismissAutoFillPanel, customInfoType = UIUserInteractionRemoteInputOperations -[RTIInputSystemClient remoteTextInputSessionWithID:performInputOperation:] perform input operation requires a valid sessionID. inputModality = Keyboard, inputOperation = dismissAutoFillPanel, customInfoType = UIUserInteractionRemoteInputOperations Snapshotting a view (0x12bc93000, UIKeyboardImpl) that is not in a visible window requires afterScreenUpdates:YES. didReceiveIncomingPushWith AVAudioSession_iOS.mm:2540 Failed to set properties, error: '!int' Error Domain=NSOSStatusErrorDomain Code=560557684 "(null)"

gdhananjay commented 2 weeks ago

One more log on debug scree when this issue occure:

Error answering call: The operation couldn’t be completed. (com.apple.CallKit.error.requesttransaction error 6.)

gdhananjay commented 2 weeks ago

My code flow for accepting call is: When app is in FG or in BG : Use event handler Event.actionCallAccept => Inside it call FlutterCallkitIncoming.endCall => connect to video call internal widget When app is in terminated state: FlutterCallkitIncoming.activeCalls() => Inside it call FlutterCallkitIncoming.endAllCalls() => connect to video call for recent active call using internal widget

This works perfectly fine on android. On IOS For BG and terminated case it gives problem of app is not getting opened and failed with error "Error answering call: The operation couldn’t be completed. (com.apple.CallKit.error.requesttransaction error 6.)"

Removed endCall and endAllCalls , but now For FG call , its giving problem of multiple accept Event.actionCallAccept events. Removing FlutterCallkitIncoming.setCallConnected from event Event.actionCallAccept may solve this issue as per issue mentioned in https://github.com/hiennguyen92/flutter_callkit_incoming/issues/588

gdhananjay commented 1 week ago

I moved endCall and endAllCalls function on actual call end , its working now. Problem i faced because it was working on Android and not on IOS.

sunny0092 commented 1 week ago

same issue

gdhananjay commented 1 week ago

I am able to resolve this, Check comment above