├── .gitignore ├── .metadata ├── CHANGELOG.md ├── LICENSE ├── README.md ├── android ├── .classpath ├── .gitignore ├── .project ├── .settings │ ├── org.eclipse.buildship.core.prefs │ └── org.eclipse.jdt.core.prefs ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── kotlin │ └── com │ └── example │ └── flutter_call_kit │ └── FlutterCallKitPlugin.kt ├── example ├── .gitignore ├── .metadata ├── README.md ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── flutter_call_kit_example │ │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Podfile │ ├── Podfile.lock │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h ├── lib │ └── main.dart ├── pubspec.lock ├── pubspec.yaml ├── test │ └── widget_test.dart └── web │ └── index.html ├── flutter_call_kit.iml ├── ios ├── .gitignore ├── Assets │ └── .gitkeep ├── Classes │ ├── FlutterCallKitPlugin.h │ └── FlutterCallKitPlugin.m └── flutter_call_kit.podspec ├── lib └── flutter_call_kit.dart ├── pubspec.lock ├── pubspec.yaml └── test └── flutter_call_kit_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | 4 | .packages 5 | .pub/ 6 | .idea 7 | .vscode 8 | build/ 9 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 056f7244ef02947c37b2369fe542ea87af6e1173 8 | channel: master 9 | 10 | project_type: plugin 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.0.3 2 | 3 | * fix event handlers 4 | 5 | ## 0.0.2 6 | 7 | * refactor example 8 | 9 | ## 0.0.1 10 | 11 | * initial release. 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Onyemaechi Okafor 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flutter Call Kit 2 | 3 | [![pub package](https://img.shields.io/pub/v/flutter_call_kit.svg)](https://pub.dartlang.org/packages/flutter_call_kit) 4 | Flutter Call Kit Plugin - Currently iOS >= 10.0 only 5 | 6 | ## Motivation 7 | 8 | **Flutter Call Kit** utilises a brand new iOS 10 framework **CallKit** to make the life easier for VoIP developers using Flutter. 9 | 10 | **Note 1**: This plugin works for only iOS. No android support yet 11 | 12 | **Note 2** This plugin was inspired by [react-native-keep](https://github.com/react-native-webrtc/react-native-callkeep) 13 | 14 | For more information about **CallKit** on iOS, please see [Official CallKit Framework Document](https://developer.apple.com/reference/callkit?language=objc) or [Introduction to CallKit by Xamarin](https://developer.xamarin.com/guides/ios/platform_features/introduction-to-ios10/callkit/) 15 | 16 | 17 | ## iOS 18 | ![Connection Service](https://github.com/react-native-webrtc/react-native-callkeep/blob/master/docs/pictures/call-kit.png) 19 | 20 | ## Usage 21 | Add `flutter_call_kit` as a [dependency in your pubspec.yaml file](https://flutter.io/using-packages/). 22 | 23 | 24 | ## Example 25 | 26 | ```dart 27 | 28 | import 'package:flutter/material.dart'; 29 | import 'dart:async'; 30 | import 'package:uuid/uuid.dart'; 31 | import 'package:flutter_call_kit/flutter_call_kit.dart'; 32 | 33 | void main() => runApp(MyApp()); 34 | 35 | class MyApp extends StatefulWidget { 36 | @override 37 | _MyAppState createState() => _MyAppState(); 38 | } 39 | 40 | class _MyAppState extends State { 41 | bool _configured; 42 | String _currentCallId; 43 | FlutterCallKit _callKit = FlutterCallKit(); 44 | @override 45 | void initState() { 46 | super.initState(); 47 | configure(); 48 | } 49 | 50 | Future configure() async { 51 | _callKit.configure( 52 | IOSOptions("My Awesome APP", 53 | imageName: 'sim_icon', 54 | supportsVideo: false, 55 | maximumCallGroups: 1, 56 | maximumCallsPerCallGroup: 1), 57 | didReceiveStartCallAction: _didReceiveStartCallAction, 58 | performAnswerCallAction: _performAnswerCallAction, 59 | performEndCallAction: _performEndCallAction, 60 | didActivateAudioSession: _didActivateAudioSession, 61 | didDisplayIncomingCall: _didDisplayIncomingCall, 62 | didPerformSetMutedCallAction: _didPerformSetMutedCallAction, 63 | didPerformDTMFAction: _didPerformDTMFAction, 64 | didToggleHoldAction: _didToggleHoldAction, 65 | ); 66 | setState(() { 67 | _configured = true; 68 | }); 69 | } 70 | 71 | /// Use startCall to ask the system to start a call - Initiate an outgoing call from this point 72 | Future startCall(String handle, String localizedCallerName) async { 73 | /// Your normal start call action 74 | await _callKit.startCall(currentCallId, handle, localizedCallerName); 75 | } 76 | 77 | Future reportEndCallWithUUID(String uuid, EndReason reason) async { 78 | await _callKit.reportEndCallWithUUID(uuid, reason); 79 | } 80 | 81 | /// Event Listener Callbacks 82 | 83 | Future _didReceiveStartCallAction(String uuid, String handle) async { 84 | // Get this event after the system decides you can start a call 85 | // You can now start a call from within your app 86 | } 87 | 88 | Future _performAnswerCallAction(String uuid) async { 89 | // Called when the user answers an incoming call 90 | } 91 | 92 | Future _performEndCallAction(String uuid) async { 93 | await _callKit.endCall(this.currentCallId); 94 | _currentCallId = null; 95 | } 96 | 97 | Future _didActivateAudioSession() async { 98 | // you might want to do following things when receiving this event: 99 | // - Start playing ringback if it is an outgoing call 100 | } 101 | 102 | Future _didDisplayIncomingCall(String error, String uuid, String handle, 103 | String localizedCallerName, bool fromPushKit) async { 104 | // You will get this event after RNCallKeep finishes showing incoming call UI 105 | // You can check if there was an error while displaying 106 | } 107 | 108 | Future _didPerformSetMutedCallAction(bool mute, String uuid) async { 109 | // Called when the system or user mutes a call 110 | } 111 | 112 | Future _didPerformDTMFAction(String digit, String uuid) async { 113 | // Called when the system or user performs a DTMF action 114 | } 115 | 116 | Future _didToggleHoldAction(bool hold, String uuid) async { 117 | // Called when the system or user holds a call 118 | } 119 | 120 | String get currentCallId { 121 | if (_currentCallId == null) { 122 | final uuid = new Uuid(); 123 | _currentCallId = uuid.v4(); 124 | } 125 | 126 | return _currentCallId; 127 | } 128 | 129 | @override 130 | Widget build(BuildContext context) { 131 | return MaterialApp( 132 | home: Scaffold( 133 | appBar: AppBar( 134 | title: const Text('Plugin example app'), 135 | ), 136 | body: Center( 137 | child: Text('Flutter Call Kit Configured: $_configured\n'), 138 | ), 139 | ), 140 | ); 141 | } 142 | } 143 | 144 | ``` 145 | 146 | ## Receiving a call when the application is not reachable. 147 | 148 | In some case your application can be unreachable : 149 | - when the user kill the application 150 | - when it's in background since a long time (eg: after ~5mn the os will kill all connections). 151 | 152 | To be able to wake up your application to display the incoming call, you can use [https://github.com/peerwaya/flutter_voip_push_notification](flutter_voip_push_notification) on iOS. 153 | 154 | You have to send a push to your application with a library supporting PushKit pushes for iOS. 155 | 156 | ### PushKit 157 | 158 | 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 : 159 | 160 | ```objective-c 161 | - (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type withCompletionHandler:(void (^)(void))completion { 162 | // Process the received push 163 | [FlutterVoipPushNotificationPlugin didReceiveIncomingPushWithPayload:payload forType:(NSString *)type]; 164 | 165 | // Retrieve information like handle and callerName here 166 | // NSString *uuid = /* fetch for payload or ... */ [[[NSUUID UUID] UUIDString] lowercaseString]; 167 | // NSString *callerName = @"caller name here"; 168 | // NSString *handle = @"caller number here"; 169 | 170 | [FlutterCallKitPlugin reportNewIncomingCall:uuid handle:handle handleType:@"generic" hasVideo:false localizedCallerName:callerName fromPushKit: YES]; 171 | 172 | completion(); 173 | } 174 | ``` 175 | 176 | ## Contributing 177 | 178 | Any pull request, issue report and suggestion are highly welcome! -------------------------------------------------------------------------------- /android/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /android/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | flutter_call_kit 4 | Project flutter_call_kit created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.buildship.core.gradleprojectbuilder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.eclipse.buildship.core.gradleprojectnature 22 | 23 | 24 | -------------------------------------------------------------------------------- /android/.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | connection.project.dir=../../../mobile/app/android 2 | eclipse.preferences.version=1 3 | -------------------------------------------------------------------------------- /android/.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled 3 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | group 'com.example.flutter_call_kit' 2 | version '1.0-SNAPSHOT' 3 | 4 | buildscript { 5 | ext.kotlin_version = '1.3.50' 6 | repositories { 7 | google() 8 | jcenter() 9 | } 10 | 11 | dependencies { 12 | classpath 'com.android.tools.build:gradle:3.5.0' 13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 14 | } 15 | } 16 | 17 | rootProject.allprojects { 18 | repositories { 19 | google() 20 | jcenter() 21 | } 22 | } 23 | 24 | apply plugin: 'com.android.library' 25 | apply plugin: 'kotlin-android' 26 | 27 | android { 28 | compileSdkVersion 29 29 | 30 | sourceSets { 31 | main.java.srcDirs += 'src/main/kotlin' 32 | } 33 | defaultConfig { 34 | minSdkVersion 16 35 | } 36 | lintOptions { 37 | disable 'InvalidPackage' 38 | } 39 | } 40 | 41 | dependencies { 42 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 43 | } 44 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_call_kit/59300690e6dad120d4c0c804344158e52804881d/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip 6 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'flutter_call_kit' 2 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /android/src/main/kotlin/com/example/flutter_call_kit/FlutterCallKitPlugin.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_call_kit 2 | 3 | import androidx.annotation.NonNull 4 | 5 | import io.flutter.embedding.engine.plugins.FlutterPlugin 6 | import io.flutter.plugin.common.MethodCall 7 | import io.flutter.plugin.common.MethodChannel 8 | import io.flutter.plugin.common.MethodChannel.MethodCallHandler 9 | import io.flutter.plugin.common.MethodChannel.Result 10 | import io.flutter.plugin.common.PluginRegistry.Registrar 11 | 12 | 13 | class FlutterCallKitPlugin: FlutterPlugin, MethodCallHandler { 14 | /// The MethodChannel that will the communication between Flutter and native Android 15 | /// 16 | /// This local reference serves to register the plugin with the Flutter Engine and unregister it 17 | /// when the Flutter Engine is detached from the Activity 18 | private lateinit var channel : MethodChannel 19 | 20 | override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { 21 | channel = MethodChannel(flutterPluginBinding.binaryMessenger, "flutter_call_kit") 22 | channel.setMethodCallHandler(this) 23 | } 24 | 25 | override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { 26 | if (call.method == "getPlatformVersion") { 27 | result.success("Android ${android.os.Build.VERSION.RELEASE}") 28 | } else { 29 | result.notImplemented() 30 | } 31 | } 32 | 33 | override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { 34 | channel.setMethodCallHandler(null) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | /build/ 32 | 33 | # Web related 34 | lib/generated_plugin_registrant.dart 35 | 36 | # Exceptions to above rules. 37 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 38 | -------------------------------------------------------------------------------- /example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 056f7244ef02947c37b2369fe542ea87af6e1173 8 | channel: master 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # flutter_call_kit_example 2 | 3 | Demonstrates how to use the flutter_call_kit plugin. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 28 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | lintOptions { 36 | disable 'InvalidPackage' 37 | } 38 | 39 | defaultConfig { 40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 41 | applicationId "com.example.flutter_call_kit_example" 42 | minSdkVersion 16 43 | targetSdkVersion 28 44 | versionCode flutterVersionCode.toInteger() 45 | versionName flutterVersionName 46 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 47 | } 48 | 49 | buildTypes { 50 | release { 51 | // TODO: Add your own signing config for the release build. 52 | // Signing with the debug keys for now, so `flutter run --release` works. 53 | signingConfig signingConfigs.debug 54 | } 55 | } 56 | } 57 | 58 | flutter { 59 | source '../..' 60 | } 61 | 62 | dependencies { 63 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 64 | testImplementation 'junit:junit:4.12' 65 | androidTestImplementation 'androidx.test:runner:1.1.1' 66 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' 67 | } 68 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 9 | 13 | 20 | 24 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/com/example/flutter_call_kit_example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_call_kit_example 2 | 3 | import android.os.Bundle 4 | 5 | import io.flutter.embedding.android.FlutterActivity 6 | import io.flutter.embedding.engine.FlutterEngine; 7 | import io.flutter.plugins.GeneratedPluginRegistrant 8 | 9 | class MainActivity: FlutterActivity() { 10 | override fun onCreate(savedInstanceState: Bundle?) { 11 | super.onCreate(savedInstanceState) 12 | GeneratedPluginRegistrant.registerWith(this) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_call_kit/59300690e6dad120d4c0c804344158e52804881d/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_call_kit/59300690e6dad120d4c0c804344158e52804881d/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_call_kit/59300690e6dad120d4c0c804344158e52804881d/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_call_kit/59300690e6dad120d4c0c804344158e52804881d/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_call_kit/59300690e6dad120d4c0c804344158e52804881d/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.5.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=true 3 | android.useAndroidX=true 4 | android.enableJetifier=true 5 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip 7 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Generated.xcconfig 20 | Flutter/app.flx 21 | Flutter/app.zip 22 | Flutter/flutter_assets/ 23 | Flutter/flutter_export_environment.sh 24 | ServiceDefinitions.json 25 | Runner/GeneratedPluginRegistrant.* 26 | 27 | # Exceptions to above rules. 28 | !default.mode1v3 29 | !default.mode2v3 30 | !default.pbxuser 31 | !default.perspectivev3 32 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def parse_KV_file(file, separator='=') 14 | file_abs_path = File.expand_path(file) 15 | if !File.exists? file_abs_path 16 | return []; 17 | end 18 | pods_ary = [] 19 | skip_line_start_symbols = ["#", "/"] 20 | File.foreach(file_abs_path) { |line| 21 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } 22 | plugin = line.split(pattern=separator) 23 | if plugin.length == 2 24 | podname = plugin[0].strip() 25 | path = plugin[1].strip() 26 | podpath = File.expand_path("#{path}", file_abs_path) 27 | pods_ary.push({:name => podname, :path => podpath}); 28 | else 29 | puts "Invalid plugin specification: #{line}" 30 | end 31 | } 32 | return pods_ary 33 | end 34 | 35 | target 'Runner' do 36 | use_frameworks! 37 | 38 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 39 | # referring to absolute paths on developers' machines. 40 | system('rm -rf .symlinks') 41 | system('mkdir -p .symlinks/plugins') 42 | 43 | # Flutter Pods 44 | generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig') 45 | if generated_xcode_build_settings.empty? 46 | puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first." 47 | end 48 | generated_xcode_build_settings.map { |p| 49 | if p[:name] == 'FLUTTER_FRAMEWORK_DIR' 50 | symlink = File.join('.symlinks', 'flutter') 51 | File.symlink(File.dirname(p[:path]), symlink) 52 | pod 'Flutter', :path => File.join(symlink, File.basename(p[:path])) 53 | end 54 | } 55 | 56 | # Plugin Pods 57 | plugin_pods = parse_KV_file('../.flutter-plugins') 58 | plugin_pods.map { |p| 59 | symlink = File.join('.symlinks', 'plugins', p[:name]) 60 | File.symlink(p[:path], symlink) 61 | pod p[:name], :path => File.join(symlink, 'ios') 62 | } 63 | end 64 | 65 | # Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system. 66 | install! 'cocoapods', :disable_input_output_paths => true 67 | 68 | post_install do |installer| 69 | installer.pods_project.targets.each do |target| 70 | target.build_configurations.each do |config| 71 | config.build_settings['ENABLE_BITCODE'] = 'NO' 72 | end 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /example/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - flutter_call_kit (0.0.1): 4 | - Flutter 5 | 6 | DEPENDENCIES: 7 | - Flutter (from `.symlinks/flutter/ios`) 8 | - flutter_call_kit (from `.symlinks/plugins/flutter_call_kit/ios`) 9 | 10 | EXTERNAL SOURCES: 11 | Flutter: 12 | :path: ".symlinks/flutter/ios" 13 | flutter_call_kit: 14 | :path: ".symlinks/plugins/flutter_call_kit/ios" 15 | 16 | SPEC CHECKSUMS: 17 | Flutter: 0e3d915762c693b495b44d77113d4970485de6ec 18 | flutter_call_kit: 0598f2eec54579aa00b53fb68a622ccdb08fe4fe 19 | 20 | PODFILE CHECKSUM: b6a0a141693093b304368d08511b46cf3d1d0ac5 21 | 22 | COCOAPODS: 1.7.2 23 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; 13 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 14 | 4C393CE4A4B96EB795D40488 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A316BE9E5452E015A90D71FB /* Pods_Runner.framework */; }; 15 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 16 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; 17 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 18 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 19 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 20 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXCopyFilesBuildPhase section */ 24 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 25 | isa = PBXCopyFilesBuildPhase; 26 | buildActionMask = 2147483647; 27 | dstPath = ""; 28 | dstSubfolderSpec = 10; 29 | files = ( 30 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, 31 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, 32 | ); 33 | name = "Embed Frameworks"; 34 | runOnlyForDeploymentPostprocessing = 0; 35 | }; 36 | /* End PBXCopyFilesBuildPhase section */ 37 | 38 | /* Begin PBXFileReference section */ 39 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 40 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 41 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 42 | 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 43 | 3BF9A14C7C6920A57573385B /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 44 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 45 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 46 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 47 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 48 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 49 | 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 50 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 52 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 53 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 54 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 55 | A26D360F6C87F966DBE86BB8 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 56 | A316BE9E5452E015A90D71FB /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 57 | F2F456B57880FC5834344C5B /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 58 | /* End PBXFileReference section */ 59 | 60 | /* Begin PBXFrameworksBuildPhase section */ 61 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 62 | isa = PBXFrameworksBuildPhase; 63 | buildActionMask = 2147483647; 64 | files = ( 65 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, 66 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, 67 | 4C393CE4A4B96EB795D40488 /* Pods_Runner.framework in Frameworks */, 68 | ); 69 | runOnlyForDeploymentPostprocessing = 0; 70 | }; 71 | /* End PBXFrameworksBuildPhase section */ 72 | 73 | /* Begin PBXGroup section */ 74 | 080B004F3781FC45CD8AF895 /* Pods */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | A26D360F6C87F966DBE86BB8 /* Pods-Runner.debug.xcconfig */, 78 | F2F456B57880FC5834344C5B /* Pods-Runner.release.xcconfig */, 79 | 3BF9A14C7C6920A57573385B /* Pods-Runner.profile.xcconfig */, 80 | ); 81 | path = Pods; 82 | sourceTree = ""; 83 | }; 84 | 9740EEB11CF90186004384FC /* Flutter */ = { 85 | isa = PBXGroup; 86 | children = ( 87 | 3B80C3931E831B6300D905FE /* App.framework */, 88 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 89 | 9740EEBA1CF902C7004384FC /* Flutter.framework */, 90 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 91 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 92 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 93 | ); 94 | name = Flutter; 95 | sourceTree = ""; 96 | }; 97 | 97C146E51CF9000F007C117D = { 98 | isa = PBXGroup; 99 | children = ( 100 | 9740EEB11CF90186004384FC /* Flutter */, 101 | 97C146F01CF9000F007C117D /* Runner */, 102 | 97C146EF1CF9000F007C117D /* Products */, 103 | 080B004F3781FC45CD8AF895 /* Pods */, 104 | EC91321A4F4025EFF256EFC3 /* Frameworks */, 105 | ); 106 | sourceTree = ""; 107 | }; 108 | 97C146EF1CF9000F007C117D /* Products */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | 97C146EE1CF9000F007C117D /* Runner.app */, 112 | ); 113 | name = Products; 114 | sourceTree = ""; 115 | }; 116 | 97C146F01CF9000F007C117D /* Runner */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 120 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 121 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 122 | 97C147021CF9000F007C117D /* Info.plist */, 123 | 97C146F11CF9000F007C117D /* Supporting Files */, 124 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 125 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 126 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 127 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 128 | ); 129 | path = Runner; 130 | sourceTree = ""; 131 | }; 132 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 133 | isa = PBXGroup; 134 | children = ( 135 | ); 136 | name = "Supporting Files"; 137 | sourceTree = ""; 138 | }; 139 | EC91321A4F4025EFF256EFC3 /* Frameworks */ = { 140 | isa = PBXGroup; 141 | children = ( 142 | A316BE9E5452E015A90D71FB /* Pods_Runner.framework */, 143 | ); 144 | name = Frameworks; 145 | sourceTree = ""; 146 | }; 147 | /* End PBXGroup section */ 148 | 149 | /* Begin PBXNativeTarget section */ 150 | 97C146ED1CF9000F007C117D /* Runner */ = { 151 | isa = PBXNativeTarget; 152 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 153 | buildPhases = ( 154 | B6B3FB64CDF2560ADFDECA12 /* [CP] Check Pods Manifest.lock */, 155 | 9740EEB61CF901F6004384FC /* Run Script */, 156 | 97C146EA1CF9000F007C117D /* Sources */, 157 | 97C146EB1CF9000F007C117D /* Frameworks */, 158 | 97C146EC1CF9000F007C117D /* Resources */, 159 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 160 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 161 | 6C15DA63DCC56CB1E841167E /* [CP] Embed Pods Frameworks */, 162 | ); 163 | buildRules = ( 164 | ); 165 | dependencies = ( 166 | ); 167 | name = Runner; 168 | productName = Runner; 169 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 170 | productType = "com.apple.product-type.application"; 171 | }; 172 | /* End PBXNativeTarget section */ 173 | 174 | /* Begin PBXProject section */ 175 | 97C146E61CF9000F007C117D /* Project object */ = { 176 | isa = PBXProject; 177 | attributes = { 178 | LastUpgradeCheck = 1020; 179 | ORGANIZATIONNAME = "The Chromium Authors"; 180 | TargetAttributes = { 181 | 97C146ED1CF9000F007C117D = { 182 | CreatedOnToolsVersion = 7.3.1; 183 | DevelopmentTeam = SG9HGT8DNM; 184 | LastSwiftMigration = 0910; 185 | }; 186 | }; 187 | }; 188 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 189 | compatibilityVersion = "Xcode 3.2"; 190 | developmentRegion = en; 191 | hasScannedForEncodings = 0; 192 | knownRegions = ( 193 | en, 194 | Base, 195 | ); 196 | mainGroup = 97C146E51CF9000F007C117D; 197 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 198 | projectDirPath = ""; 199 | projectRoot = ""; 200 | targets = ( 201 | 97C146ED1CF9000F007C117D /* Runner */, 202 | ); 203 | }; 204 | /* End PBXProject section */ 205 | 206 | /* Begin PBXResourcesBuildPhase section */ 207 | 97C146EC1CF9000F007C117D /* Resources */ = { 208 | isa = PBXResourcesBuildPhase; 209 | buildActionMask = 2147483647; 210 | files = ( 211 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 212 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 213 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 214 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 215 | ); 216 | runOnlyForDeploymentPostprocessing = 0; 217 | }; 218 | /* End PBXResourcesBuildPhase section */ 219 | 220 | /* Begin PBXShellScriptBuildPhase section */ 221 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 222 | isa = PBXShellScriptBuildPhase; 223 | buildActionMask = 2147483647; 224 | files = ( 225 | ); 226 | inputPaths = ( 227 | ); 228 | name = "Thin Binary"; 229 | outputPaths = ( 230 | ); 231 | runOnlyForDeploymentPostprocessing = 0; 232 | shellPath = /bin/sh; 233 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; 234 | }; 235 | 6C15DA63DCC56CB1E841167E /* [CP] Embed Pods Frameworks */ = { 236 | isa = PBXShellScriptBuildPhase; 237 | buildActionMask = 2147483647; 238 | files = ( 239 | ); 240 | inputPaths = ( 241 | ); 242 | name = "[CP] Embed Pods Frameworks"; 243 | outputPaths = ( 244 | ); 245 | runOnlyForDeploymentPostprocessing = 0; 246 | shellPath = /bin/sh; 247 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 248 | showEnvVarsInLog = 0; 249 | }; 250 | 9740EEB61CF901F6004384FC /* Run Script */ = { 251 | isa = PBXShellScriptBuildPhase; 252 | buildActionMask = 2147483647; 253 | files = ( 254 | ); 255 | inputPaths = ( 256 | ); 257 | name = "Run Script"; 258 | outputPaths = ( 259 | ); 260 | runOnlyForDeploymentPostprocessing = 0; 261 | shellPath = /bin/sh; 262 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 263 | }; 264 | B6B3FB64CDF2560ADFDECA12 /* [CP] Check Pods Manifest.lock */ = { 265 | isa = PBXShellScriptBuildPhase; 266 | buildActionMask = 2147483647; 267 | files = ( 268 | ); 269 | inputFileListPaths = ( 270 | ); 271 | inputPaths = ( 272 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 273 | "${PODS_ROOT}/Manifest.lock", 274 | ); 275 | name = "[CP] Check Pods Manifest.lock"; 276 | outputFileListPaths = ( 277 | ); 278 | outputPaths = ( 279 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 280 | ); 281 | runOnlyForDeploymentPostprocessing = 0; 282 | shellPath = /bin/sh; 283 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 284 | showEnvVarsInLog = 0; 285 | }; 286 | /* End PBXShellScriptBuildPhase section */ 287 | 288 | /* Begin PBXSourcesBuildPhase section */ 289 | 97C146EA1CF9000F007C117D /* Sources */ = { 290 | isa = PBXSourcesBuildPhase; 291 | buildActionMask = 2147483647; 292 | files = ( 293 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 294 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 295 | ); 296 | runOnlyForDeploymentPostprocessing = 0; 297 | }; 298 | /* End PBXSourcesBuildPhase section */ 299 | 300 | /* Begin PBXVariantGroup section */ 301 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 302 | isa = PBXVariantGroup; 303 | children = ( 304 | 97C146FB1CF9000F007C117D /* Base */, 305 | ); 306 | name = Main.storyboard; 307 | sourceTree = ""; 308 | }; 309 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 310 | isa = PBXVariantGroup; 311 | children = ( 312 | 97C147001CF9000F007C117D /* Base */, 313 | ); 314 | name = LaunchScreen.storyboard; 315 | sourceTree = ""; 316 | }; 317 | /* End PBXVariantGroup section */ 318 | 319 | /* Begin XCBuildConfiguration section */ 320 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 321 | isa = XCBuildConfiguration; 322 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 323 | buildSettings = { 324 | ALWAYS_SEARCH_USER_PATHS = NO; 325 | CLANG_ANALYZER_NONNULL = YES; 326 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 327 | CLANG_CXX_LIBRARY = "libc++"; 328 | CLANG_ENABLE_MODULES = YES; 329 | CLANG_ENABLE_OBJC_ARC = YES; 330 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 331 | CLANG_WARN_BOOL_CONVERSION = YES; 332 | CLANG_WARN_COMMA = YES; 333 | CLANG_WARN_CONSTANT_CONVERSION = YES; 334 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 335 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 336 | CLANG_WARN_EMPTY_BODY = YES; 337 | CLANG_WARN_ENUM_CONVERSION = YES; 338 | CLANG_WARN_INFINITE_RECURSION = YES; 339 | CLANG_WARN_INT_CONVERSION = YES; 340 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 341 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 342 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 343 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 344 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 345 | CLANG_WARN_STRICT_PROTOTYPES = YES; 346 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 347 | CLANG_WARN_UNREACHABLE_CODE = YES; 348 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 349 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 350 | COPY_PHASE_STRIP = NO; 351 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 352 | ENABLE_NS_ASSERTIONS = NO; 353 | ENABLE_STRICT_OBJC_MSGSEND = YES; 354 | GCC_C_LANGUAGE_STANDARD = gnu99; 355 | GCC_NO_COMMON_BLOCKS = YES; 356 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 357 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 358 | GCC_WARN_UNDECLARED_SELECTOR = YES; 359 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 360 | GCC_WARN_UNUSED_FUNCTION = YES; 361 | GCC_WARN_UNUSED_VARIABLE = YES; 362 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 363 | MTL_ENABLE_DEBUG_INFO = NO; 364 | SDKROOT = iphoneos; 365 | SUPPORTED_PLATFORMS = iphoneos; 366 | TARGETED_DEVICE_FAMILY = "1,2"; 367 | VALIDATE_PRODUCT = YES; 368 | }; 369 | name = Profile; 370 | }; 371 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 372 | isa = XCBuildConfiguration; 373 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 374 | buildSettings = { 375 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 376 | CLANG_ENABLE_MODULES = YES; 377 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 378 | DEVELOPMENT_TEAM = SG9HGT8DNM; 379 | ENABLE_BITCODE = NO; 380 | FRAMEWORK_SEARCH_PATHS = ( 381 | "$(inherited)", 382 | "$(PROJECT_DIR)/Flutter", 383 | ); 384 | INFOPLIST_FILE = Runner/Info.plist; 385 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 386 | LIBRARY_SEARCH_PATHS = ( 387 | "$(inherited)", 388 | "$(PROJECT_DIR)/Flutter", 389 | ); 390 | PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterCallKitExample; 391 | PRODUCT_NAME = "$(TARGET_NAME)"; 392 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 393 | SWIFT_VERSION = 4.0; 394 | VERSIONING_SYSTEM = "apple-generic"; 395 | }; 396 | name = Profile; 397 | }; 398 | 97C147031CF9000F007C117D /* Debug */ = { 399 | isa = XCBuildConfiguration; 400 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 401 | buildSettings = { 402 | ALWAYS_SEARCH_USER_PATHS = NO; 403 | CLANG_ANALYZER_NONNULL = YES; 404 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 405 | CLANG_CXX_LIBRARY = "libc++"; 406 | CLANG_ENABLE_MODULES = YES; 407 | CLANG_ENABLE_OBJC_ARC = YES; 408 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 409 | CLANG_WARN_BOOL_CONVERSION = YES; 410 | CLANG_WARN_COMMA = YES; 411 | CLANG_WARN_CONSTANT_CONVERSION = YES; 412 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 413 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 414 | CLANG_WARN_EMPTY_BODY = YES; 415 | CLANG_WARN_ENUM_CONVERSION = YES; 416 | CLANG_WARN_INFINITE_RECURSION = YES; 417 | CLANG_WARN_INT_CONVERSION = YES; 418 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 419 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 420 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 421 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 422 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 423 | CLANG_WARN_STRICT_PROTOTYPES = YES; 424 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 425 | CLANG_WARN_UNREACHABLE_CODE = YES; 426 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 427 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 428 | COPY_PHASE_STRIP = NO; 429 | DEBUG_INFORMATION_FORMAT = dwarf; 430 | ENABLE_STRICT_OBJC_MSGSEND = YES; 431 | ENABLE_TESTABILITY = YES; 432 | GCC_C_LANGUAGE_STANDARD = gnu99; 433 | GCC_DYNAMIC_NO_PIC = NO; 434 | GCC_NO_COMMON_BLOCKS = YES; 435 | GCC_OPTIMIZATION_LEVEL = 0; 436 | GCC_PREPROCESSOR_DEFINITIONS = ( 437 | "DEBUG=1", 438 | "$(inherited)", 439 | ); 440 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 441 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 442 | GCC_WARN_UNDECLARED_SELECTOR = YES; 443 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 444 | GCC_WARN_UNUSED_FUNCTION = YES; 445 | GCC_WARN_UNUSED_VARIABLE = YES; 446 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 447 | MTL_ENABLE_DEBUG_INFO = YES; 448 | ONLY_ACTIVE_ARCH = YES; 449 | SDKROOT = iphoneos; 450 | TARGETED_DEVICE_FAMILY = "1,2"; 451 | }; 452 | name = Debug; 453 | }; 454 | 97C147041CF9000F007C117D /* Release */ = { 455 | isa = XCBuildConfiguration; 456 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 457 | buildSettings = { 458 | ALWAYS_SEARCH_USER_PATHS = NO; 459 | CLANG_ANALYZER_NONNULL = YES; 460 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 461 | CLANG_CXX_LIBRARY = "libc++"; 462 | CLANG_ENABLE_MODULES = YES; 463 | CLANG_ENABLE_OBJC_ARC = YES; 464 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 465 | CLANG_WARN_BOOL_CONVERSION = YES; 466 | CLANG_WARN_COMMA = YES; 467 | CLANG_WARN_CONSTANT_CONVERSION = YES; 468 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 469 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 470 | CLANG_WARN_EMPTY_BODY = YES; 471 | CLANG_WARN_ENUM_CONVERSION = YES; 472 | CLANG_WARN_INFINITE_RECURSION = YES; 473 | CLANG_WARN_INT_CONVERSION = YES; 474 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 475 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 476 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 477 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 478 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 479 | CLANG_WARN_STRICT_PROTOTYPES = YES; 480 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 481 | CLANG_WARN_UNREACHABLE_CODE = YES; 482 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 483 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 484 | COPY_PHASE_STRIP = NO; 485 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 486 | ENABLE_NS_ASSERTIONS = NO; 487 | ENABLE_STRICT_OBJC_MSGSEND = YES; 488 | GCC_C_LANGUAGE_STANDARD = gnu99; 489 | GCC_NO_COMMON_BLOCKS = YES; 490 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 491 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 492 | GCC_WARN_UNDECLARED_SELECTOR = YES; 493 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 494 | GCC_WARN_UNUSED_FUNCTION = YES; 495 | GCC_WARN_UNUSED_VARIABLE = YES; 496 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 497 | MTL_ENABLE_DEBUG_INFO = NO; 498 | SDKROOT = iphoneos; 499 | SUPPORTED_PLATFORMS = iphoneos; 500 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 501 | TARGETED_DEVICE_FAMILY = "1,2"; 502 | VALIDATE_PRODUCT = YES; 503 | }; 504 | name = Release; 505 | }; 506 | 97C147061CF9000F007C117D /* Debug */ = { 507 | isa = XCBuildConfiguration; 508 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 509 | buildSettings = { 510 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 511 | CLANG_ENABLE_MODULES = YES; 512 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 513 | DEVELOPMENT_TEAM = SG9HGT8DNM; 514 | ENABLE_BITCODE = NO; 515 | FRAMEWORK_SEARCH_PATHS = ( 516 | "$(inherited)", 517 | "$(PROJECT_DIR)/Flutter", 518 | ); 519 | INFOPLIST_FILE = Runner/Info.plist; 520 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 521 | LIBRARY_SEARCH_PATHS = ( 522 | "$(inherited)", 523 | "$(PROJECT_DIR)/Flutter", 524 | ); 525 | PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterCallKitExample; 526 | PRODUCT_NAME = "$(TARGET_NAME)"; 527 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 528 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 529 | SWIFT_VERSION = 4.0; 530 | VERSIONING_SYSTEM = "apple-generic"; 531 | }; 532 | name = Debug; 533 | }; 534 | 97C147071CF9000F007C117D /* Release */ = { 535 | isa = XCBuildConfiguration; 536 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 537 | buildSettings = { 538 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 539 | CLANG_ENABLE_MODULES = YES; 540 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 541 | DEVELOPMENT_TEAM = SG9HGT8DNM; 542 | ENABLE_BITCODE = NO; 543 | FRAMEWORK_SEARCH_PATHS = ( 544 | "$(inherited)", 545 | "$(PROJECT_DIR)/Flutter", 546 | ); 547 | INFOPLIST_FILE = Runner/Info.plist; 548 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 549 | LIBRARY_SEARCH_PATHS = ( 550 | "$(inherited)", 551 | "$(PROJECT_DIR)/Flutter", 552 | ); 553 | PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterCallKitExample; 554 | PRODUCT_NAME = "$(TARGET_NAME)"; 555 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 556 | SWIFT_VERSION = 4.0; 557 | VERSIONING_SYSTEM = "apple-generic"; 558 | }; 559 | name = Release; 560 | }; 561 | /* End XCBuildConfiguration section */ 562 | 563 | /* Begin XCConfigurationList section */ 564 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 565 | isa = XCConfigurationList; 566 | buildConfigurations = ( 567 | 97C147031CF9000F007C117D /* Debug */, 568 | 97C147041CF9000F007C117D /* Release */, 569 | 249021D3217E4FDB00AE95B9 /* Profile */, 570 | ); 571 | defaultConfigurationIsVisible = 0; 572 | defaultConfigurationName = Release; 573 | }; 574 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 575 | isa = XCConfigurationList; 576 | buildConfigurations = ( 577 | 97C147061CF9000F007C117D /* Debug */, 578 | 97C147071CF9000F007C117D /* Release */, 579 | 249021D4217E4FDB00AE95B9 /* Profile */, 580 | ); 581 | defaultConfigurationIsVisible = 0; 582 | defaultConfigurationName = Release; 583 | }; 584 | /* End XCConfigurationList section */ 585 | }; 586 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 587 | } 588 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_call_kit/59300690e6dad120d4c0c804344158e52804881d/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_call_kit/59300690e6dad120d4c0c804344158e52804881d/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_call_kit/59300690e6dad120d4c0c804344158e52804881d/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_call_kit/59300690e6dad120d4c0c804344158e52804881d/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_call_kit/59300690e6dad120d4c0c804344158e52804881d/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_call_kit/59300690e6dad120d4c0c804344158e52804881d/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_call_kit/59300690e6dad120d4c0c804344158e52804881d/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_call_kit/59300690e6dad120d4c0c804344158e52804881d/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_call_kit/59300690e6dad120d4c0c804344158e52804881d/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_call_kit/59300690e6dad120d4c0c804344158e52804881d/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_call_kit/59300690e6dad120d4c0c804344158e52804881d/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_call_kit/59300690e6dad120d4c0c804344158e52804881d/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_call_kit/59300690e6dad120d4c0c804344158e52804881d/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_call_kit/59300690e6dad120d4c0c804344158e52804881d/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_call_kit/59300690e6dad120d4c0c804344158e52804881d/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_call_kit/59300690e6dad120d4c0c804344158e52804881d/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_call_kit/59300690e6dad120d4c0c804344158e52804881d/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_call_kit/59300690e6dad120d4c0c804344158e52804881d/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | flutter_call_kit_example 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'dart:async'; 3 | import 'package:uuid/uuid.dart'; 4 | import 'package:flutter_call_kit/flutter_call_kit.dart'; 5 | 6 | void main() => runApp(MyApp()); 7 | 8 | class MyApp extends StatefulWidget { 9 | @override 10 | _MyAppState createState() => _MyAppState(); 11 | } 12 | 13 | class _MyAppState extends State { 14 | bool _configured; 15 | String _currentCallId; 16 | FlutterCallKit _callKit = FlutterCallKit(); 17 | @override 18 | void initState() { 19 | super.initState(); 20 | configure(); 21 | } 22 | 23 | Future configure() async { 24 | _callKit.configure( 25 | IOSOptions("My Awesome APP", 26 | imageName: 'sim_icon', 27 | supportsVideo: false, 28 | maximumCallGroups: 1, 29 | maximumCallsPerCallGroup: 1), 30 | didReceiveStartCallAction: _didReceiveStartCallAction, 31 | performAnswerCallAction: _performAnswerCallAction, 32 | performEndCallAction: _performEndCallAction, 33 | didActivateAudioSession: _didActivateAudioSession, 34 | didDisplayIncomingCall: _didDisplayIncomingCall, 35 | didPerformSetMutedCallAction: _didPerformSetMutedCallAction, 36 | didPerformDTMFAction: _didPerformDTMFAction, 37 | didToggleHoldAction: _didToggleHoldAction, 38 | ); 39 | setState(() { 40 | _configured = true; 41 | }); 42 | } 43 | 44 | /// Use startCall to ask the system to start a call - Initiate an outgoing call from this point 45 | Future startCall(String handle, String localizedCallerName) async { 46 | /// Your normal start call action 47 | await _callKit.startCall(currentCallId, handle, localizedCallerName); 48 | } 49 | 50 | Future reportEndCallWithUUID(String uuid, EndReason reason) async { 51 | await _callKit.reportEndCallWithUUID(uuid, reason); 52 | } 53 | 54 | /// Event Listener Callbacks 55 | 56 | Future _didReceiveStartCallAction(String uuid, String handle) async { 57 | // Get this event after the system decides you can start a call 58 | // You can now start a call from within your app 59 | } 60 | 61 | Future _performAnswerCallAction(String uuid) async { 62 | // Called when the user answers an incoming call 63 | } 64 | 65 | Future _performEndCallAction(String uuid) async { 66 | await _callKit.endCall(this.currentCallId); 67 | _currentCallId = null; 68 | } 69 | 70 | Future _didActivateAudioSession() async { 71 | // you might want to do following things when receiving this event: 72 | // - Start playing ringback if it is an outgoing call 73 | } 74 | 75 | Future _didDisplayIncomingCall(String error, String uuid, String handle, 76 | String localizedCallerName, bool fromPushKit) async { 77 | // You will get this event after RNCallKeep finishes showing incoming call UI 78 | // You can check if there was an error while displaying 79 | } 80 | 81 | Future _didPerformSetMutedCallAction(bool mute, String uuid) async { 82 | // Called when the system or user mutes a call 83 | } 84 | 85 | Future _didPerformDTMFAction(String digit, String uuid) async { 86 | // Called when the system or user performs a DTMF action 87 | } 88 | 89 | Future _didToggleHoldAction(bool hold, String uuid) async { 90 | // Called when the system or user holds a call 91 | } 92 | 93 | String get currentCallId { 94 | if (_currentCallId == null) { 95 | final uuid = new Uuid(); 96 | _currentCallId = uuid.v4(); 97 | } 98 | 99 | return _currentCallId; 100 | } 101 | 102 | @override 103 | Widget build(BuildContext context) { 104 | return MaterialApp( 105 | home: Scaffold( 106 | appBar: AppBar( 107 | title: const Text('Plugin example app'), 108 | ), 109 | body: Center( 110 | child: Text('Flutter Call Kit Configured: $_configured\n'), 111 | ), 112 | ), 113 | ); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /example/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.8.1" 11 | boolean_selector: 12 | dependency: transitive 13 | description: 14 | name: boolean_selector 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "2.1.0" 18 | characters: 19 | dependency: transitive 20 | description: 21 | name: characters 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.1.0" 25 | charcode: 26 | dependency: transitive 27 | description: 28 | name: charcode 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.3.1" 32 | clock: 33 | dependency: transitive 34 | description: 35 | name: clock 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.1.0" 39 | collection: 40 | dependency: transitive 41 | description: 42 | name: collection 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.15.0" 46 | convert: 47 | dependency: transitive 48 | description: 49 | name: convert 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "2.1.1" 53 | crypto: 54 | dependency: transitive 55 | description: 56 | name: crypto 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "2.1.3" 60 | cupertino_icons: 61 | dependency: "direct main" 62 | description: 63 | name: cupertino_icons 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "0.1.2" 67 | fake_async: 68 | dependency: transitive 69 | description: 70 | name: fake_async 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "1.2.0" 74 | flutter: 75 | dependency: "direct main" 76 | description: flutter 77 | source: sdk 78 | version: "0.0.0" 79 | flutter_call_kit: 80 | dependency: "direct dev" 81 | description: 82 | path: ".." 83 | relative: true 84 | source: path 85 | version: "0.0.3" 86 | flutter_test: 87 | dependency: "direct dev" 88 | description: flutter 89 | source: sdk 90 | version: "0.0.0" 91 | matcher: 92 | dependency: transitive 93 | description: 94 | name: matcher 95 | url: "https://pub.dartlang.org" 96 | source: hosted 97 | version: "0.12.10" 98 | meta: 99 | dependency: transitive 100 | description: 101 | name: meta 102 | url: "https://pub.dartlang.org" 103 | source: hosted 104 | version: "1.7.0" 105 | path: 106 | dependency: transitive 107 | description: 108 | name: path 109 | url: "https://pub.dartlang.org" 110 | source: hosted 111 | version: "1.8.0" 112 | sky_engine: 113 | dependency: transitive 114 | description: flutter 115 | source: sdk 116 | version: "0.0.99" 117 | source_span: 118 | dependency: transitive 119 | description: 120 | name: source_span 121 | url: "https://pub.dartlang.org" 122 | source: hosted 123 | version: "1.8.1" 124 | stack_trace: 125 | dependency: transitive 126 | description: 127 | name: stack_trace 128 | url: "https://pub.dartlang.org" 129 | source: hosted 130 | version: "1.10.0" 131 | stream_channel: 132 | dependency: transitive 133 | description: 134 | name: stream_channel 135 | url: "https://pub.dartlang.org" 136 | source: hosted 137 | version: "2.1.0" 138 | string_scanner: 139 | dependency: transitive 140 | description: 141 | name: string_scanner 142 | url: "https://pub.dartlang.org" 143 | source: hosted 144 | version: "1.1.0" 145 | term_glyph: 146 | dependency: transitive 147 | description: 148 | name: term_glyph 149 | url: "https://pub.dartlang.org" 150 | source: hosted 151 | version: "1.2.0" 152 | test_api: 153 | dependency: transitive 154 | description: 155 | name: test_api 156 | url: "https://pub.dartlang.org" 157 | source: hosted 158 | version: "0.4.2" 159 | typed_data: 160 | dependency: transitive 161 | description: 162 | name: typed_data 163 | url: "https://pub.dartlang.org" 164 | source: hosted 165 | version: "1.3.0" 166 | uuid: 167 | dependency: "direct main" 168 | description: 169 | name: uuid 170 | url: "https://pub.dartlang.org" 171 | source: hosted 172 | version: "2.0.1" 173 | vector_math: 174 | dependency: transitive 175 | description: 176 | name: vector_math 177 | url: "https://pub.dartlang.org" 178 | source: hosted 179 | version: "2.1.0" 180 | sdks: 181 | dart: ">=2.12.0 <3.0.0" 182 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_call_kit_example 2 | description: Demonstrates how to use the flutter_call_kit plugin. 3 | publish_to: 'none' 4 | 5 | environment: 6 | sdk: ">=2.1.0 <3.0.0" 7 | 8 | dependencies: 9 | flutter: 10 | sdk: flutter 11 | 12 | # The following adds the Cupertino Icons font to your application. 13 | # Use with the CupertinoIcons class for iOS style icons. 14 | cupertino_icons: ^0.1.2 15 | uuid: 2.0.1 16 | 17 | dev_dependencies: 18 | flutter_test: 19 | sdk: flutter 20 | 21 | flutter_call_kit: 22 | path: ../ 23 | 24 | # For information on the generic Dart part of this file, see the 25 | # following page: https://dart.dev/tools/pub/pubspec 26 | 27 | # The following section is specific to Flutter. 28 | flutter: 29 | 30 | # The following line ensures that the Material Icons font is 31 | # included with your application, so that you can use the icons in 32 | # the material Icons class. 33 | uses-material-design: true 34 | 35 | # To add assets to your application, add an assets section, like this: 36 | # assets: 37 | # - images/a_dot_burr.jpeg 38 | # - images/a_dot_ham.jpeg 39 | 40 | # An image asset can refer to one or more resolution-specific "variants", see 41 | # https://flutter.dev/assets-and-images/#resolution-aware. 42 | 43 | # For details regarding adding assets from package dependencies, see 44 | # https://flutter.dev/assets-and-images/#from-packages 45 | 46 | # To add custom fonts to your application, add a fonts section here, 47 | # in this "flutter" section. Each entry in this list should have a 48 | # "family" key with the font family name, and a "fonts" key with a 49 | # list giving the asset and other descriptors for the font. For 50 | # example: 51 | # fonts: 52 | # - family: Schyler 53 | # fonts: 54 | # - asset: fonts/Schyler-Regular.ttf 55 | # - asset: fonts/Schyler-Italic.ttf 56 | # style: italic 57 | # - family: Trajan Pro 58 | # fonts: 59 | # - asset: fonts/TrajanPro.ttf 60 | # - asset: fonts/TrajanPro_Bold.ttf 61 | # weight: 700 62 | # 63 | # For details regarding fonts from package dependencies, 64 | # see https://flutter.dev/custom-fonts/#from-packages 65 | -------------------------------------------------------------------------------- /example/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:flutter_call_kit_example/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Verify Platform version', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(MyApp()); 17 | 18 | // Verify that platform version is retrieved. 19 | expect( 20 | find.byWidgetPredicate( 21 | (Widget widget) => widget is Text && 22 | widget.data.startsWith('Running on:'), 23 | ), 24 | findsOneWidget, 25 | ); 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /example/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | flutter_call_kit_example 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /flutter_call_kit.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/Generated.xcconfig 37 | /Flutter/flutter_export_environment.sh -------------------------------------------------------------------------------- /ios/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/peerwaya/flutter_call_kit/59300690e6dad120d4c0c804344158e52804881d/ios/Assets/.gitkeep -------------------------------------------------------------------------------- /ios/Classes/FlutterCallKitPlugin.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import 5 | 6 | API_AVAILABLE(ios(10.0)) 7 | @interface FlutterCallKitPlugin : NSObject 8 | @property (nonatomic, strong) CXCallController * _Nullable callKitCallController; 9 | @property (nonatomic, strong) CXProvider * _Nullable callKitProvider; 10 | 11 | + (void)reportNewIncomingCall:(NSString *_Nonnull)uuidString 12 | handle:(NSString *_Nonnull)handle 13 | handleType:(NSString *_Nonnull)handleType 14 | hasVideo:(BOOL)hasVideo 15 | localizedCallerName:(NSString * _Nullable)localizedCallerName 16 | fromPushKit:(BOOL)fromPushKit 17 | completion:(void (^)(void))completion; 18 | @end 19 | -------------------------------------------------------------------------------- /ios/Classes/FlutterCallKitPlugin.m: -------------------------------------------------------------------------------- 1 | #import "FlutterCallKitPlugin.h" 2 | 3 | #import 4 | 5 | #ifdef DEBUG 6 | static int const OUTGOING_CALL_WAKEUP_DELAY = 10; 7 | #else 8 | static int const OUTGOING_CALL_WAKEUP_DELAY = 5; 9 | #endif 10 | 11 | static NSString *const kHandleStartCallNotification = @"handleStartCallNotification"; 12 | static NSString *const kDidReceiveStartCallAction = @"didReceiveStartCallAction"; 13 | static NSString *const kPerformAnswerCallAction = @"performAnswerCallAction"; 14 | static NSString *const kPerformEndCallAction = @"performEndCallAction"; 15 | static NSString *const kDidActivateAudioSession = @"didActivateAudioSession"; 16 | static NSString *const kDidDeactivateAudioSession = @"didDeactivateAudioSession"; 17 | static NSString *const kDidDisplayIncomingCall = @"didDisplayIncomingCall"; 18 | static NSString *const kDidPerformSetMutedCallAction = @"didPerformSetMutedCallAction"; 19 | static NSString *const kDidPerformDTMFAction = @"didPerformDTMFAction"; 20 | static NSString *const kDidToggleHoldAction = @"didToggleHoldAction"; 21 | 22 | static NSString *const kProviderReset = @"onProviderReset"; 23 | 24 | NSString *const kIncomingCallNotification = @"incomingCallNotification"; 25 | 26 | static FlutterError *getFlutterError(NSError *error) { 27 | if (error == nil) return nil; 28 | return [FlutterError errorWithCode:[NSString stringWithFormat:@"Error %ld", error.code] 29 | message:error.domain 30 | details:error.localizedDescription]; 31 | } 32 | 33 | @implementation FlutterCallKitPlugin { 34 | FlutterMethodChannel* _channel; 35 | NSOperatingSystemVersion _version; 36 | BOOL _isConfigured; 37 | NSDictionary *_incomingCallNotification; 38 | NSDictionary *_startCallNotification; 39 | } 40 | 41 | static CXProvider* sharedProvider; 42 | 43 | + (void)registerWithRegistrar:(NSObject*)registrar { 44 | FlutterCallKitPlugin* instance = [[FlutterCallKitPlugin alloc] initWithRegistrar:registrar messenger:[registrar messenger]]; 45 | [registrar addApplicationDelegate:instance]; 46 | } 47 | 48 | - (instancetype)initWithRegistrar:(NSObject*)registrar 49 | messenger:(NSObject*)messenger{ 50 | self = [super init]; 51 | if (self) { 52 | _channel = [FlutterMethodChannel 53 | methodChannelWithName:@"com.peerwaya/flutter_callkit_plugin" 54 | binaryMessenger:[registrar messenger]]; 55 | [registrar addMethodCallDelegate:self channel:_channel]; 56 | _isConfigured = NO; 57 | [[NSNotificationCenter defaultCenter] addObserver:self 58 | selector:@selector(handleNewIncomingCall:) 59 | name:kIncomingCallNotification 60 | object:nil]; 61 | } 62 | return self; 63 | } 64 | 65 | + (void)initCallKitProvider { 66 | if (sharedProvider == nil) { 67 | NSDictionary *settings = [[NSUserDefaults standardUserDefaults] dictionaryForKey:@"FlutterCallKitPluginSettings"]; 68 | sharedProvider = [[CXProvider alloc] initWithConfiguration:[FlutterCallKitPlugin getProviderConfiguration:settings]]; 69 | NSLog(@"[FlutterCallKit][initCallKitProvider] sharedProvider initialized with %@", settings); 70 | } 71 | } 72 | 73 | - (void)dealloc 74 | { 75 | #ifdef DEBUG 76 | NSLog(@"[FlutterCallKit][dealloc]"); 77 | #endif 78 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 79 | 80 | if (self.callKitProvider != nil) { 81 | [self.callKitProvider invalidate]; 82 | } 83 | sharedProvider = nil; 84 | } 85 | 86 | - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { 87 | NSString *method = call.method; 88 | if ([@"configure" isEqualToString:method]) { 89 | _isConfigured = YES; 90 | [self configure:call.arguments result:result]; 91 | }else if ([@"checkIfBusy" isEqualToString:method]) { 92 | #ifdef DEBUG 93 | NSLog(@"[FlutterCallKitPlugin][checkIfBusy]"); 94 | #endif 95 | result(@(self.callKitCallController.callObserver.calls.count > 0)); 96 | }else if ([@"checkSpeaker" isEqualToString:method]) { 97 | #ifdef DEBUG 98 | NSLog(@"[FlutterCallKitPlugin][checkSpeaker]"); 99 | #endif 100 | NSString *output = [AVAudioSession sharedInstance].currentRoute.outputs.count > 0 ? [AVAudioSession sharedInstance].currentRoute.outputs[0].portType : nil; 101 | result(@([output isEqualToString:@"Speaker"])); 102 | }else if ([@"displayIncomingCall" isEqualToString:method]) { 103 | [self displayIncomingCall:call.arguments result:result]; 104 | }else if ([@"startCall" isEqualToString:method]) { 105 | [self startCall:call.arguments result:result]; 106 | }else if ([@"reportConnectingOutgoingCallWithUUID" isEqualToString:method]) { 107 | [self reportConnectingOutgoingCallWithUUID:(NSString *)call.arguments result:result]; 108 | }else if ([@"reportConnectedOutgoingCallWithUUID" isEqualToString:method]) { 109 | [self reportConnectedOutgoingCallWithUUID:(NSString *)call.arguments result:result]; 110 | }else if ([@"reportEndCall" isEqualToString:method]) { 111 | [self reportEndCall:call.arguments result:result]; 112 | }else if ([@"endCall" isEqualToString:method]) { 113 | [self endCall:(NSString *)call.arguments result:result]; 114 | }else if ([@"endAllCalls" isEqualToString:method]) { 115 | [self endAllCalls:result]; 116 | }else if ([@"setMutedCall" isEqualToString:method]) { 117 | [self setMutedCall:call.arguments result:result]; 118 | }else if ([@"updateDisplay" isEqualToString:method]) { 119 | [self updateDisplay:call.arguments result:result]; 120 | }else if ([@"setOnHold" isEqualToString:method]) { 121 | [self setOnHold:call.arguments result:result]; 122 | }else { 123 | result(FlutterMethodNotImplemented); 124 | } 125 | } 126 | 127 | - (void)configure:(NSDictionary *)options result:(FlutterResult)result 128 | { 129 | #ifdef DEBUG 130 | NSLog(@"[FlutterCallKitPlugin][setup] options = %@", options); 131 | #endif 132 | _version = [[[NSProcessInfo alloc] init] operatingSystemVersion]; 133 | self.callKitCallController = [[CXCallController alloc] initWithQueue:dispatch_get_main_queue()]; 134 | NSDictionary *settings = [[NSMutableDictionary alloc] initWithDictionary:options]; 135 | // Store settings in NSUserDefault 136 | [[NSUserDefaults standardUserDefaults] setObject:settings forKey:@"FlutterCallKitPluginSettings"]; 137 | [[NSUserDefaults standardUserDefaults] synchronize]; 138 | 139 | [FlutterCallKitPlugin initCallKitProvider]; 140 | 141 | self.callKitProvider = sharedProvider; 142 | [self.callKitProvider setDelegate:self queue:nil]; 143 | if (_incomingCallNotification != nil) { 144 | [_channel invokeMethod:kDidDisplayIncomingCall arguments:_incomingCallNotification]; 145 | _incomingCallNotification = nil; 146 | } 147 | if (_startCallNotification != nil) { 148 | [_channel invokeMethod:kDidDisplayIncomingCall arguments:_startCallNotification]; 149 | _startCallNotification = nil; 150 | } 151 | result(nil); 152 | } 153 | 154 | #pragma mark - CXCallController call actions 155 | - (void)displayIncomingCall:(NSDictionary *)arguments result:(FlutterResult)result 156 | { 157 | NSString* uuidString = arguments[@"uuid"]; 158 | NSString* handle = arguments[@"handle"]; 159 | NSString* handleType = arguments[@"handleType"]; 160 | NSNumber* video = arguments[@"video"]; 161 | NSString* localizedCallerName = arguments[@"localizedCallerName"]; 162 | [FlutterCallKitPlugin reportNewIncomingCall:uuidString handle:handle handleType:handleType hasVideo:[video boolValue] localizedCallerName:localizedCallerName fromPushKit:NO completion:nil]; 163 | result(nil); 164 | } 165 | 166 | - (void)startCall:(NSDictionary *)arguments result:(FlutterResult)result 167 | { 168 | NSString* uuidString = arguments[@"uuid"]; 169 | NSString* handle = arguments[@"handle"]; 170 | NSString* handleType = arguments[@"handleType"]; 171 | NSString* contactIdentifier = arguments[@"contactIdentifier"]; 172 | NSNumber* video = arguments[@"video"]; 173 | #ifdef DEBUG 174 | NSLog(@"[FlutterCallKitPlugin][startCall] uuid = %@", uuidString); 175 | #endif 176 | int _handleType = [FlutterCallKitPlugin getHandleType:handleType]; 177 | NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:uuidString]; 178 | CXHandle *callHandle = [[CXHandle alloc] initWithType:_handleType value:handle]; 179 | CXStartCallAction *startCallAction = [[CXStartCallAction alloc] initWithCallUUID:uuid handle:callHandle]; 180 | [startCallAction setVideo:[video boolValue]]; 181 | [startCallAction setContactIdentifier:contactIdentifier]; 182 | CXTransaction *transaction = [[CXTransaction alloc] initWithAction:startCallAction]; 183 | 184 | [self requestTransaction:transaction result:result]; 185 | } 186 | 187 | - (void)endCall:(NSString *)uuidString result:(FlutterResult)result 188 | { 189 | #ifdef DEBUG 190 | NSLog(@"[FlutterCallKitPlugin][endCall] uuid = %@", uuidString); 191 | #endif 192 | NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:uuidString]; 193 | CXEndCallAction *endCallAction = [[CXEndCallAction alloc] initWithCallUUID:uuid]; 194 | CXTransaction *transaction = [[CXTransaction alloc] initWithAction:endCallAction]; 195 | 196 | [self requestTransaction:transaction result:result]; 197 | } 198 | 199 | - (void)endAllCalls:(FlutterResult)result 200 | { 201 | #ifdef DEBUG 202 | NSLog(@"[FlutterCallKitPlugin][endAllCalls] calls = %@", self.callKitCallController.callObserver.calls); 203 | #endif 204 | for (CXCall *call in self.callKitCallController.callObserver.calls) { 205 | CXEndCallAction *endCallAction = [[CXEndCallAction alloc] initWithCallUUID:call.UUID]; 206 | CXTransaction *transaction = [[CXTransaction alloc] initWithAction:endCallAction]; 207 | [self requestTransaction:transaction result:nil]; 208 | } 209 | result(nil); 210 | } 211 | 212 | - (void)setOnHold:(NSDictionary *)arguments result:(FlutterResult)result 213 | { 214 | NSString* uuidString = arguments[@"uuid"]; 215 | NSNumber* hold = arguments[@"hold"]; 216 | #ifdef DEBUG 217 | NSLog(@"[FlutterCallKitPlugin][setOnHold] uuid = %@, shouldHold = %d", uuidString, [hold boolValue]); 218 | #endif 219 | NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:uuidString]; 220 | CXSetHeldCallAction *setHeldCallAction = [[CXSetHeldCallAction alloc] initWithCallUUID:uuid onHold:[hold boolValue]]; 221 | CXTransaction *transaction = [[CXTransaction alloc] init]; 222 | [transaction addAction:setHeldCallAction]; 223 | 224 | [self requestTransaction:transaction result:result]; 225 | } 226 | 227 | - (void)reportConnectingOutgoingCallWithUUID:(NSString *)uuidString result:(FlutterResult)result 228 | { 229 | NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:uuidString]; 230 | [self.callKitProvider reportOutgoingCallWithUUID:uuid startedConnectingAtDate:[NSDate date]]; 231 | result(nil); 232 | } 233 | 234 | - (void)reportConnectedOutgoingCallWithUUID:(NSString *)uuidString result:(FlutterResult)result 235 | { 236 | NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:uuidString]; 237 | [self.callKitProvider reportOutgoingCallWithUUID:uuid connectedAtDate:[NSDate date]]; 238 | result(nil); 239 | } 240 | 241 | - (void)reportEndCall:(NSDictionary *)arguments result:(FlutterResult)result 242 | { 243 | NSString* uuidString = arguments[@"uuid"]; 244 | NSNumber* reason = arguments[@"reason"]; 245 | #ifdef DEBUG 246 | NSLog(@"[FlutterCallKitPlugin][reportEndCallWithUUID] uuid = %@ reason = %d", uuidString, [reason intValue]); 247 | #endif 248 | NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:uuidString]; 249 | switch ([reason intValue]) { 250 | case CXCallEndedReasonFailed: 251 | [self.callKitProvider reportCallWithUUID:uuid endedAtDate:[NSDate date] reason:CXCallEndedReasonFailed]; 252 | break; 253 | case CXCallEndedReasonRemoteEnded: 254 | [self.callKitProvider reportCallWithUUID:uuid endedAtDate:[NSDate date] reason:CXCallEndedReasonRemoteEnded]; 255 | break; 256 | case CXCallEndedReasonUnanswered: 257 | [self.callKitProvider reportCallWithUUID:uuid endedAtDate:[NSDate date] reason:CXCallEndedReasonUnanswered]; 258 | break; 259 | case CXCallEndedReasonAnsweredElsewhere: 260 | [self.callKitProvider reportCallWithUUID:uuid endedAtDate:[NSDate date] reason:CXCallEndedReasonUnanswered]; 261 | break; 262 | case CXCallEndedReasonDeclinedElsewhere: 263 | [self.callKitProvider reportCallWithUUID:uuid endedAtDate:[NSDate date] reason:CXCallEndedReasonUnanswered]; 264 | break; 265 | default: 266 | break; 267 | } 268 | result(nil); 269 | } 270 | 271 | - (void)updateDisplay:(NSDictionary *)arguments result:(FlutterResult)result 272 | { 273 | NSString* uuidString = arguments[@"uuid"]; 274 | NSString* displayName = arguments[@"displayName"]; 275 | NSString* handle = arguments[@"handle"]; 276 | NSString* handleType = arguments[@"handleType"]; 277 | #ifdef DEBUG 278 | NSLog(@"[FlutterCallKitPlugin][updateDisplay] uuid = %@ displayName = %@ handle = %@", uuidString, displayName, handle); 279 | #endif 280 | NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:uuidString]; 281 | int _handleType = [FlutterCallKitPlugin getHandleType:handleType]; 282 | CXHandle *callHandle = [[CXHandle alloc] initWithType:_handleType value:handle]; 283 | CXCallUpdate *callUpdate = [[CXCallUpdate alloc] init]; 284 | callUpdate.localizedCallerName = displayName; 285 | callUpdate.remoteHandle = callHandle; 286 | [self.callKitProvider reportCallWithUUID:uuid updated:callUpdate]; 287 | result(nil); 288 | } 289 | 290 | - (void)setMutedCall:(NSDictionary *)arguments result:(FlutterResult)result 291 | { 292 | NSString* uuidString = arguments[@"uuid"]; 293 | NSNumber* muted = arguments[@"muted"]; 294 | #ifdef DEBUG 295 | NSLog(@"[FlutterCallKitPlugin][setMutedCall] muted = %i", [muted boolValue]); 296 | #endif 297 | NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:uuidString]; 298 | CXSetMutedCallAction *setMutedAction = [[CXSetMutedCallAction alloc] initWithCallUUID:uuid muted:[muted boolValue]]; 299 | CXTransaction *transaction = [[CXTransaction alloc] init]; 300 | [transaction addAction:setMutedAction]; 301 | 302 | [self requestTransaction:transaction result:result]; 303 | } 304 | 305 | - (void)sendDTMF:(NSDictionary *)arguments result:(FlutterResult)result 306 | { 307 | NSString* uuidString = arguments[@"uuid"]; 308 | NSString* key = arguments[@"key"]; 309 | #ifdef DEBUG 310 | NSLog(@"[FlutterCallKitPlugin][sendDTMF] key = %@", key); 311 | #endif 312 | NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:uuidString]; 313 | CXPlayDTMFCallAction *dtmfAction = [[CXPlayDTMFCallAction alloc] initWithCallUUID:uuid digits:key type:CXPlayDTMFCallActionTypeHardPause]; 314 | CXTransaction *transaction = [[CXTransaction alloc] init]; 315 | [transaction addAction:dtmfAction]; 316 | 317 | [self requestTransaction:transaction result:result]; 318 | } 319 | 320 | - (void)requestTransaction:(CXTransaction *)transaction result:(FlutterResult)result 321 | { 322 | #ifdef DEBUG 323 | NSLog(@"[FlutterCallKitPlugin][requestTransaction] transaction = %@", transaction); 324 | #endif 325 | if (self.callKitCallController == nil) { 326 | self.callKitCallController = [[CXCallController alloc] initWithQueue:dispatch_get_main_queue()]; 327 | } 328 | [self.callKitCallController requestTransaction:transaction completion:^(NSError * _Nullable error) { 329 | if (error != nil) { 330 | NSLog(@"[FlutterCallKitPlugin][requestTransaction] Error requesting transaction (%@): (%@)", transaction.actions, error); 331 | if (result) { 332 | result(getFlutterError(error)); 333 | } 334 | } else { 335 | NSLog(@"[FlutterCallKitPlugin][requestTransaction] Requested transaction successfully"); 336 | 337 | // CXStartCallAction 338 | if ([[transaction.actions firstObject] isKindOfClass:[CXStartCallAction class]]) { 339 | CXStartCallAction *startCallAction = [transaction.actions firstObject]; 340 | CXCallUpdate *callUpdate = [[CXCallUpdate alloc] init]; 341 | callUpdate.remoteHandle = startCallAction.handle; 342 | callUpdate.hasVideo = startCallAction.video; 343 | callUpdate.localizedCallerName = startCallAction.contactIdentifier; 344 | callUpdate.supportsDTMF = NO; 345 | callUpdate.supportsHolding = NO; 346 | callUpdate.supportsGrouping = NO; 347 | callUpdate.supportsUngrouping = NO; 348 | [self.callKitProvider reportCallWithUUID:startCallAction.callUUID updated:callUpdate]; 349 | } 350 | if (result) { 351 | result(nil); 352 | } 353 | } 354 | }]; 355 | } 356 | 357 | - (BOOL)lessThanIos10_2 358 | { 359 | if (_version.majorVersion < 10) { 360 | return YES; 361 | } else if (_version.majorVersion > 10) { 362 | return NO; 363 | } else { 364 | return _version.minorVersion < 2; 365 | } 366 | } 367 | 368 | + (int)getHandleType:(NSString *)handleType 369 | { 370 | int _handleType; 371 | if ([handleType isEqualToString:@"generic"]) { 372 | _handleType = CXHandleTypeGeneric; 373 | } else if ([handleType isEqualToString:@"number"]) { 374 | _handleType = CXHandleTypePhoneNumber; 375 | } else if ([handleType isEqualToString:@"email"]) { 376 | _handleType = CXHandleTypeEmailAddress; 377 | } else { 378 | _handleType = CXHandleTypeGeneric; 379 | } 380 | return _handleType; 381 | } 382 | 383 | + (CXProviderConfiguration *)getProviderConfiguration:(NSDictionary*)settings 384 | { 385 | #ifdef DEBUG 386 | NSLog(@"[FlutterCallKitPlugin][getProviderConfiguration]"); 387 | #endif 388 | CXProviderConfiguration *providerConfiguration = [[CXProviderConfiguration alloc] initWithLocalizedName:settings[@"appName"]]; 389 | providerConfiguration.supportsVideo = YES; 390 | providerConfiguration.maximumCallGroups = 3; 391 | providerConfiguration.maximumCallsPerCallGroup = 1; 392 | providerConfiguration.supportedHandleTypes = [NSSet setWithObjects:[NSNumber numberWithInteger:CXHandleTypeGeneric],[NSNumber numberWithInteger:CXHandleTypePhoneNumber], [NSNumber numberWithInteger:CXHandleTypeEmailAddress], nil]; 393 | if (@available(iOS 11.0, *)) { 394 | providerConfiguration.includesCallsInRecents = [settings[@"includesCallsInRecents"] boolValue]; 395 | } 396 | if (settings[@"supportsVideo"]) { 397 | providerConfiguration.supportsVideo = [settings[@"supportsVideo"] boolValue]; 398 | } 399 | if (settings[@"maximumCallGroups"]) { 400 | providerConfiguration.maximumCallGroups = [settings[@"maximumCallGroups"] integerValue]; 401 | } 402 | if (settings[@"maximumCallsPerCallGroup"]) { 403 | providerConfiguration.maximumCallsPerCallGroup = [settings[@"maximumCallsPerCallGroup"] integerValue]; 404 | } 405 | if (settings[@"imageName"]) { 406 | providerConfiguration.iconTemplateImageData = UIImagePNGRepresentation([UIImage imageNamed:settings[@"imageName"]]); 407 | } 408 | if (settings[@"ringtoneSound"]) { 409 | providerConfiguration.ringtoneSound = settings[@"ringtoneSound"]; 410 | } 411 | return providerConfiguration; 412 | } 413 | 414 | - (void)configureAudioSession 415 | { 416 | #ifdef DEBUG 417 | NSLog(@"[FlutterCallKitPlugin][configureAudioSession] Activating audio session"); 418 | #endif 419 | 420 | AVAudioSession* audioSession = [AVAudioSession sharedInstance]; 421 | [audioSession setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:nil]; 422 | 423 | [audioSession setMode:AVAudioSessionModeVoiceChat error:nil]; 424 | 425 | double sampleRate = 44100.0; 426 | [audioSession setPreferredSampleRate:sampleRate error:nil]; 427 | 428 | NSTimeInterval bufferDuration = .005; 429 | [audioSession setPreferredIOBufferDuration:bufferDuration error:nil]; 430 | [audioSession setActive:TRUE error:nil]; 431 | } 432 | 433 | #pragma mark - AppDelegate 434 | - (BOOL)application:(UIApplication *)application 435 | continueUserActivity:(NSUserActivity *)userActivity 436 | restorationHandler:(void (^)(NSArray> *restorableObjects))restorationHandler 437 | { 438 | #ifdef DEBUG 439 | NSLog(@"[FlutterCallKitPlugin][application:continueUserActivity]"); 440 | #endif 441 | INInteraction *interaction = userActivity.interaction; 442 | INPerson *contact; 443 | NSString *handle; 444 | BOOL isAudioCall; 445 | BOOL isVideoCall; 446 | 447 | //HACK TO AVOID XCODE 10 COMPILE CRASH 448 | //REMOVE ON NEXT MAJOR RELEASE OF RNCALLKIT 449 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 450 | //XCode 11 451 | // iOS 13 returns an INStartCallIntent userActivity type 452 | if (@available(iOS 13, *)) { 453 | INStartCallIntent *intent = (INStartCallIntent*)interaction.intent; 454 | if ([intent respondsToSelector:@selector(callCapability)]) { 455 | isAudioCall = intent.callCapability == INCallCapabilityAudioCall; 456 | isVideoCall = intent.callCapability == INCallCapabilityVideoCall; 457 | } else { 458 | isAudioCall = [userActivity.activityType isEqualToString:INStartAudioCallIntentIdentifier]; 459 | isVideoCall = [userActivity.activityType isEqualToString:INStartVideoCallIntentIdentifier]; 460 | } 461 | } else { 462 | #endif 463 | //XCode 10 and below 464 | isAudioCall = [userActivity.activityType isEqualToString:INStartAudioCallIntentIdentifier]; 465 | isVideoCall = [userActivity.activityType isEqualToString:INStartVideoCallIntentIdentifier]; 466 | //HACK TO AVOID XCODE 10 COMPILE CRASH 467 | //REMOVE ON NEXT MAJOR RELEASE OF RNCALLKIT 468 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 469 | } 470 | #endif 471 | 472 | if (isAudioCall) { 473 | INStartAudioCallIntent *startAudioCallIntent = (INStartAudioCallIntent *)interaction.intent; 474 | contact = [startAudioCallIntent.contacts firstObject]; 475 | } else if (isVideoCall) { 476 | INStartVideoCallIntent *startVideoCallIntent = (INStartVideoCallIntent *)interaction.intent; 477 | contact = [startVideoCallIntent.contacts firstObject]; 478 | } 479 | 480 | if (contact != nil) { 481 | handle = contact.personHandle.value; 482 | } 483 | 484 | if (handle != nil && handle.length > 0 ){ 485 | NSDictionary *userInfo = @{ 486 | @"handle": handle, 487 | @"video": @(isVideoCall) 488 | }; 489 | if (_isConfigured) { 490 | [_channel invokeMethod:kHandleStartCallNotification arguments:userInfo]; 491 | } else { 492 | _startCallNotification = userInfo; 493 | } 494 | return YES; 495 | } 496 | return NO; 497 | } 498 | 499 | + (void)reportNewIncomingCall:(NSString *)uuidString 500 | handle:(NSString *)handle 501 | handleType:(NSString *)handleType 502 | hasVideo:(BOOL)hasVideo 503 | localizedCallerName:(NSString * _Nullable)localizedCallerName 504 | fromPushKit:(BOOL)fromPushKit 505 | completion: (void (^)(void))completion 506 | { 507 | #ifdef DEBUG 508 | NSLog(@"[FlutterCallKitPlugin][reportNewIncomingCall] uuidString = %@, handle = %@, handleType = %@, hasVideo = %@, localizedCallerName = %@, fromPushKit = %@", uuidString, handle, handleType, @(hasVideo), localizedCallerName, @(fromPushKit) ); 509 | #endif 510 | int _handleType = [FlutterCallKitPlugin getHandleType:handleType]; 511 | NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:uuidString]; 512 | NSLog(@"[FlutterCallKitPlugin][reportNewIncomingCall] uuid = %@", uuid ); 513 | CXCallUpdate *callUpdate = [[CXCallUpdate alloc] init]; 514 | callUpdate.remoteHandle = [[CXHandle alloc] initWithType:_handleType value:handle]; 515 | callUpdate.supportsDTMF = YES; 516 | callUpdate.supportsHolding = YES; 517 | callUpdate.supportsGrouping = YES; 518 | callUpdate.supportsUngrouping = YES; 519 | callUpdate.hasVideo = hasVideo; 520 | callUpdate.localizedCallerName = localizedCallerName; 521 | 522 | [FlutterCallKitPlugin initCallKitProvider]; 523 | [sharedProvider reportNewIncomingCallWithUUID:uuid update:callUpdate completion:^(NSError * _Nullable error) { 524 | [[NSNotificationCenter defaultCenter] postNotificationName:kIncomingCallNotification 525 | object:self 526 | userInfo:@{ @"error": error ? error.localizedDescription : @"", @"callUUID": [uuidString lowercaseString], @"handle": handle, @"localizedCallerName": localizedCallerName, @"fromPushKit":[NSNumber numberWithBool:fromPushKit] }]; 527 | if(completion!=nil){ 528 | completion(); 529 | } 530 | }]; 531 | } 532 | 533 | - (void)handleNewIncomingCall:(NSNotification *)notification 534 | { 535 | #ifdef DEBUG 536 | NSLog(@"[FlutterCallKitPlugin] handleNewIncomingCall notification.userInfo = %@", notification.userInfo); 537 | #endif 538 | if (_isConfigured) { 539 | [_channel invokeMethod:kDidDisplayIncomingCall arguments:notification.userInfo]; 540 | } else { 541 | _incomingCallNotification = notification.userInfo; 542 | } 543 | NSDictionary *userInfo = (NSDictionary *)notification.userInfo; 544 | if (userInfo[@"error"] == nil) { 545 | // Workaround per https://forums.developer.apple.com/message/169511 546 | if ([self lessThanIos10_2]) { 547 | [self configureAudioSession]; 548 | } 549 | } 550 | } 551 | 552 | #pragma mark - CXProviderDelegate 553 | 554 | - (void)providerDidReset:(CXProvider *)provider{ 555 | #ifdef DEBUG 556 | NSLog(@"[FlutterCallKitPlugin][providerDidReset]"); 557 | #endif 558 | //this means something big changed, so tell the JS. The JS should 559 | //probably respond by hanging up all calls. 560 | [_channel invokeMethod:kProviderReset arguments:nil]; 561 | } 562 | 563 | // Starting outgoing call 564 | - (void)provider:(CXProvider *)provider performStartCallAction:(CXStartCallAction *)action 565 | { 566 | #ifdef DEBUG 567 | NSLog(@"[FlutterCallKitPlugin][CXProviderDelegate][provider:performStartCallAction]"); 568 | #endif 569 | //do this first, audio sessions are flakey 570 | [self configureAudioSession]; 571 | //tell the JS to actually make the call 572 | [_channel invokeMethod:kDidReceiveStartCallAction arguments:@{ @"callUUID": [action.callUUID.UUIDString lowercaseString], @"handle": action.handle.value } result:^(id _Nullable result) { 573 | [action fulfill]; 574 | }]; 575 | 576 | } 577 | 578 | // Answering incoming call 579 | - (void)provider:(CXProvider *)provider performAnswerCallAction:(CXAnswerCallAction *)action 580 | { 581 | #ifdef DEBUG 582 | NSLog(@"[FlutterCallKitPlugin][CXProviderDelegate][provider:performAnswerCallAction]"); 583 | #endif 584 | [self configureAudioSession]; 585 | [_channel invokeMethod:kPerformAnswerCallAction arguments:@{ @"callUUID": [action.callUUID.UUIDString lowercaseString] } result:^(id _Nullable result) { 586 | [action fulfill]; 587 | }]; 588 | 589 | } 590 | 591 | // Ending incoming call 592 | - (void)provider:(CXProvider *)provider performEndCallAction:(CXEndCallAction *)action 593 | { 594 | #ifdef DEBUG 595 | NSLog(@"[FlutterCallKitPlugin][CXProviderDelegate][provider:performEndCallAction]"); 596 | #endif 597 | [_channel invokeMethod:kPerformEndCallAction arguments:@{ @"callUUID": [action.callUUID.UUIDString lowercaseString] } result:^(id _Nullable result) { 598 | [action fulfill]; 599 | }]; 600 | } 601 | 602 | 603 | -(void)provider:(CXProvider *)provider performSetHeldCallAction:(CXSetHeldCallAction *)action 604 | { 605 | #ifdef DEBUG 606 | NSLog(@"[FlutterCallKitPlugin][CXProviderDelegate][provider:performSetHeldCallAction]"); 607 | #endif 608 | [_channel invokeMethod:kDidToggleHoldAction arguments:@{ @"hold": @(action.onHold), @"callUUID": [action.callUUID.UUIDString lowercaseString] } result:^(id _Nullable result) { 609 | [action fulfill]; 610 | }]; 611 | 612 | } 613 | 614 | - (void)provider:(CXProvider *)provider performPlayDTMFCallAction:(CXPlayDTMFCallAction *)action { 615 | #ifdef DEBUG 616 | NSLog(@"[FlutterCallKitPlugin][CXProviderDelegate][provider:performPlayDTMFCallAction]"); 617 | #endif 618 | [_channel invokeMethod:kDidPerformDTMFAction arguments:@{ @"digits": action.digits, @"callUUID": [action.callUUID.UUIDString lowercaseString] } result:^(id _Nullable result) { 619 | [action fulfill]; 620 | }]; 621 | 622 | } 623 | 624 | -(void)provider:(CXProvider *)provider performSetMutedCallAction:(CXSetMutedCallAction *)action 625 | { 626 | #ifdef DEBUG 627 | NSLog(@"[FlutterCallKitPlugin][CXProviderDelegate][provider:performSetMutedCallAction]"); 628 | #endif 629 | [_channel invokeMethod:kDidPerformSetMutedCallAction arguments:@{ @"muted": @(action.muted), @"callUUID": [action.callUUID.UUIDString lowercaseString] } result:^(id _Nullable result) { 630 | [action fulfill]; 631 | }]; 632 | 633 | } 634 | 635 | - (void)provider:(CXProvider *)provider timedOutPerformingAction:(CXAction *)action 636 | { 637 | #ifdef DEBUG 638 | NSLog(@"[FlutterCallKitPlugin][CXProviderDelegate][provider:timedOutPerformingAction]"); 639 | #endif 640 | } 641 | 642 | - (void)provider:(CXProvider *)provider didActivateAudioSession:(AVAudioSession *)audioSession 643 | { 644 | #ifdef DEBUG 645 | NSLog(@"[FlutterCallKitPlugin][CXProviderDelegate][provider:didActivateAudioSession]"); 646 | #endif 647 | NSDictionary *userInfo 648 | = @{ 649 | AVAudioSessionInterruptionTypeKey: [NSNumber numberWithInt:AVAudioSessionInterruptionTypeEnded], 650 | AVAudioSessionInterruptionOptionKey: [NSNumber numberWithInt:AVAudioSessionInterruptionOptionShouldResume] 651 | }; 652 | [[NSNotificationCenter defaultCenter] postNotificationName:AVAudioSessionInterruptionNotification object:nil userInfo:userInfo]; 653 | 654 | [self configureAudioSession]; 655 | [_channel invokeMethod:kDidActivateAudioSession arguments:nil]; 656 | } 657 | 658 | - (void)provider:(CXProvider *)provider didDeactivateAudioSession:(AVAudioSession *)audioSession 659 | { 660 | #ifdef DEBUG 661 | NSLog(@"[FlutterCallKitPlugin][CXProviderDelegate][provider:didDeactivateAudioSession]"); 662 | #endif 663 | [_channel invokeMethod:kDidDeactivateAudioSession arguments:nil]; 664 | } 665 | @end 666 | -------------------------------------------------------------------------------- /ios/flutter_call_kit.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 3 | # 4 | Pod::Spec.new do |s| 5 | s.name = 'flutter_call_kit' 6 | s.version = '0.0.1' 7 | s.summary = 'A new flutter plugin project.' 8 | s.description = <<-DESC 9 | A new flutter plugin project. 10 | DESC 11 | s.homepage = 'http://example.com' 12 | s.license = { :file => '../LICENSE' } 13 | s.author = { 'Your Company' => 'email@example.com' } 14 | s.source = { :path => '.' } 15 | s.source_files = 'Classes/**/*' 16 | s.public_header_files = 'Classes/**/*.h' 17 | s.dependency 'Flutter' 18 | 19 | s.ios.deployment_target = '8.0' 20 | end 21 | 22 | -------------------------------------------------------------------------------- /lib/flutter_call_kit.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | 4 | import 'package:flutter/services.dart'; 5 | import 'package:flutter/widgets.dart'; 6 | 7 | /// Device sends this event once it decides the app is allowed to start a call, either from the built-in phone screens (iOS/_Recents_), or by the app calling `RNCallKeep.startCall`. 8 | /// 9 | /// Try to start your app call action from here (e.g. get credentials of the user by `data.handle` and/or send INVITE to your SIP server) 10 | /// Note: on iOS `callUUID` is not defined as the call is not yet managed by CallKit. You have to generate your own and call `startCall`. 11 | /// [uuid] - The UUID of the call that is to be answered 12 | /// [handle] - Phone number of the callee see [HandleType] for other options 13 | typedef Future OnReceiveStartCallAction(String? uuid, String? handle); 14 | 15 | /// User answer the incoming call 16 | /// 17 | /// [uuid]- The UUID of the call that is to be answered. 18 | typedef Future OnAnswerCallAction(String? uuid); 19 | 20 | /// User finish the call. 21 | /// 22 | /// [uuid]- The UUID of the call that is to be ended. 23 | typedef Future OnEndCallAction(String? uuid); 24 | 25 | /// The [AudioSession] has been activated by **FlutterCallKit**. 26 | /// 27 | /// you might want to do following things when receiving this event: 28 | /// - Start playing ringback if it is an outgoing call 29 | typedef Future OnActivateAudioSession(); 30 | 31 | /// The [AudioSession] has been deactivated. 32 | typedef Future OnDeactivateAudioSession(); 33 | 34 | /// Callback for [displayIncomingCall] 35 | /// 36 | /// you might want to do following things when receiving this event: 37 | /// Start playing ringback if it is an outgoing call 38 | typedef Future OnIncomingCall(String? error, String? uuid, 39 | String? handle, String? localizedCallerName, bool? fromPushKit); 40 | 41 | /// A call was muted by the system or the user: 42 | /// 43 | /// [muted] - whether the call was muted 44 | /// [uuid] - The UUID of the call. 45 | typedef Future OnMuted(bool? muted, String? uuid); 46 | 47 | /// A call was held or unheld by the current user 48 | /// 49 | /// [hold] - whether the call was held 50 | /// [uuid] - The UUID of the call. 51 | typedef Future OnHold(bool? hold, String? uuid); 52 | 53 | /// Used to type a number on his dialer 54 | /// 55 | /// [digits] - The digits that emitted the dtmf tone 56 | /// [uuid] - The UUID of the call. 57 | typedef Future OnDTMF(String? digits, String? uuid); 58 | 59 | typedef Future OnStartCall(String? handle, bool? video); 60 | 61 | enum HandleType { phoneNumber, generic, email } 62 | 63 | enum EndReason { 64 | failed, 65 | remoteEnded, 66 | unanswered, 67 | } 68 | 69 | class IOSOptions { 70 | /// It will be displayed on system UI when incoming calls received 71 | final String appName; 72 | 73 | /// If provided, it will be displayed on system UI during the call 74 | final String imageName; 75 | 76 | /// If provided, it will be played when incoming calls received; the system will use the default ringtone if this is not provided 77 | final String ringtoneSound; 78 | 79 | /// If provided, the maximum number of call groups supported by this application (Default: 3) 80 | final int maximumCallGroups; 81 | 82 | /// If provided, the maximum number of calls in a single group, used for conferencing (Default: 1, no conferencing) 83 | final int maximumCallsPerCallGroup; 84 | 85 | /// If provided, whether or not the application supports video calling (Default: true) 86 | final bool supportsVideo; 87 | 88 | /// If provided, whether or not the application saves calls in users recents call log (Default: true, iOS 11+ only) 89 | final bool includesCallsInRecents; 90 | 91 | IOSOptions( 92 | this.appName, { 93 | this.imageName = "", 94 | this.ringtoneSound = "", 95 | this.maximumCallGroups = 3, 96 | this.maximumCallsPerCallGroup = 1, 97 | this.supportsVideo = true, 98 | this.includesCallsInRecents = true, 99 | }) : assert(appName != null), 100 | assert(imageName != null), 101 | assert(ringtoneSound != null), 102 | assert(maximumCallGroups != null), 103 | assert(maximumCallsPerCallGroup != null), 104 | assert(supportsVideo != null), 105 | assert(includesCallsInRecents != null); 106 | 107 | Map toMap() { 108 | return { 109 | "appName": appName, 110 | "imageName": imageName, 111 | "ringtoneSound": ringtoneSound, 112 | "maximumCallGroups": maximumCallGroups.toString(), 113 | "maximumCallsPerCallGroup": maximumCallsPerCallGroup.toString(), 114 | "supportsVideo": supportsVideo, 115 | "includesCallsInRecents": includesCallsInRecents, 116 | }; 117 | } 118 | } 119 | 120 | class FlutterCallKit { 121 | factory FlutterCallKit() => _instance; 122 | 123 | @visibleForTesting 124 | FlutterCallKit.private(MethodChannel channel) : _channel = channel; 125 | 126 | static final FlutterCallKit _instance = FlutterCallKit.private( 127 | const MethodChannel('com.peerwaya/flutter_callkit_plugin')); 128 | 129 | final MethodChannel _channel; 130 | 131 | OnReceiveStartCallAction? _didReceiveStartCallAction; 132 | 133 | /// this means something big changed, so tell the Dart side. The Dart side should 134 | /// probably respond by hanging up all calls. 135 | VoidCallback? _onProviderReset; 136 | 137 | OnAnswerCallAction? _performAnswerCallAction; 138 | 139 | OnEndCallAction? _performEndCallAction; 140 | 141 | OnActivateAudioSession? _didActivateAudioSession; 142 | 143 | OnDeactivateAudioSession? _didDeactivateAudioSession; 144 | 145 | OnIncomingCall? _didDisplayIncomingCall; 146 | 147 | OnMuted? _didPerformSetMutedCallAction; 148 | OnHold? _didToggleHoldAction; 149 | OnDTMF? _didPerformDTMFAction; 150 | 151 | OnStartCall? _handleStartCallNotification; 152 | 153 | /// Configures with [options] and sets up handlers for incoming messages. 154 | void configure( 155 | IOSOptions options, { 156 | OnReceiveStartCallAction? didReceiveStartCallAction, 157 | VoidCallback? onProviderReset, 158 | OnAnswerCallAction? performAnswerCallAction, 159 | OnEndCallAction? performEndCallAction, 160 | OnActivateAudioSession? didActivateAudioSession, 161 | OnDeactivateAudioSession? didDeactivateAudioSession, 162 | OnIncomingCall? didDisplayIncomingCall, 163 | OnMuted? didPerformSetMutedCallAction, 164 | OnDTMF? didPerformDTMFAction, 165 | OnHold? didToggleHoldAction, 166 | OnStartCall? handleStartCallNotification, 167 | }) { 168 | if (!Platform.isIOS) { 169 | return; 170 | } 171 | _didReceiveStartCallAction = didReceiveStartCallAction; 172 | _onProviderReset = onProviderReset; 173 | _performAnswerCallAction = performAnswerCallAction; 174 | _performEndCallAction = performEndCallAction; 175 | _didActivateAudioSession = didActivateAudioSession; 176 | _didDeactivateAudioSession = didDeactivateAudioSession; 177 | _didDisplayIncomingCall = didDisplayIncomingCall; 178 | _didPerformSetMutedCallAction = didPerformSetMutedCallAction; 179 | _didPerformDTMFAction = didPerformDTMFAction; 180 | _didToggleHoldAction = didToggleHoldAction; 181 | _handleStartCallNotification = handleStartCallNotification; 182 | _channel.setMethodCallHandler(_handleMethod); 183 | _channel.invokeMethod('configure', options.toMap()); 184 | } 185 | 186 | Future _handleMethod(MethodCall call) async { 187 | switch (call.method) { 188 | case "didReceiveStartCallAction": 189 | if (_didReceiveStartCallAction == null) { 190 | return null; 191 | } 192 | Map map = call.arguments.cast(); 193 | return _didReceiveStartCallAction!(map["callUUID"], map["handle"]); 194 | case "onProviderReset": 195 | if (_onProviderReset == null) { 196 | return null; 197 | } 198 | return _onProviderReset!(); 199 | case "performAnswerCallAction": 200 | if (_performAnswerCallAction == null) { 201 | return null; 202 | } 203 | return _performAnswerCallAction!( 204 | call.arguments.cast()["callUUID"]); 205 | case "performEndCallAction": 206 | if (_performEndCallAction == null) { 207 | return null; 208 | } 209 | return _performEndCallAction!( 210 | call.arguments.cast()["callUUID"]); 211 | case "didActivateAudioSession": 212 | if (_didActivateAudioSession == null) { 213 | return null; 214 | } 215 | return _didActivateAudioSession!(); 216 | case "didDeactivateAudioSession": 217 | if (_didDeactivateAudioSession == null) { 218 | return null; 219 | } 220 | return _didDeactivateAudioSession!(); 221 | case "didDisplayIncomingCall": 222 | if (_didDisplayIncomingCall == null) { 223 | print("_didDisplayIncomingCall is null"); 224 | return null; 225 | } 226 | Map map = call.arguments.cast(); 227 | return _didDisplayIncomingCall!(map["error"], map["callUUID"], 228 | map["handle"], map["localizedCallerName"], map["fromPushKit"]); 229 | case "didPerformSetMutedCallAction": 230 | if (_didPerformSetMutedCallAction == null) { 231 | return null; 232 | } 233 | Map map = call.arguments.cast(); 234 | return _didPerformSetMutedCallAction!(map["muted"], map["callUUID"]); 235 | case "didPerformDTMFAction": 236 | if (_didPerformDTMFAction == null) { 237 | return null; 238 | } 239 | Map map = call.arguments.cast(); 240 | return _didPerformDTMFAction!(map["digits"], map["callUUID"]); 241 | case "didToggleHoldAction": 242 | if (_didToggleHoldAction == null) { 243 | return null; 244 | } 245 | Map map = call.arguments.cast(); 246 | return _didToggleHoldAction!(map["hold"], map["callUUID"]); 247 | case "handleStartCallNotification": 248 | if (_handleStartCallNotification == null) { 249 | return null; 250 | } 251 | Map map = call.arguments.cast(); 252 | return _handleStartCallNotification!(map["handle"], map["video"]); 253 | default: 254 | throw UnsupportedError("Unrecognized JSON message"); 255 | } 256 | } 257 | 258 | /// Display system UI for incoming calls 259 | /// 260 | /// An [uuid] that should be stored and re-used for [stopCall]. 261 | /// A [handle] e.g Phone number of the caller 262 | /// A [handleType] which describes this [handle] see [HandleType] 263 | /// tell the system whether this is a [video] call 264 | Future displayIncomingCall( 265 | String uuid, String handle, String localizedCallerName, 266 | {HandleType handleType = HandleType.phoneNumber, 267 | bool video = false}) async { 268 | if (!Platform.isIOS) { 269 | return; 270 | } 271 | await _channel.invokeMethod('displayIncomingCall', { 272 | "uuid": uuid, 273 | "handle": handle, 274 | "localizedCallerName": localizedCallerName, 275 | "handleType": handleTypeToString(handleType), 276 | "video": video, 277 | }); 278 | } 279 | 280 | /// When you make an outgoing call, tell the device that a call is occurring. 281 | /// 282 | /// An [uuid] that should be stored and re-used for [stopCall]. 283 | /// A [handle] e.g Phone number of the caller 284 | /// The [contactIdentifier] is displayed in the native call UI, and is typically the name of the call recipient. 285 | /// A [handleType] which describes this [handle] see [HandleType] 286 | /// tell the system whether this is a [video] call 287 | /// 288 | Future startCall(String uuid, String handle, String contactIdentifier, 289 | {HandleType handleType = HandleType.phoneNumber, 290 | bool video = false}) async { 291 | if (!Platform.isIOS) { 292 | return; 293 | } 294 | await _channel.invokeMethod('startCall', { 295 | "uuid": uuid, 296 | "handle": handle, 297 | "contactIdentifier": contactIdentifier, 298 | "handleType": handleTypeToString(handleType), 299 | "video": video, 300 | }); 301 | } 302 | 303 | Future reportConnectingOutgoingCallWithUUID(String uuid) async { 304 | if (!Platform.isIOS) { 305 | return; 306 | } 307 | await _channel.invokeMethod( 308 | 'reportConnectingOutgoingCallWithUUID', uuid); 309 | } 310 | 311 | Future reportConnectedOutgoingCallWithUUID(String uuid) async { 312 | if (!Platform.isIOS) { 313 | return; 314 | } 315 | await _channel.invokeMethod( 316 | 'reportConnectedOutgoingCallWithUUID', uuid); 317 | } 318 | 319 | /// Report that the call ended without the user initiating 320 | /// 321 | /// The [uuid] used for [startCall] or [displayIncomingCall] 322 | /// [reason] for the end call one of [EndReason] 323 | Future reportEndCallWithUUID(String? uuid, EndReason reason) async { 324 | if (uuid == null) { 325 | throw Exception( 326 | 'reportEndCallWithUUID. Check uuid, it should not be null'); 327 | } 328 | if (!Platform.isIOS) { 329 | return; 330 | } 331 | await _channel.invokeMethod('reportEndCall', { 332 | 'uuid': uuid, 333 | 'reason': endReasonToInt(reason), 334 | }); 335 | } 336 | 337 | Future rejectCall(String uuid) async { 338 | if (!Platform.isIOS) { 339 | return; 340 | } 341 | await _channel.invokeMethod('endCall', uuid); 342 | } 343 | 344 | /// When you finish an incoming/outgoing call. 345 | /// 346 | /// The [uuid] used for `startCall` or `displayIncomingCall` 347 | 348 | Future endCall(String? uuid) async { 349 | if (uuid == null) { 350 | throw Exception('End call. Check uuid, it should not be null'); 351 | } 352 | if (!Platform.isIOS) { 353 | return; 354 | } 355 | await _channel.invokeMethod('endCall', uuid); 356 | } 357 | 358 | /// End all calls that have been started on the device. 359 | /// 360 | Future endAllCalls() async { 361 | if (!Platform.isIOS) { 362 | return; 363 | } 364 | await _channel.invokeMethod('endAllCalls'); 365 | } 366 | 367 | /// Switch the mic on/off. 368 | /// 369 | /// [uuid] of the current call. 370 | /// set [mute] to true or false 371 | Future setMutedCall(String uuid, bool mute) async { 372 | if (!Platform.isIOS) { 373 | return; 374 | } 375 | await _channel.invokeMethod('setMutedCall', { 376 | 'uuid': uuid, 377 | 'mute': mute, 378 | }); 379 | } 380 | 381 | /// Checks if there are any active calls on the device and returns a future with a boolean value 382 | /// (`true` if there're active calls, `false` otherwise). 383 | /// 384 | Future checkIfBusy() async { 385 | if (!Platform.isIOS) { 386 | return null; 387 | } 388 | return await _channel.invokeMethod('checkIfBusy') as bool?; 389 | } 390 | 391 | /// Checks if the device speaker is on and returns a promise with a boolean value (`true` if speaker is on, `false` otherwise). 392 | /// 393 | Future checkSpeaker() async { 394 | if (!Platform.isIOS) { 395 | return null; 396 | } 397 | return await _channel.invokeMethod('checkSpeaker') as bool?; 398 | } 399 | 400 | /// Use this to update the display after an outgoing call has started. 401 | /// 402 | /// The [uuid] used for [startCall] or [displayIncomingCall] 403 | /// A [handle] e.g Phone number of the caller 404 | /// The [displayName] is the name of the caller to be displayed on the native UI 405 | /// A [handleType] which describes this [handle] see [HandleType] 406 | /// 407 | Future updateDisplay(String uuid, String handle, String displayName, 408 | {HandleType handleType = HandleType.phoneNumber}) async { 409 | if (!Platform.isIOS) { 410 | return; 411 | } 412 | await _channel.invokeMethod('updateDisplay', { 413 | "uuid": uuid, 414 | "handle": handle, 415 | "handleType": handleTypeToString(handleType), 416 | "displayName": displayName, 417 | }); 418 | } 419 | 420 | /// Set a call on/off hold. 421 | /// 422 | /// [uuid] of the current call. 423 | /// set [hold] to true or false 424 | 425 | Future setOnHold(String uuid, bool hold) async { 426 | if (!Platform.isIOS) { 427 | return; 428 | } 429 | await _channel.invokeMethod('setMutedCall', { 430 | 'uuid': uuid, 431 | 'hold': hold, 432 | }); 433 | } 434 | 435 | Future setReachable() async { 436 | if (!Platform.isIOS) { 437 | return; 438 | } 439 | await _channel.invokeMethod('setReachable'); 440 | } 441 | 442 | static String handleTypeToString(HandleType handleType) { 443 | switch (handleType) { 444 | case HandleType.generic: 445 | return "generic"; 446 | case HandleType.phoneNumber: 447 | return "number"; 448 | case HandleType.email: 449 | return "email"; 450 | default: 451 | return "number"; 452 | } 453 | } 454 | 455 | static int endReasonToInt(EndReason reason) { 456 | switch (reason) { 457 | case EndReason.failed: 458 | return 1; 459 | case EndReason.remoteEnded: 460 | return 2; 461 | case EndReason.unanswered: 462 | return 3; 463 | default: 464 | return 1; 465 | } 466 | } 467 | } 468 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.8.1" 11 | boolean_selector: 12 | dependency: transitive 13 | description: 14 | name: boolean_selector 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "2.1.0" 18 | characters: 19 | dependency: transitive 20 | description: 21 | name: characters 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.1.0" 25 | charcode: 26 | dependency: transitive 27 | description: 28 | name: charcode 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.3.1" 32 | clock: 33 | dependency: transitive 34 | description: 35 | name: clock 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.1.0" 39 | collection: 40 | dependency: transitive 41 | description: 42 | name: collection 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.15.0" 46 | fake_async: 47 | dependency: transitive 48 | description: 49 | name: fake_async 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.2.0" 53 | flutter: 54 | dependency: "direct main" 55 | description: flutter 56 | source: sdk 57 | version: "0.0.0" 58 | flutter_test: 59 | dependency: "direct dev" 60 | description: flutter 61 | source: sdk 62 | version: "0.0.0" 63 | matcher: 64 | dependency: transitive 65 | description: 66 | name: matcher 67 | url: "https://pub.dartlang.org" 68 | source: hosted 69 | version: "0.12.10" 70 | meta: 71 | dependency: transitive 72 | description: 73 | name: meta 74 | url: "https://pub.dartlang.org" 75 | source: hosted 76 | version: "1.7.0" 77 | path: 78 | dependency: transitive 79 | description: 80 | name: path 81 | url: "https://pub.dartlang.org" 82 | source: hosted 83 | version: "1.8.0" 84 | sky_engine: 85 | dependency: transitive 86 | description: flutter 87 | source: sdk 88 | version: "0.0.99" 89 | source_span: 90 | dependency: transitive 91 | description: 92 | name: source_span 93 | url: "https://pub.dartlang.org" 94 | source: hosted 95 | version: "1.8.1" 96 | stack_trace: 97 | dependency: transitive 98 | description: 99 | name: stack_trace 100 | url: "https://pub.dartlang.org" 101 | source: hosted 102 | version: "1.10.0" 103 | stream_channel: 104 | dependency: transitive 105 | description: 106 | name: stream_channel 107 | url: "https://pub.dartlang.org" 108 | source: hosted 109 | version: "2.1.0" 110 | string_scanner: 111 | dependency: transitive 112 | description: 113 | name: string_scanner 114 | url: "https://pub.dartlang.org" 115 | source: hosted 116 | version: "1.1.0" 117 | term_glyph: 118 | dependency: transitive 119 | description: 120 | name: term_glyph 121 | url: "https://pub.dartlang.org" 122 | source: hosted 123 | version: "1.2.0" 124 | test_api: 125 | dependency: transitive 126 | description: 127 | name: test_api 128 | url: "https://pub.dartlang.org" 129 | source: hosted 130 | version: "0.4.2" 131 | typed_data: 132 | dependency: transitive 133 | description: 134 | name: typed_data 135 | url: "https://pub.dartlang.org" 136 | source: hosted 137 | version: "1.3.0" 138 | vector_math: 139 | dependency: transitive 140 | description: 141 | name: vector_math 142 | url: "https://pub.dartlang.org" 143 | source: hosted 144 | version: "2.1.0" 145 | sdks: 146 | dart: ">=2.12.0 <3.0.0" 147 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_call_kit 2 | description: Flutter iOS CallKit framework - Currently iOS >= 10.0 only 3 | version: 0.0.3 4 | author: Onyemaechi Okafor 5 | homepage: https://github.com/peerwaya/flutter_call_kit 6 | 7 | environment: 8 | sdk: '>=2.12.0 <3.0.0' 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | 14 | dev_dependencies: 15 | flutter_test: 16 | sdk: flutter 17 | 18 | # For information on the generic Dart part of this file, see the 19 | # following page: https://dart.dev/tools/pub/pubspec 20 | 21 | # The following section is specific to Flutter. 22 | flutter: 23 | # This section identifies this Flutter project as a plugin project. 24 | # The androidPackage and pluginClass identifiers should not ordinarily 25 | # be modified. They are used by the tooling to maintain consistency when 26 | # adding or updating assets for this project. 27 | plugin: 28 | androidPackage: com.example.flutter_call_kit 29 | pluginClass: FlutterCallKitPlugin 30 | 31 | # To add assets to your plugin package, add an assets section, like this: 32 | # assets: 33 | # - images/a_dot_burr.jpeg 34 | # - images/a_dot_ham.jpeg 35 | # 36 | # For details regarding assets in packages, see 37 | # https://flutter.dev/assets-and-images/#from-packages 38 | # 39 | # An image asset can refer to one or more resolution-specific "variants", see 40 | # https://flutter.dev/assets-and-images/#resolution-aware. 41 | 42 | # To add custom fonts to your plugin package, add a fonts section here, 43 | # in this "flutter" section. Each entry in this list should have a 44 | # "family" key with the font family name, and a "fonts" key with a 45 | # list giving the asset and other descriptors for the font. For 46 | # example: 47 | # fonts: 48 | # - family: Schyler 49 | # fonts: 50 | # - asset: fonts/Schyler-Regular.ttf 51 | # - asset: fonts/Schyler-Italic.ttf 52 | # style: italic 53 | # - family: Trajan Pro 54 | # fonts: 55 | # - asset: fonts/TrajanPro.ttf 56 | # - asset: fonts/TrajanPro_Bold.ttf 57 | # weight: 700 58 | # 59 | # For details regarding fonts in packages, see 60 | # https://flutter.dev/custom-fonts/#from-packages 61 | -------------------------------------------------------------------------------- /test/flutter_call_kit_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | void main() { 5 | const MethodChannel channel = MethodChannel('flutter_call_kit'); 6 | 7 | TestWidgetsFlutterBinding.ensureInitialized(); 8 | 9 | setUp(() { 10 | channel.setMockMethodCallHandler((MethodCall methodCall) async { 11 | return '42'; 12 | }); 13 | }); 14 | 15 | tearDown(() { 16 | channel.setMockMethodCallHandler(null); 17 | }); 18 | } 19 | --------------------------------------------------------------------------------