Giter Site home page Giter Site logo

peerwaya / flutter_call_kit Goto Github PK

View Code? Open in Web Editor NEW
55.0 3.0 47.0 138 KB

Flutter iOS CallKit framework - Currently iOS >= 10.0 only

License: BSD 2-Clause "Simplified" License

Kotlin 3.27% Ruby 5.58% Swift 0.74% Objective-C 53.07% Dart 36.98% HTML 0.36%
callkit ios flutter voip webrtc call-kit pushkit

flutter_call_kit's Introduction

Flutter Call Kit

pub package Flutter Call Kit Plugin - Currently iOS >= 10.0 only

Motivation

Flutter Call Kit utilises a brand new iOS 10 framework CallKit to make the life easier for VoIP developers using Flutter.

Note 1: This plugin works for only iOS. No android support yet

Note 2 This plugin was inspired by react-native-keep

For more information about CallKit on iOS, please see Official CallKit Framework Document or Introduction to CallKit by Xamarin

iOS

Connection Service

Usage

Add flutter_call_kit as a dependency in your pubspec.yaml file.

Example

import 'package:flutter/material.dart';
import 'dart:async';
import 'package:uuid/uuid.dart';
import 'package:flutter_call_kit/flutter_call_kit.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  bool _configured;
  String _currentCallId;
  FlutterCallKit _callKit = FlutterCallKit();
  @override
  void initState() {
    super.initState();
    configure();
  }

  Future<void> configure() async {
    _callKit.configure(
      IOSOptions("My Awesome APP",
          imageName: 'sim_icon',
          supportsVideo: false,
          maximumCallGroups: 1,
          maximumCallsPerCallGroup: 1),
      didReceiveStartCallAction: _didReceiveStartCallAction,
      performAnswerCallAction: _performAnswerCallAction,
      performEndCallAction: _performEndCallAction,
      didActivateAudioSession: _didActivateAudioSession,
      didDisplayIncomingCall: _didDisplayIncomingCall,
      didPerformSetMutedCallAction: _didPerformSetMutedCallAction,
      didPerformDTMFAction: _didPerformDTMFAction,
      didToggleHoldAction: _didToggleHoldAction,
    );
    setState(() {
      _configured = true;
    });
  }

  /// Use startCall to ask the system to start a call - Initiate an outgoing call from this point
  Future<void> startCall(String handle, String localizedCallerName) async {
    /// Your normal start call action
    await _callKit.startCall(currentCallId, handle, localizedCallerName);
  }

  Future<void> reportEndCallWithUUID(String uuid, EndReason reason) async {
    await _callKit.reportEndCallWithUUID(uuid, reason);
  }

  /// Event Listener Callbacks

  Future<void> _didReceiveStartCallAction(String uuid, String handle) async {
    // Get this event after the system decides you can start a call
    // You can now start a call from within your app
  }

  Future<void> _performAnswerCallAction(String uuid) async {
    // Called when the user answers an incoming call
  }

  Future<void> _performEndCallAction(String uuid) async {
    await _callKit.endCall(this.currentCallId);
    _currentCallId = null;
  }

  Future<void> _didActivateAudioSession() async {
    // you might want to do following things when receiving this event:
    // - Start playing ringback if it is an outgoing call
  }

  Future<void> _didDisplayIncomingCall(String error, String uuid, String handle,
      String localizedCallerName, bool fromPushKit) async {
    // You will get this event after RNCallKeep finishes showing incoming call UI
    // You can check if there was an error while displaying
  }

  Future<void> _didPerformSetMutedCallAction(bool mute, String uuid) async {
    // Called when the system or user mutes a call
  }

  Future<void> _didPerformDTMFAction(String digit, String uuid) async {
    // Called when the system or user performs a DTMF action
  }

  Future<void> _didToggleHoldAction(bool hold, String uuid) async {
    // Called when the system or user holds a call
  }

  String get currentCallId {
    if (_currentCallId == null) {
      final uuid = new Uuid();
      _currentCallId = uuid.v4();
    }

    return _currentCallId;
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Plugin example app'),
        ),
        body: Center(
          child: Text('Flutter Call Kit Configured: $_configured\n'),
        ),
      ),
    );
  }
}

Receiving a call when the application is not reachable.

In some case your application can be unreachable :

  • when the user kill the application
  • when it's in background since a long time (eg: after ~5mn the os will kill all connections).

To be able to wake up your application to display the incoming call, you can use https://github.com/peerwaya/flutter_voip_push_notification on iOS.

You have to send a push to your application with a library supporting PushKit pushes for iOS.

PushKit

Since iOS 13, you'll have to report the incoming calls that wakes up your application with a VoIP push. Add this in your AppDelegate.m if you're using VoIP pushes to wake up your application :

- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type withCompletionHandler:(void (^)(void))completion {
  // Process the received push
  [FlutterVoipPushNotificationPlugin didReceiveIncomingPushWithPayload:payload forType:(NSString *)type];

  // Retrieve information like handle and callerName here
  // NSString *uuid = /* fetch for payload or ... */ [[[NSUUID UUID] UUIDString] lowercaseString];
  // NSString *callerName = @"caller name here";
  // NSString *handle = @"caller number here";

  [FlutterCallKitPlugin reportNewIncomingCall:uuid handle:handle handleType:@"generic" hasVideo:false localizedCallerName:callerName fromPushKit: YES];

  completion();
}

Contributing

Any pull request, issue report and suggestion are highly welcome!

flutter_call_kit's People

Contributors

egorlogachev avatar jrkim123us avatar peerwaya avatar victoruvarov avatar zi6xuan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

flutter_call_kit's Issues

Can't run application

There is a problem during the building step.
My steps:

  1. cd ios
  2. flutter clean
  3. flutter pub get
  4. pod update (log item is: Installing flutter_call_kit (0.0.1))

Screenshot at Apr 28 14-18-31

Here is the flutter doctor -v output
[✓] Flutter (Channel stable, 1.22.6, on macOS 11.2.3 20D91 darwin-arm, locale ru-RU)
• Flutter version 1.22.6 at /users/locskot/dev/sdk/Flutter
• Framework revision 9b2d32b605 (3 months ago), 2021-01-22 14:36:39 -0800
• Engine revision 2f0af37152
• Dart version 2.10.5

[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
• Android SDK at /Users/locskot/Library/Android/sdk
• Platform android-30, build-tools 30.0.3
• Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
• All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 12.5)
• Xcode at /Applications/Xcode.app/Contents/Developer
• Xcode 12.5, Build version 12E262
• CocoaPods version 1.10.1

[!] Android Studio (version 4.1)
• Android Studio at /Applications/Android Studio.app/Contents
✗ Flutter plugin not installed; this adds Flutter specific functionality.
✗ Dart plugin not installed; this adds Dart specific functionality.
• Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)

No implementation found for method startCall

When executing await _callKit.startCall(currentCallId, handle, localizedCallerName); I am getting the error MissingPluginException (MissingPluginException(No implementation found for method startCall on channel com.peerwaya/flutter_callkit_plugin)). The plugin is installed and in my Podfile I have it set to platform :ios, '10.0'. Anyone else run into this problem or have any idea how to resolve it? I have tried running flutter clean, as well as completely deleting the app from my phone and reinstalling it.

Trying to run the sample

Hi,

First of all thank you for your effort on this library.
I created a project with your code sample, then put a print("...") on each function callbacks and debug it on device.
My app it's open and then i made call to my iPhone.
The iOS caller UI fires normally, (not the UI that you put as screen shot), i answer the call then end-it but nothing happens.
Can you give me a hint to make it work properly?

Thank you,
Artur

End a call in the background

Currently you can only report a new call in the background with

FlutterCallKitPlugin.reportNewIncomingCall(uuid, handle: handle, handleType: handleType, hasVideo: true, localizedCallerName: caller, fromPushKit: true)

is it possible to add a similar static method to also end the call.

Maybe something like this

FlutterCallKitPlugin.reportEndCall(uuid, ....)

Currently I have to extend your plugin to get it working

extension FlutterCallKitPlugin {
    static func reportEndCall(_ uuid: String) {
        if #available(iOS 10.0, *) {
            let endCallAction = CXEndCallAction.init(call: UUID(uuidString: uuid)!)
            let transaction = CXTransaction(action: endCallAction)
            
            CXCallController().request(transaction) { (error) in
                if error != nil {
                    endCallAction.fail()
                }
                endCallAction.fulfill()
            }
        }
    }
}

No callback is fired when video button is clicked

  • When receiving a voip call with CallKit in the background and video is selected you are given a screen that has a video button.
  • When clicking that button, no callback is fired. Is this a missing feature?

IOS 15. and xcode 13 flutter call kit have issue

Command CompileSwiftSources failed with a nonzero exit code
Undefined symbols for architecture x86_64:
"_AVAudioSessionInterruptionTypeKey", referenced from:
-[FlutterCallKitPlugin provider:didActivateAudioSession:] in FlutterCallKitPlugin.o
"_AVAudioSessionInterruptionNotification", referenced from:
-[FlutterCallKitPlugin provider:didActivateAudioSession:] in FlutterCallKitPlugin.o
"_AVAudioSessionCategoryPlayAndRecord", referenced from:
-[FlutterCallKitPlugin configureAudioSession] in FlutterCallKitPlugin.o
"_AVAudioSessionInterruptionOptionKey", referenced from:
-[FlutterCallKitPlugin provider:didActivateAudioSession:] in FlutterCallKitPlugin.o
"OBJC_CLASS$_AVAudioSession", referenced from:
objc-class-ref in FlutterCallKitPlugin.o
"_AVAudioSessionModeVoiceChat", referenced from:
-[FlutterCallKitPlugin configureAudioSession] in FlutterCallKitPlugin.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Command CompileSwiftSources failed with a nonzero exit code

App getting crashed when in background

The app is working fine when it is foreground but when VOIP notification is received the app is getting crashed in background. Can you please help with this.

FYI: All the capabilities are given in Xcode and all the configurations are done as per the documentation. Also the payload coming from the server is also correct.

What does imageName stands for?

Hi, I've already implemented the plugin but I don't know how to show my app icon on the call interface. What should I put on the imageName param of the options? Thank you!

Processing an incoming call on a locked screen.

Hello!

There is a problem with processing an incoming call on a locked screen.
When the screen is unlocked - everything is super, the CallKit window appears about the incoming call with two buttons "Answer" and "Cancel". If you click Answer, the "performAnswerCallAction" event will trigger, I will process it, the CallKit screen will disappear, and my screen (my application) will remain, where I will continue to work with the current call.
If your phone is locked - another CallKit screen with a slider appears and after a pile through the slider - it is replaced by another CallKit screen.
But my application is not launched in the background and the only way to get to it is to press the custom button on the last CallKit screen, then the phone will request the lock code and only then will launch the main ViewController application.

The question is the following - how in the event handler "performAnswerCallAction" when the phone screen is locked to run the main application with Flutter in the background?

How to implement with swift support?

i try to convert to swift code for get push voip and wakes up my app:

  // Handle incoming pushes
  func pushRegistry(_ registry: PKPushRegistry,
                    didReceiveIncomingPushWith payload: PKPushPayload,
                    for type: PKPushType, completion: @escaping () -> Void) {
     FlutterVoipPushNotificationPlugin.didReceiveIncomingPush(
        with: payload,
        forType: type.rawValue
     )
    let payloadDict = payload.dictionaryPayload["aps"] as! Dictionary<String, Any>
    let message = payloadDict["msg_data"] as! String
    print(message)
    let data = try! JSONSerialization.jsonObject(with: message.data(using: .utf8)!, options: []) as! [String:Any]
    let uuid = data["msg_id"] as! String
    let uID = data["u_id"] as! Int
    let callerName = data["msg_name"] as! String

    FlutterCallKitPlugin.reportNewIncomingCall(
        uuid,
        handle: String(uID),
        handleType: "generic",
        hasVideo: false,
        localizedCallerName: callerName,
        fromPushKit: true
    )
  }
  1. push voip woking ok, my app can wakes up and get push payload.
  2. but i get error: displayIncomingCall throws an error: parameter 'localizedName' cannot be nil'
  3. in reactnative has result but still don't know how to solver it in Flutter: here
  4. i test with swift code then my app show call scene. But still cannot process call, of course because this code working in swift not Flutter:
    provider.reportNewIncomingCall(with: UUID(), update: update, completion: { error in })

So how to implement it with swift support? Thank.

Sorry my bad english!

how do i end an incoming call display when sender cancels the call?

Hi,

When Alice calls Bob, my server sends a VoIP notification to Bob who receives an incoming call display on his phone. When Alice cancels her call, I'd like to kill the incoming call display on Bob's phone but I haven't been able to figure out how to do this.

Can anyone help me?

Thanks,
DK

Display Incoming Call crashes for IOS

Hi!

I am using FCM and have background handler there. On Background msg I try to initialise and call displayIncomingCall method, but on calling this method app just crashes. Should I have additional settings for IOS or is it required to use voip?

Crash app when execute 'initLocalizedName' of CXProviderConfiguration.

Hi @peerwaya !
I use flutter_call_kit and flutter_voip_push_notification plugin.
When I sent VoIP push notification, app carashed.
What should I do ?

This is my crash log.

CrashReporter Key:   22822194399095553f124ce501ccdff73e36e0f9
Hardware Model:      iPhone10,3
Process:             Runner [5296]
Path:                /private/var/containers/Bundle/Application/***************/Runner.app/Runner
Identifier:          *****************
Version:             1 (1.0.0)
Code Type:           ARM-64 (Native)
Role:                Foreground
Parent Process:      launchd [1]
Coalition:           com.******************** [1548]


Date/Time:           2020-01-08 05:04:04.3427 +0900
Launch Time:         2020-01-08 05:04:00.0822 +0900
OS Version:          iPhone OS 13.2.3 (17B111)
Release Type:        User
Baseband Version:    5.11.01
Report Version:      104

Exception Type:  EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note:  EXC_CORPSE_NOTIFY
Triggered by Thread:  0

Last Exception Backtrace:
0   CoreFoundation                	0x1837e980c __exceptionPreprocess + 220
1   libobjc.A.dylib               	0x183511fa4 objc_exception_throw + 55
2   CoreFoundation                	0x1836def84 +[NSException raise:format:] + 107
3   CallKit                       	0x19156ac84 -[CXProviderConfiguration initWithLocalizedName:] + 135
4   flutter_call_kit              	0x10be4200c +[FlutterCallKitPlugin getProviderConfiguration:] + 40972 (FlutterCallKitPlugin.m:369)
5   flutter_call_kit              	0x10be3fe7c +[FlutterCallKitPlugin initCallKitProvider] + 32380 (FlutterCallKitPlugin.m:66)
6   flutter_call_kit              	0x10be42930 +[FlutterCallKitPlugin reportNewIncomingCall:handle:handleType:hasVideo:localizedCallerName:fromPushKit:] + 43312 (FlutterCallKitPlugin.m:489)
7   Runner                        	0x1006954e8 specialized AppDelegate.pushRegistry(_:didReceiveIncomingPushWith:for:completion:) + 38120 (AppDelegate.swift:47)
8   Runner                        	0x100694e0c @objc AppDelegate.pushRegistry(_:didReceiveIncomingPushWith:for:completion:) + 36364 (<compiler-generated>:39)
9   PushKit                       	0x1974c7928 __73-[PKPushRegistry voipPayloadReceived:mustPostCall:withCompletionHandler:]_block_invoke + 287
10  libdispatch.dylib             	0x1834b6610 _dispatch_call_block_and_release + 23
11  libdispatch.dylib             	0x1834b7184 _dispatch_client_callout + 15
12  libdispatch.dylib             	0x18349a34c _dispatch_main_queue_callback_4CF$VARIANT$armv81 + 995
13  CoreFoundation                	0x1837673a8 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 11
14  CoreFoundation                	0x18376239c __CFRunLoopRun + 2003
15  CoreFoundation                	0x1837618a0 CFRunLoopRunSpecific + 463
16  GraphicsServices              	0x18d6b9328 GSEventRunModal + 103
17  UIKitCore                     	0x187852740 UIApplicationMain + 1935
18  Runner                        	0x100693688 main + 30344 (AppDelegate.swift:9)
19  libdyld.dylib                 	0x1835ec360 start + 3


Thread 0 name:  Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0   libsystem_kernel.dylib        	0x00000001835e1ec4 __pthread_kill + 8
1   libsystem_pthread.dylib       	0x0000000183501c00 pthread_kill$VARIANT$armv81 + 192
2   libsystem_c.dylib             	0x0000000183451844 abort + 100
3   libc++abi.dylib               	0x00000001835aa7d4 __cxa_bad_cast + 0
4   libc++abi.dylib               	0x00000001835aa9c4 demangling_unexpected_handler+ 6596 () + 0
5   libobjc.A.dylib               	0x0000000183512258 _objc_terminate+ 25176 () + 124
6   libc++abi.dylib               	0x00000001835b7304 std::__terminate(void (*)+ 58116 ()) + 16
7   libc++abi.dylib               	0x00000001835b729c std::terminate+ 58012 () + 44
8   libdispatch.dylib             	0x00000001834b7198 _dispatch_client_callout + 36
9   libdispatch.dylib             	0x000000018349a34c _dispatch_main_queue_callback_4CF$VARIANT$armv81 + 996
10  CoreFoundation                	0x00000001837673a8 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12
11  CoreFoundation                	0x000000018376239c __CFRunLoopRun + 2004
12  CoreFoundation                	0x00000001837618a0 CFRunLoopRunSpecific + 464
13  GraphicsServices              	0x000000018d6b9328 GSEventRunModal + 104
14  UIKitCore                     	0x0000000187852740 UIApplicationMain + 1936
15  Runner                        	0x0000000100693688 main + 30344 (AppDelegate.swift:9)
16  libdyld.dylib                 	0x00000001835ec360 start + 4

My AppDelegate.swift

import UIKit
import Flutter
import Firebase
import PushKit
import flutter_voip_push_notification
import flutter_call_kit

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate, PKPushRegistryDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    
    let buildMode = BuildMode.current
    let flavor = Flavor.current
    print("buildMode: \(buildMode), flavor: \(flavor)")

    configureFirebase()
    
    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
    
//    func pushRegistry(_ registry: PKPushRegistry,
//                      didReceiveIncomingPushWith payload: PKPushPayload,
//                      for type: PKPushType,
//                      completion: @escaping () -> Void){
//        // Register VoIP push token (a property of PKPushCredentials) with server
//        FlutterVoipPushNotificationPlugin.didReceiveIncomingPush(with: payload, forType: type.rawValue)
//    }
    
    func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
        NSLog("didUpdatePushCredentials: \(pushCredentials.token)")
        // Process the received push
        FlutterVoipPushNotificationPlugin.didUpdate(pushCredentials, forType: type.rawValue);
    }
    
    func pushRegistry(_ registry: PKPushRegistry,
                      didReceiveIncomingPushWith payload: PKPushPayload,
                      for type: PKPushType,
                      completion: @escaping () -> Void) {
        let uuid = NSUUID().uuidString
        let handle = "+819011111111"
        let callerName = "Robert"

        FlutterCallKitPlugin.reportNewIncomingCall(uuid, handle: handle, handleType: "generic", hasVideo: false, localizedCallerName: callerName, fromPushKit: true)
        // Process the received push
        FlutterVoipPushNotificationPlugin.didReceiveIncomingPush(with: payload, forType: type.rawValue)

        completion()
    }
}

extension Flavor {
    var firebaseOptions: FirebaseOptions {
        let filename = { () -> String in
            let base = "GoogleService-Info"
            switch Flavor.current {
            case .development: return "\(base)-Development"
            case .staging: return "\(base)-Staging"
            case .production: return "\(base)-Production"
            }
        }()
        let path = Bundle.main.path(forResource: filename, ofType: "plist")!
        return FirebaseOptions(contentsOfFile: path)!
    }
}

private func configureFirebase() {
    let firOptions = Flavor.current.firebaseOptions
    FirebaseApp.configure(options: firOptions)
}

My flutter version

Flutter 1.13.8-pre.3 • channel master • https://github.com/flutter/flutter.git
Framework • revision 0aed0b61a1 (5 days ago) • 2020-01-03 17:53:54 -0800
Engine • revision eb139936eb
Tools • Dart 2.8.0 (build 2.8.0-dev.0.0 2f57602411)

Missing submodule 'AVFoundation.AVAudioSession'

Can't run saying /Users/user/flutter2/.pub-cache/git/flutter_call_kit-f6310f0b3c574235a8dd7b605730d56a79bc3415/ios/Classes/FlutterCallKitPlugin.m:3:2: Missing submodule 'AVFoundation.AVAudioSession'

version issue

Because App name depends on flutter_call_kit 0.0.3 which doesn't match any versions, version solving failed.
pub get failed (1; Because eMedHub depends on flutter_call_kit 0.0.3 which doesn't match any versions, version solving failed.)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.