voice-react-native-sdk 1.6.2-fork.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +206 -0
- package/README.md +179 -0
- package/android/build.gradle +113 -0
- package/android/gradle.properties +29 -0
- package/android/src/main/AndroidManifest.xml +30 -0
- package/android/src/main/java/com/twiliovoicereactnative/AudioSwitchManager.java +141 -0
- package/android/src/main/java/com/twiliovoicereactnative/CallListenerProxy.java +186 -0
- package/android/src/main/java/com/twiliovoicereactnative/CallMessageListenerProxy.java +103 -0
- package/android/src/main/java/com/twiliovoicereactnative/CallRecordDatabase.java +183 -0
- package/android/src/main/java/com/twiliovoicereactnative/CommonConstants.java +195 -0
- package/android/src/main/java/com/twiliovoicereactnative/ConfigurationProperties.java +41 -0
- package/android/src/main/java/com/twiliovoicereactnative/Constants.java +23 -0
- package/android/src/main/java/com/twiliovoicereactnative/ExpoActivityLifecycleListener.java +27 -0
- package/android/src/main/java/com/twiliovoicereactnative/ExpoApplicationLifecycleListener.java +14 -0
- package/android/src/main/java/com/twiliovoicereactnative/ExpoModule.kt +36 -0
- package/android/src/main/java/com/twiliovoicereactnative/ExpoPackage.java +20 -0
- package/android/src/main/java/com/twiliovoicereactnative/JSEventEmitter.java +98 -0
- package/android/src/main/java/com/twiliovoicereactnative/MediaPlayerManager.java +58 -0
- package/android/src/main/java/com/twiliovoicereactnative/NotificationUtility.java +363 -0
- package/android/src/main/java/com/twiliovoicereactnative/ReactNativeArgumentsSerializer.java +276 -0
- package/android/src/main/java/com/twiliovoicereactnative/SDKLog.java +74 -0
- package/android/src/main/java/com/twiliovoicereactnative/StatsListenerProxy.java +178 -0
- package/android/src/main/java/com/twiliovoicereactnative/TwilioVoiceReactNativeModule.java +778 -0
- package/android/src/main/java/com/twiliovoicereactnative/TwilioVoiceReactNativePackage.java +28 -0
- package/android/src/main/java/com/twiliovoicereactnative/VoiceActivityProxy.java +101 -0
- package/android/src/main/java/com/twiliovoicereactnative/VoiceApplicationProxy.java +133 -0
- package/android/src/main/java/com/twiliovoicereactnative/VoiceFirebaseMessagingService.java +88 -0
- package/android/src/main/java/com/twiliovoicereactnative/VoiceService.java +403 -0
- package/android/src/main/res/drawable/answered_call_small_icon.png +0 -0
- package/android/src/main/res/drawable/ic_launcher_round.png +0 -0
- package/android/src/main/res/drawable/ic_launcher_sdk.png +0 -0
- package/android/src/main/res/drawable/incoming_call_small_icon.png +0 -0
- package/android/src/main/res/drawable/outgoing_call_small_icon.png +0 -0
- package/android/src/main/res/raw/disconnect.wav +0 -0
- package/android/src/main/res/raw/incoming.wav +0 -0
- package/android/src/main/res/raw/outgoing.wav +0 -0
- package/android/src/main/res/raw/ringtone.wav +0 -0
- package/android/src/main/res/raw/silent.wav +0 -0
- package/android/src/main/res/values/colors.xml +11 -0
- package/android/src/main/res/values/config.xml +5 -0
- package/android/src/main/res/values/dimens.xml +7 -0
- package/android/src/main/res/values/strings.xml +25 -0
- package/android/src/main/res/values/styles.xml +10 -0
- package/android/src/main/res/values-night/colors.xml +11 -0
- package/expo-config-plugin/android.js +191 -0
- package/expo-config-plugin/ios.js +22 -0
- package/expo-module.config.json +6 -0
- package/ios/TwilioVoicePushRegistry.h +19 -0
- package/ios/TwilioVoicePushRegistry.m +72 -0
- package/ios/TwilioVoiceReactNative+CallInvite.m +56 -0
- package/ios/TwilioVoiceReactNative+CallKit.m +533 -0
- package/ios/TwilioVoiceReactNative+CallMessage.m +81 -0
- package/ios/TwilioVoiceReactNative.h +74 -0
- package/ios/TwilioVoiceReactNative.m +1046 -0
- package/ios/TwilioVoiceReactNative.xcodeproj/project.pbxproj +328 -0
- package/ios/TwilioVoiceReactNativeConstants.h +200 -0
- package/ios/Utilities/TwilioVoiceStatsReport.h +175 -0
- package/lib/commonjs/AudioDevice.js +101 -0
- package/lib/commonjs/AudioDevice.js.map +1 -0
- package/lib/commonjs/Call.js +765 -0
- package/lib/commonjs/Call.js.map +1 -0
- package/lib/commonjs/CallInvite.js +452 -0
- package/lib/commonjs/CallInvite.js.map +1 -0
- package/lib/commonjs/CallMessage/CallMessage.js +54 -0
- package/lib/commonjs/CallMessage/CallMessage.js.map +1 -0
- package/lib/commonjs/CallMessage/IncomingCallMessage.js +120 -0
- package/lib/commonjs/CallMessage/IncomingCallMessage.js.map +1 -0
- package/lib/commonjs/CallMessage/OutgoingCallMessage.js +148 -0
- package/lib/commonjs/CallMessage/OutgoingCallMessage.js.map +1 -0
- package/lib/commonjs/ExpoModule.js +69 -0
- package/lib/commonjs/ExpoModule.js.map +1 -0
- package/lib/commonjs/Voice.js +611 -0
- package/lib/commonjs/Voice.js.map +1 -0
- package/lib/commonjs/common.js +26 -0
- package/lib/commonjs/common.js.map +1 -0
- package/lib/commonjs/constants.js +158 -0
- package/lib/commonjs/constants.js.map +1 -0
- package/lib/commonjs/error/InvalidArgumentError.js +32 -0
- package/lib/commonjs/error/InvalidArgumentError.js.map +1 -0
- package/lib/commonjs/error/InvalidStateError.js +33 -0
- package/lib/commonjs/error/InvalidStateError.js.map +1 -0
- package/lib/commonjs/error/TwilioError.js +38 -0
- package/lib/commonjs/error/TwilioError.js.map +1 -0
- package/lib/commonjs/error/UnsupportedPlatformError.js +33 -0
- package/lib/commonjs/error/UnsupportedPlatformError.js.map +1 -0
- package/lib/commonjs/error/generated.js +2343 -0
- package/lib/commonjs/error/generated.js.map +1 -0
- package/lib/commonjs/error/index.js +112 -0
- package/lib/commonjs/error/index.js.map +1 -0
- package/lib/commonjs/error/utility.js +37 -0
- package/lib/commonjs/error/utility.js.map +1 -0
- package/lib/commonjs/index.js +95 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/type/AudioDevice.js +6 -0
- package/lib/commonjs/type/AudioDevice.js.map +1 -0
- package/lib/commonjs/type/Call.js +6 -0
- package/lib/commonjs/type/Call.js.map +1 -0
- package/lib/commonjs/type/CallInvite.js +6 -0
- package/lib/commonjs/type/CallInvite.js.map +1 -0
- package/lib/commonjs/type/CallKit.js +30 -0
- package/lib/commonjs/type/CallKit.js.map +1 -0
- package/lib/commonjs/type/CallMessage.js +6 -0
- package/lib/commonjs/type/CallMessage.js.map +1 -0
- package/lib/commonjs/type/Error.js +6 -0
- package/lib/commonjs/type/Error.js.map +1 -0
- package/lib/commonjs/type/NativeModule.js +6 -0
- package/lib/commonjs/type/NativeModule.js.map +1 -0
- package/lib/commonjs/type/RTCStats.js +29 -0
- package/lib/commonjs/type/RTCStats.js.map +1 -0
- package/lib/commonjs/type/Voice.js +6 -0
- package/lib/commonjs/type/Voice.js.map +1 -0
- package/lib/commonjs/type/common.js +2 -0
- package/lib/commonjs/type/common.js.map +1 -0
- package/lib/module/AudioDevice.js +97 -0
- package/lib/module/AudioDevice.js.map +1 -0
- package/lib/module/Call.js +766 -0
- package/lib/module/Call.js.map +1 -0
- package/lib/module/CallInvite.js +450 -0
- package/lib/module/CallInvite.js.map +1 -0
- package/lib/module/CallMessage/CallMessage.js +51 -0
- package/lib/module/CallMessage/CallMessage.js.map +1 -0
- package/lib/module/CallMessage/IncomingCallMessage.js +116 -0
- package/lib/module/CallMessage/IncomingCallMessage.js.map +1 -0
- package/lib/module/CallMessage/OutgoingCallMessage.js +154 -0
- package/lib/module/CallMessage/OutgoingCallMessage.js.map +1 -0
- package/lib/module/ExpoModule.js +58 -0
- package/lib/module/ExpoModule.js.map +1 -0
- package/lib/module/Voice.js +598 -0
- package/lib/module/Voice.js.map +1 -0
- package/lib/module/common.js +11 -0
- package/lib/module/common.js.map +1 -0
- package/lib/module/constants.js +151 -0
- package/lib/module/constants.js.map +1 -0
- package/lib/module/error/InvalidArgumentError.js +23 -0
- package/lib/module/error/InvalidArgumentError.js.map +1 -0
- package/lib/module/error/InvalidStateError.js +24 -0
- package/lib/module/error/InvalidStateError.js.map +1 -0
- package/lib/module/error/TwilioError.js +29 -0
- package/lib/module/error/TwilioError.js.map +1 -0
- package/lib/module/error/UnsupportedPlatformError.js +24 -0
- package/lib/module/error/UnsupportedPlatformError.js.map +1 -0
- package/lib/module/error/generated.js +2315 -0
- package/lib/module/error/generated.js.map +1 -0
- package/lib/module/error/index.js +6 -0
- package/lib/module/error/index.js.map +1 -0
- package/lib/module/error/utility.js +27 -0
- package/lib/module/error/utility.js.map +1 -0
- package/lib/module/index.js +23 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/type/AudioDevice.js +2 -0
- package/lib/module/type/AudioDevice.js.map +1 -0
- package/lib/module/type/Call.js +2 -0
- package/lib/module/type/Call.js.map +1 -0
- package/lib/module/type/CallInvite.js +2 -0
- package/lib/module/type/CallInvite.js.map +1 -0
- package/lib/module/type/CallKit.js +22 -0
- package/lib/module/type/CallKit.js.map +1 -0
- package/lib/module/type/CallMessage.js +2 -0
- package/lib/module/type/CallMessage.js.map +1 -0
- package/lib/module/type/Error.js +2 -0
- package/lib/module/type/Error.js.map +1 -0
- package/lib/module/type/NativeModule.js +2 -0
- package/lib/module/type/NativeModule.js.map +1 -0
- package/lib/module/type/RTCStats.js +21 -0
- package/lib/module/type/RTCStats.js.map +1 -0
- package/lib/module/type/Voice.js +2 -0
- package/lib/module/type/Voice.js.map +1 -0
- package/lib/module/type/common.js +2 -0
- package/lib/module/type/common.js.map +1 -0
- package/lib/typescript/AudioDevice.d.ts +74 -0
- package/lib/typescript/Call.d.ts +874 -0
- package/lib/typescript/CallInvite.d.ts +484 -0
- package/lib/typescript/CallMessage/CallMessage.d.ts +53 -0
- package/lib/typescript/CallMessage/IncomingCallMessage.d.ts +75 -0
- package/lib/typescript/CallMessage/OutgoingCallMessage.d.ts +198 -0
- package/lib/typescript/ExpoModule.d.ts +13 -0
- package/lib/typescript/Voice.d.ts +620 -0
- package/lib/typescript/common.d.ts +11 -0
- package/lib/typescript/constants.d.ts +148 -0
- package/lib/typescript/error/InvalidArgumentError.d.ts +11 -0
- package/lib/typescript/error/InvalidStateError.d.ts +12 -0
- package/lib/typescript/error/TwilioError.d.ts +14 -0
- package/lib/typescript/error/UnsupportedPlatformError.d.ts +12 -0
- package/lib/typescript/error/generated.d.ts +1558 -0
- package/lib/typescript/error/index.d.ts +5 -0
- package/lib/typescript/error/utility.d.ts +12 -0
- package/lib/typescript/index.d.ts +18 -0
- package/lib/typescript/type/AudioDevice.d.ts +15 -0
- package/lib/typescript/type/Call.d.ts +59 -0
- package/lib/typescript/type/CallInvite.d.ts +40 -0
- package/lib/typescript/type/CallKit.d.ts +60 -0
- package/lib/typescript/type/CallMessage.d.ts +20 -0
- package/lib/typescript/type/Error.d.ts +9 -0
- package/lib/typescript/type/NativeModule.d.ts +59 -0
- package/lib/typescript/type/RTCStats.d.ts +91 -0
- package/lib/typescript/type/Voice.d.ts +16 -0
- package/lib/typescript/type/common.d.ts +15 -0
- package/package.json +167 -0
- package/src/AudioDevice.tsx +88 -0
- package/src/Call.tsx +1343 -0
- package/src/CallInvite.tsx +757 -0
- package/src/CallMessage/CallMessage.ts +83 -0
- package/src/CallMessage/IncomingCallMessage.ts +104 -0
- package/src/CallMessage/OutgoingCallMessage.ts +308 -0
- package/src/ExpoModule.ts +59 -0
- package/src/Voice.tsx +1010 -0
- package/src/common.ts +16 -0
- package/src/constants.ts +193 -0
- package/src/error/InvalidArgumentError.ts +19 -0
- package/src/error/InvalidStateError.ts +19 -0
- package/src/error/TwilioError.ts +22 -0
- package/src/error/UnsupportedPlatformError.ts +19 -0
- package/src/error/generated.ts +2277 -0
- package/src/error/index.ts +18 -0
- package/src/error/utility.ts +36 -0
- package/src/index.tsx +24 -0
- package/src/type/AudioDevice.ts +18 -0
- package/src/type/Call.ts +102 -0
- package/src/type/CallInvite.ts +59 -0
- package/src/type/CallKit.ts +61 -0
- package/src/type/CallMessage.ts +31 -0
- package/src/type/Error.ts +11 -0
- package/src/type/NativeModule.ts +93 -0
- package/src/type/RTCStats.ts +99 -0
- package/src/type/Voice.ts +31 -0
- package/src/type/common.ts +16 -0
- package/twilio-voice-react-native.podspec +22 -0
|
@@ -0,0 +1,1046 @@
|
|
|
1
|
+
//
|
|
2
|
+
// TwilioVoiceReactNative.m
|
|
3
|
+
// TwilioVoiceReactNative
|
|
4
|
+
//
|
|
5
|
+
// Copyright © 2022 Twilio, Inc. All rights reserved.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
@import AVKit;
|
|
9
|
+
|
|
10
|
+
#import "TwilioVoicePushRegistry.h"
|
|
11
|
+
#import "TwilioVoiceReactNative.h"
|
|
12
|
+
#import "TwilioVoiceReactNativeConstants.h"
|
|
13
|
+
#import "TwilioVoiceStatsReport.h"
|
|
14
|
+
|
|
15
|
+
NSString * const kTwilioVoiceReactNativeVoiceError = @"Voice error";
|
|
16
|
+
dispatch_time_t const kPushRegistryDeviceTokenRetryTimeout = 3;
|
|
17
|
+
dispatch_time_t const kExponentialBackoff = 2;
|
|
18
|
+
|
|
19
|
+
// Call & call invite
|
|
20
|
+
NSString * const kTwilioVoiceReactNativeEventKeyCall = @"call";
|
|
21
|
+
NSString * const kTwilioVoiceReactNativeEventKeyCallInvite = @"callInvite";
|
|
22
|
+
NSString * const kTwilioVoiceReactNativeEventKeyCancelledCallInvite = @"cancelledCallInvite";
|
|
23
|
+
|
|
24
|
+
// Audio device
|
|
25
|
+
NSString * const kTwilioVoiceAudioDeviceUid = @"uid";
|
|
26
|
+
|
|
27
|
+
static TVODefaultAudioDevice *sTwilioAudioDevice;
|
|
28
|
+
|
|
29
|
+
@import TwilioVoice;
|
|
30
|
+
|
|
31
|
+
#pragma mark - Custom AVRoutePickerView Implementation
|
|
32
|
+
|
|
33
|
+
@interface TVRNAVRoutePickerView : AVRoutePickerView
|
|
34
|
+
|
|
35
|
+
- (void)present;
|
|
36
|
+
|
|
37
|
+
@end
|
|
38
|
+
|
|
39
|
+
@implementation TVRNAVRoutePickerView
|
|
40
|
+
|
|
41
|
+
- (instancetype)initWithFrame:(CGRect)frame {
|
|
42
|
+
if (self = [super initWithFrame:frame]) {
|
|
43
|
+
self.hidden = YES;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return self;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
- (void)present {
|
|
50
|
+
UIButton *routePickerButton;
|
|
51
|
+
for (id view in self.subviews) {
|
|
52
|
+
if ([view isKindOfClass:[UIButton class]]) {
|
|
53
|
+
routePickerButton = (UIButton *)view;
|
|
54
|
+
[routePickerButton sendActionsForControlEvents:UIControlEventTouchUpInside];
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@end
|
|
61
|
+
|
|
62
|
+
#pragma mark - TwilioVoiceReactNative
|
|
63
|
+
|
|
64
|
+
@interface TwilioVoiceReactNative ()
|
|
65
|
+
|
|
66
|
+
@property (nonatomic, strong) TwilioVoicePushRegistry *twilioVoicePushRegistry;
|
|
67
|
+
|
|
68
|
+
@property(nonatomic, strong) NSData *deviceTokenData;
|
|
69
|
+
@property(nonatomic, strong) NSMutableDictionary *audioDevices;
|
|
70
|
+
@property(nonatomic, strong) NSDictionary *selectedAudioDevice;
|
|
71
|
+
@property(nonatomic, assign) BOOL registrationInProgress;
|
|
72
|
+
|
|
73
|
+
@end
|
|
74
|
+
|
|
75
|
+
@implementation TwilioVoiceReactNative {
|
|
76
|
+
BOOL _hasObserver;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
- (instancetype)init {
|
|
80
|
+
if (self = [super init]) {
|
|
81
|
+
_callMap = [NSMutableDictionary dictionary];
|
|
82
|
+
_callConnectMap = [NSMutableDictionary dictionary];
|
|
83
|
+
_callInviteMap = [NSMutableDictionary dictionary];
|
|
84
|
+
_cancelledCallInviteMap = [NSMutableDictionary dictionary];
|
|
85
|
+
_audioDevices = [NSMutableDictionary dictionary];
|
|
86
|
+
|
|
87
|
+
NSString *reactNativeSDK = kTwilioVoiceReactNativeReactNativeVoiceSDK;
|
|
88
|
+
setenv("global-env-sdk", [reactNativeSDK UTF8String], 1);
|
|
89
|
+
|
|
90
|
+
NSString *reactNativeSdkVersion = kTwilioVoiceReactNativeReactNativeVoiceSDKVer;
|
|
91
|
+
setenv("com.twilio.voice.env.sdk.version", [reactNativeSdkVersion UTF8String], 1);
|
|
92
|
+
|
|
93
|
+
sTwilioAudioDevice = [TVODefaultAudioDevice audioDevice];
|
|
94
|
+
TwilioVoiceSDK.audioDevice = sTwilioAudioDevice;
|
|
95
|
+
|
|
96
|
+
TwilioVoiceSDK.logLevel = TVOLogLevelTrace;
|
|
97
|
+
|
|
98
|
+
[self subscribeToNotifications];
|
|
99
|
+
[self initializeCallKit];
|
|
100
|
+
[self initializeAudioDeviceList];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return self;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
- (void)initializePushRegistry {
|
|
107
|
+
self.twilioVoicePushRegistry = [TwilioVoicePushRegistry new];
|
|
108
|
+
[self.twilioVoicePushRegistry updatePushRegistry];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
- (void)subscribeToNotifications {
|
|
112
|
+
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
113
|
+
selector:@selector(handlePushRegistryNotification:)
|
|
114
|
+
name:kTwilioVoicePushRegistryNotification
|
|
115
|
+
object:nil];
|
|
116
|
+
|
|
117
|
+
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
118
|
+
selector:@selector(handleRouteChange:)
|
|
119
|
+
name:AVAudioSessionRouteChangeNotification
|
|
120
|
+
object:nil];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
- (void)dealloc {
|
|
124
|
+
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
- (void)handlePushRegistryNotification:(NSNotification *)notification {
|
|
128
|
+
NSMutableDictionary *eventBody = [notification.userInfo mutableCopy];
|
|
129
|
+
NSString *type = eventBody[kTwilioVoiceReactNativeVoiceEventType];
|
|
130
|
+
if ([type isEqualToString:kTwilioVoicePushRegistryNotificationDeviceTokenUpdated]) {
|
|
131
|
+
self.deviceTokenData = eventBody[kTwilioVoicePushRegistryNotificationDeviceToken];
|
|
132
|
+
|
|
133
|
+
// Skip the event emitting since 1, the listener has not registered and 2, the app does not need to know about this
|
|
134
|
+
return;
|
|
135
|
+
} else if ([type isEqualToString:kTwilioVoicePushRegistryNotificationIncomingPushReceived]) {
|
|
136
|
+
NSDictionary *payload = eventBody[kTwilioVoicePushRegistryNotificationIncomingPushPayload];
|
|
137
|
+
[TwilioVoiceSDK handleNotification:payload delegate:self delegateQueue:nil callMessageDelegate:self];
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
+ (TVODefaultAudioDevice *)twilioAudioDevice {
|
|
142
|
+
return sTwilioAudioDevice;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
- (void)handleRouteChange:(NSNotification *)notification {
|
|
146
|
+
[self availableAudioDevices];
|
|
147
|
+
|
|
148
|
+
NSMutableArray *nativeAudioDeviceInfos = [NSMutableArray array];
|
|
149
|
+
for (NSString *key in [self.audioDevices allKeys]) {
|
|
150
|
+
[nativeAudioDeviceInfos addObject:self.audioDevices[key]];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
NSMutableDictionary *eventBody = [@{
|
|
154
|
+
kTwilioVoiceReactNativeVoiceEventType: kTwilioVoiceReactNativeVoiceEventAudioDevicesUpdated,
|
|
155
|
+
kTwilioVoiceReactNativeAudioDeviceKeyAudioDevices: nativeAudioDeviceInfos
|
|
156
|
+
} mutableCopy];
|
|
157
|
+
|
|
158
|
+
if (self.selectedAudioDevice != nil) {
|
|
159
|
+
eventBody[kTwilioVoiceReactNativeAudioDeviceKeySelectedDevice] = self.selectedAudioDevice;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
[self sendEventWithName:kTwilioVoiceReactNativeScopeVoice body:eventBody];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
- (void)initializeAudioDeviceList {
|
|
166
|
+
NSUUID *receiverUuid = [NSUUID UUID];
|
|
167
|
+
NSDictionary *builtInReceiver = @{ kTwilioVoiceReactNativeAudioDeviceKeyUuid: receiverUuid.UUIDString,
|
|
168
|
+
kTwilioVoiceReactNativeAudioDeviceKeyType: kTwilioVoiceReactNativeAudioDeviceKeyEarpiece,
|
|
169
|
+
kTwilioVoiceReactNativeAudioDeviceKeyName: @"iPhone",
|
|
170
|
+
kTwilioVoiceAudioDeviceUid: AVAudioSessionPortBuiltInReceiver};
|
|
171
|
+
self.audioDevices[receiverUuid.UUIDString] = builtInReceiver;
|
|
172
|
+
|
|
173
|
+
NSUUID *speakerUuid = [NSUUID UUID];
|
|
174
|
+
NSDictionary *builtInSpeaker = @{ kTwilioVoiceReactNativeAudioDeviceKeyUuid: speakerUuid.UUIDString,
|
|
175
|
+
kTwilioVoiceReactNativeAudioDeviceKeyType: kTwilioVoiceReactNativeAudioDeviceKeySpeaker,
|
|
176
|
+
kTwilioVoiceReactNativeAudioDeviceKeyName: @"Speaker",
|
|
177
|
+
kTwilioVoiceAudioDeviceUid: AVAudioSessionPortBuiltInSpeaker};
|
|
178
|
+
self.audioDevices[speakerUuid.UUIDString] = builtInSpeaker;
|
|
179
|
+
|
|
180
|
+
[self availableAudioDevices];
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
- (NSDictionary *)availableAudioDevices {
|
|
184
|
+
// Pop all except the built-in earpiece and speaker
|
|
185
|
+
for (NSString *key in [self.audioDevices allKeys]) {
|
|
186
|
+
NSDictionary *audioDevice = self.audioDevices[key];
|
|
187
|
+
if (![audioDevice[kTwilioVoiceAudioDeviceUid] isEqualToString:AVAudioSessionPortBuiltInReceiver] &&
|
|
188
|
+
![audioDevice[kTwilioVoiceAudioDeviceUid] isEqualToString:AVAudioSessionPortBuiltInSpeaker]) {
|
|
189
|
+
[self.audioDevices removeObjectForKey:key];
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
NSLog(@"Available audio input devices");
|
|
194
|
+
NSArray *availableInputs = [[AVAudioSession sharedInstance] availableInputs];
|
|
195
|
+
for (AVAudioSessionPortDescription *port in availableInputs) {
|
|
196
|
+
NSLog(@"\t%@, %@, %@", port.portType, port. portName, port.UID);
|
|
197
|
+
|
|
198
|
+
if ([port.portType isEqualToString:AVAudioSessionPortBluetoothHFP]) {
|
|
199
|
+
NSUUID *uuid = [NSUUID UUID];
|
|
200
|
+
NSDictionary *bluetoothHfpDevice = @{ kTwilioVoiceReactNativeAudioDeviceKeyUuid: uuid.UUIDString,
|
|
201
|
+
kTwilioVoiceReactNativeAudioDeviceKeyType: [self audioPortTypeMapping:port.portType],
|
|
202
|
+
kTwilioVoiceReactNativeAudioDeviceKeyName: port.portName,
|
|
203
|
+
kTwilioVoiceAudioDeviceUid: port.UID };
|
|
204
|
+
self.audioDevices[uuid.UUIDString] = bluetoothHfpDevice;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
NSLog(@"Current route, inputs");
|
|
209
|
+
AVAudioSessionRouteDescription *currentRoute = [[AVAudioSession sharedInstance] currentRoute];
|
|
210
|
+
NSArray *inputs = currentRoute.inputs;
|
|
211
|
+
for (AVAudioSessionPortDescription *port in inputs) {
|
|
212
|
+
NSLog(@"\t%@, %@, %@", port.portType, port.portName, port.UID);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
NSLog(@"Current route, outputs");
|
|
216
|
+
NSArray *outputs = currentRoute.outputs;
|
|
217
|
+
for (AVAudioSessionPortDescription *port in outputs) {
|
|
218
|
+
NSLog(@"\t%@, %@, %@", port.portType, port.portName, port.UID);
|
|
219
|
+
|
|
220
|
+
if ([port.portType isEqualToString:AVAudioSessionPortBuiltInReceiver]) {
|
|
221
|
+
for (NSString *key in [self.audioDevices allKeys]) {
|
|
222
|
+
NSDictionary *device = self.audioDevices[key];
|
|
223
|
+
if ([device[kTwilioVoiceReactNativeAudioDeviceKeyType] isEqualToString:kTwilioVoiceReactNativeAudioDeviceKeyEarpiece]) {
|
|
224
|
+
self.selectedAudioDevice = device;
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
} else if ([port.portType isEqualToString:AVAudioSessionPortBuiltInSpeaker]) {
|
|
229
|
+
for (NSString *key in [self.audioDevices allKeys]) {
|
|
230
|
+
NSDictionary *device = self.audioDevices[key];
|
|
231
|
+
if ([device[kTwilioVoiceReactNativeAudioDeviceKeyType] isEqualToString:kTwilioVoiceReactNativeAudioDeviceKeySpeaker]) {
|
|
232
|
+
self.selectedAudioDevice = device;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
} else {
|
|
236
|
+
BOOL found = NO;
|
|
237
|
+
for (NSString *key in [self.audioDevices allKeys]) {
|
|
238
|
+
NSDictionary *device = self.audioDevices[key];
|
|
239
|
+
if ([device[kTwilioVoiceAudioDeviceUid] isEqualToString:port.UID]) {
|
|
240
|
+
found = YES;
|
|
241
|
+
self.selectedAudioDevice = device;
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (!found) {
|
|
247
|
+
NSLog(@"Unidentified output device selected: %@, %@, %@", port.portType, port.portName, port.UID);
|
|
248
|
+
NSUUID *uuid = [NSUUID UUID];
|
|
249
|
+
NSDictionary *unidentifiedDevice = @{ kTwilioVoiceReactNativeAudioDeviceKeyUuid: uuid.UUIDString,
|
|
250
|
+
kTwilioVoiceReactNativeAudioDeviceKeyType: [self audioPortTypeMapping:port.portType],
|
|
251
|
+
kTwilioVoiceReactNativeAudioDeviceKeyName: port.portName,
|
|
252
|
+
kTwilioVoiceAudioDeviceUid: port.UID };
|
|
253
|
+
self.audioDevices[uuid.UUIDString] = unidentifiedDevice;
|
|
254
|
+
self.selectedAudioDevice = unidentifiedDevice;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return self.audioDevices;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
- (NSString *)audioPortTypeMapping:(NSString *)portType {
|
|
263
|
+
if ([portType isEqualToString:AVAudioSessionPortBuiltInReceiver]) {
|
|
264
|
+
return kTwilioVoiceReactNativeAudioDeviceKeyEarpiece;
|
|
265
|
+
} else if ([portType isEqualToString:AVAudioSessionPortBuiltInSpeaker]) {
|
|
266
|
+
return kTwilioVoiceReactNativeAudioDeviceKeySpeaker;
|
|
267
|
+
} else if ([portType isEqualToString:AVAudioSessionPortBluetoothHFP]) {
|
|
268
|
+
return kTwilioVoiceReactNativeAudioDeviceKeyBluetooth;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return portType;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
- (BOOL)selectAudioDevice:(NSString *)uuid {
|
|
275
|
+
if (!self.audioDevices[uuid]) {
|
|
276
|
+
NSLog(@"No matching audio device found for %@", uuid);
|
|
277
|
+
return NO;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
NSDictionary *device = self.audioDevices[uuid];
|
|
281
|
+
NSString *portUid = device[kTwilioVoiceAudioDeviceUid];
|
|
282
|
+
NSString *portType = device[kTwilioVoiceReactNativeAudioDeviceKeyType];
|
|
283
|
+
|
|
284
|
+
NSLog(@"Selecting %@(%@), %@", device[kTwilioVoiceReactNativeAudioDeviceKeyName], device[kTwilioVoiceReactNativeAudioDeviceKeyType], device[kTwilioVoiceAudioDeviceUid]);
|
|
285
|
+
|
|
286
|
+
AVAudioSessionPortDescription *portDescription = nil;
|
|
287
|
+
if ([portType isEqualToString:kTwilioVoiceReactNativeAudioDeviceKeyEarpiece]) {
|
|
288
|
+
NSArray *availableInputs = [[AVAudioSession sharedInstance] availableInputs];
|
|
289
|
+
for (AVAudioSessionPortDescription *port in availableInputs) {
|
|
290
|
+
if ([port.portType isEqualToString:AVAudioSessionPortBuiltInMic]) {
|
|
291
|
+
portDescription = port;
|
|
292
|
+
break;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (!portDescription) {
|
|
297
|
+
NSLog(@"Built-in mic not found");
|
|
298
|
+
return NO;
|
|
299
|
+
}
|
|
300
|
+
} else if ([portType isEqualToString:kTwilioVoiceReactNativeAudioDeviceKeyBluetooth]) {
|
|
301
|
+
NSArray *availableInputs = [[AVAudioSession sharedInstance] availableInputs];
|
|
302
|
+
for (AVAudioSessionPortDescription *port in availableInputs) {
|
|
303
|
+
if ([port.UID isEqualToString:portUid]) {
|
|
304
|
+
portDescription = port;
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (!portDescription) {
|
|
310
|
+
NSLog(@"Bluetooth device %@ not found", device[kTwilioVoiceReactNativeAudioDeviceKeyName]);
|
|
311
|
+
return NO;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Update preferred input
|
|
316
|
+
NSError *inputError;
|
|
317
|
+
[[AVAudioSession sharedInstance] setPreferredInput:portDescription error:&inputError];
|
|
318
|
+
if (inputError) {
|
|
319
|
+
NSLog(@"Failed to set preferred input: %@", inputError);
|
|
320
|
+
return NO;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Override output to speaker if speaker is selected
|
|
324
|
+
if ([portType isEqualToString:kTwilioVoiceReactNativeAudioDeviceKeySpeaker]) {
|
|
325
|
+
AVAudioSessionPortOverride outputOverride = AVAudioSessionPortOverrideSpeaker;
|
|
326
|
+
NSError *outputError;
|
|
327
|
+
[[AVAudioSession sharedInstance] overrideOutputAudioPort:outputOverride error:&outputError];
|
|
328
|
+
if (outputError) {
|
|
329
|
+
NSLog(@"Failed to override output port: %@", outputError);
|
|
330
|
+
return NO;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return YES;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// TODO: Move to separate utility file someday
|
|
338
|
+
- (NSDictionary *)callInfo:(TVOCall *)call {
|
|
339
|
+
NSMutableDictionary *callInfo = [@{kTwilioVoiceReactNativeCallInfoIsMuted: @(call.isMuted),
|
|
340
|
+
kTwilioVoiceReactNativeCallInfoIsOnHold: @(call.isOnHold),
|
|
341
|
+
kTwilioVoiceReactNativeCallInfoSid: call.sid,
|
|
342
|
+
kTwilioVoiceReactNativeCallInfoState: [self stringOfState:call.state]} mutableCopy];
|
|
343
|
+
|
|
344
|
+
if (call.uuid != nil) {
|
|
345
|
+
callInfo[kTwilioVoiceReactNativeCallInfoUuid] = call.uuid.UUIDString;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (call.from != nil) {
|
|
349
|
+
callInfo[kTwilioVoiceReactNativeCallInfoFrom] = call.from;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (call.to != nil) {
|
|
353
|
+
callInfo[kTwilioVoiceReactNativeCallInfoTo] = call.to;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
NSString *initialConnectedTimestamp = self.callConnectMap[call.uuid.UUIDString];
|
|
357
|
+
if (initialConnectedTimestamp != nil) {
|
|
358
|
+
callInfo[kTwilioVoiceReactNativeCallInfoInitialConnectedTimestamp] = initialConnectedTimestamp;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
TVOCallInvite *callInvite = self.callInviteMap[call.uuid.UUIDString];
|
|
362
|
+
if (callInvite && callInvite.customParameters) {
|
|
363
|
+
callInfo[kTwilioVoiceReactNativeCallInviteInfoCustomParameters] = [callInvite.customParameters copy];
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return callInfo;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
- (NSDictionary *)callInviteInfo:(TVOCallInvite *)callInvite {
|
|
370
|
+
NSMutableDictionary *callInviteInfo = [@{kTwilioVoiceReactNativeCallInviteInfoUuid: callInvite.uuid.UUIDString,
|
|
371
|
+
kTwilioVoiceReactNativeCallInviteInfoCallSid: callInvite.callSid,
|
|
372
|
+
kTwilioVoiceReactNativeCallInviteInfoTo: callInvite.to} mutableCopy];
|
|
373
|
+
|
|
374
|
+
if (callInvite.from != nil) {
|
|
375
|
+
callInviteInfo[kTwilioVoiceReactNativeCallInviteInfoFrom] = callInvite.from;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (callInvite.customParameters != nil) {
|
|
379
|
+
callInviteInfo[kTwilioVoiceReactNativeCallInviteInfoCustomParameters] = [callInvite.customParameters copy];
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return callInviteInfo;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
- (NSDictionary *)cancelledCallInviteInfo:(TVOCancelledCallInvite *)cancelledCallInvite {
|
|
386
|
+
NSMutableDictionary *cancelledCallInviteInfo = [@{
|
|
387
|
+
kTwilioVoiceReactNativeCancelledCallInviteInfoCallSid: cancelledCallInvite.callSid,
|
|
388
|
+
kTwilioVoiceReactNativeCancelledCallInviteInfoTo: cancelledCallInvite.to
|
|
389
|
+
} mutableCopy];
|
|
390
|
+
|
|
391
|
+
if (cancelledCallInvite.from != nil) {
|
|
392
|
+
cancelledCallInviteInfo[kTwilioVoiceReactNativeCancelledCallInviteInfoFrom] = cancelledCallInvite.from;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (cancelledCallInvite.customParameters != nil) {
|
|
396
|
+
cancelledCallInviteInfo[kTwilioVoiceReactNativeCancelledCallInviteInfoCustomParameters] = [cancelledCallInvite.customParameters copy];
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
return cancelledCallInviteInfo;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
RCT_EXPORT_MODULE();
|
|
403
|
+
|
|
404
|
+
#pragma mark - React Native
|
|
405
|
+
|
|
406
|
+
- (NSArray<NSString *> *)supportedEvents
|
|
407
|
+
{
|
|
408
|
+
return @[kTwilioVoiceReactNativeScopeVoice, kTwilioVoiceReactNativeScopeCall, kTwilioVoiceReactNativeScopeCallInvite, kTwilioVoiceReactNativeScopeCallMessage];
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
+ (BOOL)requiresMainQueueSetup
|
|
412
|
+
{
|
|
413
|
+
return YES;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
- (void)startObserving {
|
|
417
|
+
NSLog(@"Started observing");
|
|
418
|
+
_hasObserver = YES;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
- (void)stopObserving {
|
|
422
|
+
NSLog(@"Stopped observing");
|
|
423
|
+
_hasObserver = NO;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
- (void)sendEventWithName:(NSString *)eventName body:(id)body {
|
|
427
|
+
if (_hasObserver) {
|
|
428
|
+
[super sendEventWithName:eventName body:body];
|
|
429
|
+
} else {
|
|
430
|
+
NSLog(@"No event observer registered yet. Omitting event: %@, event body: %@", eventName, body);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
#pragma mark - Bingings (Voice methods)
|
|
435
|
+
|
|
436
|
+
RCT_EXPORT_METHOD(voice_getVersion:(RCTPromiseResolveBlock)resolve
|
|
437
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
438
|
+
{
|
|
439
|
+
resolve(TwilioVoiceSDK.sdkVersion);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
RCT_EXPORT_METHOD(voice_getDeviceToken:(RCTPromiseResolveBlock)resolve
|
|
443
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
444
|
+
{
|
|
445
|
+
if (self.deviceTokenData) {
|
|
446
|
+
const char *tokenBytes = (const char *)[self.deviceTokenData bytes];
|
|
447
|
+
NSMutableString *deviceTokenString = [NSMutableString string];
|
|
448
|
+
for (NSUInteger i = 0; i < [self.deviceTokenData length]; ++i) {
|
|
449
|
+
[deviceTokenString appendFormat:@"%02.2hhx", tokenBytes[i]];
|
|
450
|
+
}
|
|
451
|
+
resolve(deviceTokenString);
|
|
452
|
+
} else {
|
|
453
|
+
resolve(@"");
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
RCT_EXPORT_METHOD(voice_initializePushRegistry:(RCTPromiseResolveBlock)resolve
|
|
458
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
459
|
+
{
|
|
460
|
+
[self initializePushRegistry];
|
|
461
|
+
resolve(nil);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
RCT_EXPORT_METHOD(voice_setCallKitConfiguration:(NSDictionary *)configuration
|
|
465
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
466
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
467
|
+
{
|
|
468
|
+
[self initializeCallKitWithConfiguration:configuration];
|
|
469
|
+
resolve(nil);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
RCT_EXPORT_METHOD(voice_register:(NSString *)accessToken
|
|
473
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
474
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
475
|
+
{
|
|
476
|
+
#if TARGET_IPHONE_SIMULATOR
|
|
477
|
+
if (!self.deviceTokenData) {
|
|
478
|
+
NSLog(@"Please note that PushKit and incoming call are not supported on simulators");
|
|
479
|
+
NSString *testDeviceToken = @"deadbeefdeadbeefdeadbeefdeadbeef";
|
|
480
|
+
self.deviceTokenData = [testDeviceToken dataUsingEncoding:NSUTF8StringEncoding];
|
|
481
|
+
}
|
|
482
|
+
#endif
|
|
483
|
+
|
|
484
|
+
if (self.registrationInProgress) {
|
|
485
|
+
reject(kTwilioVoiceReactNativeVoiceError, @"Registration in progress. Please try again later", nil);
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
self.registrationInProgress = YES;
|
|
490
|
+
|
|
491
|
+
[self asyncPushRegistryInitialization:kPushRegistryDeviceTokenRetryTimeout
|
|
492
|
+
completion:^(NSData *deviceTokenData) {
|
|
493
|
+
if (deviceTokenData) {
|
|
494
|
+
[TwilioVoiceSDK registerWithAccessToken:accessToken
|
|
495
|
+
deviceToken:deviceTokenData
|
|
496
|
+
completion:^(NSError *error) {
|
|
497
|
+
self.registrationInProgress = NO;
|
|
498
|
+
if (error) {
|
|
499
|
+
NSString *errorMessage = [NSString stringWithFormat:@"Failed to register: %@", error];
|
|
500
|
+
NSLog(@"%@", errorMessage);
|
|
501
|
+
[self sendEventWithName:kTwilioVoiceReactNativeScopeVoice
|
|
502
|
+
body:@{kTwilioVoiceReactNativeVoiceEventType: kTwilioVoiceReactNativeVoiceEventError,
|
|
503
|
+
kTwilioVoiceReactNativeVoiceErrorKeyError: @{kTwilioVoiceReactNativeVoiceErrorKeyCode: @(error.code),
|
|
504
|
+
kTwilioVoiceReactNativeVoiceErrorKeyMessage: [error localizedDescription]}}];
|
|
505
|
+
reject(kTwilioVoiceReactNativeVoiceError, errorMessage, nil);
|
|
506
|
+
} else {
|
|
507
|
+
[self sendEventWithName:kTwilioVoiceReactNativeScopeVoice
|
|
508
|
+
body:@{kTwilioVoiceReactNativeVoiceEventType: kTwilioVoiceReactNativeVoiceEventRegistered}];
|
|
509
|
+
resolve(nil);
|
|
510
|
+
}
|
|
511
|
+
}];
|
|
512
|
+
} else {
|
|
513
|
+
self.registrationInProgress = NO;
|
|
514
|
+
reject(kTwilioVoiceReactNativeVoiceError, @"Failed to initialize PushKit device token", nil);
|
|
515
|
+
}
|
|
516
|
+
}];
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
- (void)asyncPushRegistryInitialization:(dispatch_time_t)timeout
|
|
520
|
+
completion:(void(^)(NSData *deviceTokenData))completion {
|
|
521
|
+
if (self.deviceTokenData) {
|
|
522
|
+
completion(self.deviceTokenData);
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
if (timeout > 0) {
|
|
527
|
+
__block dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC));
|
|
528
|
+
__weak typeof(self) weakSelf = self;
|
|
529
|
+
dispatch_after(delay, dispatch_get_main_queue(), ^{
|
|
530
|
+
dispatch_time_t backoffTimeout = (timeout > kExponentialBackoff)? timeout - kExponentialBackoff : 0;
|
|
531
|
+
[weakSelf asyncPushRegistryInitialization:backoffTimeout completion:completion];
|
|
532
|
+
});
|
|
533
|
+
} else {
|
|
534
|
+
// Device token could be nil. The voice_register() method will handle the promise.
|
|
535
|
+
completion(self.deviceTokenData);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
RCT_EXPORT_METHOD(voice_unregister:(NSString *)accessToken
|
|
540
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
541
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
542
|
+
{
|
|
543
|
+
#if TARGET_IPHONE_SIMULATOR
|
|
544
|
+
if (!self.deviceTokenData) {
|
|
545
|
+
NSLog(@"Please note that PushKit and incoming call are not supported on simulators");
|
|
546
|
+
NSString *testDeviceToken = @"deadbeefdeadbeefdeadbeefdeadbeef";
|
|
547
|
+
self.deviceTokenData = [testDeviceToken dataUsingEncoding:NSUTF8StringEncoding];
|
|
548
|
+
}
|
|
549
|
+
#endif
|
|
550
|
+
|
|
551
|
+
if (self.registrationInProgress) {
|
|
552
|
+
reject(kTwilioVoiceReactNativeVoiceError, @"Registration in progress. Please try again later", nil);
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
self.registrationInProgress = YES;
|
|
557
|
+
|
|
558
|
+
[self asyncPushRegistryInitialization:kPushRegistryDeviceTokenRetryTimeout
|
|
559
|
+
completion:^(NSData *deviceTokenData) {
|
|
560
|
+
if (deviceTokenData) {
|
|
561
|
+
[TwilioVoiceSDK unregisterWithAccessToken:accessToken
|
|
562
|
+
deviceToken:deviceTokenData
|
|
563
|
+
completion:^(NSError *error) {
|
|
564
|
+
self.registrationInProgress = NO;
|
|
565
|
+
if (error) {
|
|
566
|
+
NSString *errorMessage = [NSString stringWithFormat:@"Failed to unregister: %@", error];
|
|
567
|
+
NSLog(@"%@", errorMessage);
|
|
568
|
+
[self sendEventWithName:kTwilioVoiceReactNativeScopeVoice
|
|
569
|
+
body:@{kTwilioVoiceReactNativeVoiceEventType: kTwilioVoiceReactNativeVoiceEventError,
|
|
570
|
+
kTwilioVoiceReactNativeVoiceErrorKeyError: @{kTwilioVoiceReactNativeVoiceErrorKeyCode: @(error.code),
|
|
571
|
+
kTwilioVoiceReactNativeVoiceErrorKeyMessage: [error localizedDescription]}}];
|
|
572
|
+
reject(kTwilioVoiceReactNativeVoiceError, errorMessage, nil);
|
|
573
|
+
} else {
|
|
574
|
+
[self sendEventWithName:kTwilioVoiceReactNativeScopeVoice
|
|
575
|
+
body:@{kTwilioVoiceReactNativeVoiceEventType: kTwilioVoiceReactNativeVoiceEventUnregistered}];
|
|
576
|
+
resolve(nil);
|
|
577
|
+
}
|
|
578
|
+
}];
|
|
579
|
+
} else {
|
|
580
|
+
self.registrationInProgress = NO;
|
|
581
|
+
reject(kTwilioVoiceReactNativeVoiceError, @"Failed to initialize PushKit device token", nil);
|
|
582
|
+
}
|
|
583
|
+
}];
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
RCT_EXPORT_METHOD(voice_connect_ios:(NSString *)accessToken
|
|
587
|
+
params:(NSDictionary *)params
|
|
588
|
+
contactHandle:(NSString *)contactHandle
|
|
589
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
590
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
591
|
+
{
|
|
592
|
+
[self makeCallWithAccessToken:accessToken params:params contactHandle:contactHandle];
|
|
593
|
+
self.callPromiseResolver = resolve;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
RCT_EXPORT_METHOD(voice_getCalls:(RCTPromiseResolveBlock)resolve
|
|
597
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
598
|
+
{
|
|
599
|
+
NSMutableArray *callInfoArray = [NSMutableArray array];
|
|
600
|
+
for (NSString *uuid in [self.callMap allKeys]) {
|
|
601
|
+
TVOCall *call = self.callMap[uuid];
|
|
602
|
+
[callInfoArray addObject:[self callInfo:call]];
|
|
603
|
+
}
|
|
604
|
+
resolve(callInfoArray);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
RCT_EXPORT_METHOD(voice_getCallInvites:(RCTPromiseResolveBlock)resolve
|
|
608
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
609
|
+
{
|
|
610
|
+
NSMutableArray *callInviteInfoArray = [NSMutableArray array];
|
|
611
|
+
for (NSString *uuid in [self.callInviteMap allKeys]) {
|
|
612
|
+
TVOCallInvite *callInvite = self.callInviteMap[uuid];
|
|
613
|
+
[callInviteInfoArray addObject:[self callInviteInfo:callInvite]];
|
|
614
|
+
}
|
|
615
|
+
resolve(callInviteInfoArray);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
RCT_EXPORT_METHOD(voice_getAudioDevices:(RCTPromiseResolveBlock)resolve
|
|
619
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
620
|
+
{
|
|
621
|
+
NSMutableArray *nativeAudioDeviceInfos = [NSMutableArray array];
|
|
622
|
+
for (NSString *key in [self.audioDevices allKeys]) {
|
|
623
|
+
[nativeAudioDeviceInfos addObject:self.audioDevices[key]];
|
|
624
|
+
}
|
|
625
|
+
resolve(@{kTwilioVoiceReactNativeAudioDeviceKeyAudioDevices: nativeAudioDeviceInfos,
|
|
626
|
+
kTwilioVoiceReactNativeAudioDeviceKeySelectedDevice: self.selectedAudioDevice});
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
RCT_EXPORT_METHOD(voice_selectAudioDevice:(NSString *)uuid
|
|
630
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
631
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
632
|
+
{
|
|
633
|
+
if ([self selectAudioDevice:uuid]) {
|
|
634
|
+
resolve(nil);
|
|
635
|
+
} else {
|
|
636
|
+
reject(kTwilioVoiceReactNativeVoiceError, [NSString stringWithFormat:@"Failed to select audio device %@", uuid], nil);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
RCT_EXPORT_METHOD(voice_showNativeAvRoutePicker:(RCTPromiseResolveBlock)resolve
|
|
641
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
642
|
+
{
|
|
643
|
+
TVRNAVRoutePickerView *routePicker = [[TVRNAVRoutePickerView alloc] initWithFrame:CGRectZero];
|
|
644
|
+
|
|
645
|
+
UIWindow *window = [UIApplication sharedApplication].windows[0];
|
|
646
|
+
UIViewController *rootViewController = window.rootViewController;
|
|
647
|
+
if (rootViewController) {
|
|
648
|
+
UIViewController *topViewController = rootViewController;
|
|
649
|
+
while (topViewController.presentedViewController) {
|
|
650
|
+
topViewController = topViewController.presentedViewController;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
654
|
+
[topViewController.view addSubview:routePicker];
|
|
655
|
+
[routePicker present];
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
resolve(nil);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
#pragma mark - Bingings (Call)
|
|
663
|
+
|
|
664
|
+
RCT_EXPORT_METHOD(call_disconnect:(NSString *)uuid
|
|
665
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
666
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
667
|
+
{
|
|
668
|
+
if (self.callMap[uuid]) {
|
|
669
|
+
[self endCallWithUuid:[[NSUUID alloc] initWithUUIDString:uuid]];
|
|
670
|
+
resolve(nil);
|
|
671
|
+
} else {
|
|
672
|
+
reject(kTwilioVoiceReactNativeVoiceError, [NSString stringWithFormat:@"Call with %@ not found", uuid], nil);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
RCT_EXPORT_METHOD(call_getState:(NSString *)uuid
|
|
677
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
678
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
679
|
+
{
|
|
680
|
+
TVOCall *call = self.callMap[uuid];
|
|
681
|
+
NSString *state = @"";
|
|
682
|
+
if (call) {
|
|
683
|
+
state = [self stringOfState:call.state];
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
resolve(state);
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
RCT_EXPORT_METHOD(call_getSid:(NSString *)uuid
|
|
690
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
691
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
692
|
+
{
|
|
693
|
+
TVOCall *call = self.callMap[uuid];
|
|
694
|
+
NSString *callSid = (call && call.state != TVOCallStateConnecting)? call.sid : @"";
|
|
695
|
+
|
|
696
|
+
resolve(callSid);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
RCT_EXPORT_METHOD(call_getFrom:(NSString *)uuid
|
|
700
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
701
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
702
|
+
{
|
|
703
|
+
TVOCall *call = self.callMap[uuid];
|
|
704
|
+
NSString *from = (call && [call.from length] > 0)? call.from : @"";
|
|
705
|
+
|
|
706
|
+
resolve(from);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
RCT_EXPORT_METHOD(call_getTo:(NSString *)uuid
|
|
710
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
711
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
712
|
+
{
|
|
713
|
+
TVOCall *call = self.callMap[uuid];
|
|
714
|
+
NSString *to = (call && [call.to length] > 0)? call.to : @"";
|
|
715
|
+
|
|
716
|
+
resolve(to);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
RCT_EXPORT_METHOD(call_hold:(NSString *)uuid
|
|
720
|
+
onHold:(BOOL)onHold
|
|
721
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
722
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
723
|
+
{
|
|
724
|
+
TVOCall *call = self.callMap[uuid];
|
|
725
|
+
if (call) {
|
|
726
|
+
[call setOnHold:onHold];
|
|
727
|
+
resolve(@(call.isOnHold));
|
|
728
|
+
} else {
|
|
729
|
+
reject(kTwilioVoiceReactNativeVoiceError, [NSString stringWithFormat:@"Call with %@ not found", uuid], nil);
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
RCT_EXPORT_METHOD(call_isOnHold:(NSString *)uuid
|
|
734
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
735
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
736
|
+
{
|
|
737
|
+
TVOCall *call = self.callMap[uuid];
|
|
738
|
+
if (call) {
|
|
739
|
+
resolve(@(call.isOnHold));
|
|
740
|
+
} else {
|
|
741
|
+
resolve(@(NO));
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
RCT_EXPORT_METHOD(call_mute:(NSString *)uuid
|
|
746
|
+
muted:(BOOL)muted
|
|
747
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
748
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
749
|
+
{
|
|
750
|
+
TVOCall *call = self.callMap[uuid];
|
|
751
|
+
if (call) {
|
|
752
|
+
[call setMuted:muted];
|
|
753
|
+
resolve(@(call.isMuted));
|
|
754
|
+
} else {
|
|
755
|
+
reject(kTwilioVoiceReactNativeVoiceError, [NSString stringWithFormat:@"Call with %@ not found", uuid], nil);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
RCT_EXPORT_METHOD(call_isMuted:(NSString *)uuid
|
|
760
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
761
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
762
|
+
{
|
|
763
|
+
TVOCall *call = self.callMap[uuid];
|
|
764
|
+
if (call) {
|
|
765
|
+
resolve(@(call.isMuted));
|
|
766
|
+
} else {
|
|
767
|
+
resolve(@(NO));
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
RCT_EXPORT_METHOD(call_sendDigits:(NSString *)uuid
|
|
772
|
+
digits:(NSString *)digits
|
|
773
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
774
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
775
|
+
{
|
|
776
|
+
TVOCall *call = self.callMap[uuid];
|
|
777
|
+
if (call) {
|
|
778
|
+
[call sendDigits:digits];
|
|
779
|
+
resolve(nil);
|
|
780
|
+
} else {
|
|
781
|
+
reject(kTwilioVoiceReactNativeVoiceError, [NSString stringWithFormat:@"Call with %@ not found", uuid], nil);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
RCT_EXPORT_METHOD(call_postFeedback:(NSString *)uuid
|
|
786
|
+
score:(NSString *)score
|
|
787
|
+
issue:(NSString *)issue
|
|
788
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
789
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
790
|
+
{
|
|
791
|
+
TVOCall *call = self.callMap[uuid];
|
|
792
|
+
if (call) {
|
|
793
|
+
[call postFeedback:[self scoreFromString:score] issue:[self issueFromString:issue]];
|
|
794
|
+
resolve(nil);
|
|
795
|
+
} else {
|
|
796
|
+
reject(kTwilioVoiceReactNativeVoiceError, [NSString stringWithFormat:@"Call with %@ not found", uuid], nil);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
RCT_EXPORT_METHOD(call_getStats:(NSString *)uuid
|
|
801
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
802
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
803
|
+
{
|
|
804
|
+
TVOCall *call = self.callMap[uuid];
|
|
805
|
+
if (call) {
|
|
806
|
+
[call getStatsWithBlock:^(NSArray<TVOStatsReport *> *statsReports) {
|
|
807
|
+
NSAssert([statsReports count] >= 1, @"Invalid stats reports array size");
|
|
808
|
+
NSArray *statsReportJson = [TwilioVoiceStatsReport jsonWithStatsReportsArray:statsReports];
|
|
809
|
+
resolve(statsReportJson);
|
|
810
|
+
}];
|
|
811
|
+
} else {
|
|
812
|
+
reject(kTwilioVoiceReactNativeVoiceError, [NSString stringWithFormat:@"Call with %@ not found", uuid], nil);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
RCT_EXPORT_METHOD(call_sendMessage:(NSString *)uuid
|
|
817
|
+
content:(NSString *)content
|
|
818
|
+
contentType:(NSString *)contentType
|
|
819
|
+
messageType:(NSString *)messageType
|
|
820
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
821
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
822
|
+
{
|
|
823
|
+
TVOCallInvite *callInvite = self.callInviteMap[uuid];
|
|
824
|
+
TVOCall *call = self.callMap[uuid];
|
|
825
|
+
if (call) {
|
|
826
|
+
TVOCallMessage *callMessage = [TVOCallMessage messageWithContent:content
|
|
827
|
+
messageType:messageType
|
|
828
|
+
block:^(TVOCallMessageBuilder *builder) {
|
|
829
|
+
builder.contentType = contentType;
|
|
830
|
+
}];
|
|
831
|
+
NSString *voiceEventSid = [call sendMessage:callMessage];
|
|
832
|
+
resolve(voiceEventSid);
|
|
833
|
+
} else if (callInvite) {
|
|
834
|
+
TVOCallMessage *callMessage = [TVOCallMessage messageWithContent:content
|
|
835
|
+
messageType:messageType
|
|
836
|
+
block:^(TVOCallMessageBuilder *builder) {
|
|
837
|
+
builder.contentType = contentType;
|
|
838
|
+
}];
|
|
839
|
+
NSString *voiceEventSid = [callInvite sendMessage:callMessage];
|
|
840
|
+
resolve(voiceEventSid);
|
|
841
|
+
} else {
|
|
842
|
+
reject(kTwilioVoiceReactNativeVoiceError, [NSString stringWithFormat:@"Call with %@ not found", uuid], nil);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
#pragma mark - Bingings (Call Invite)
|
|
847
|
+
|
|
848
|
+
RCT_EXPORT_METHOD(callInvite_accept:(NSString *)callInviteUuid
|
|
849
|
+
acceptOptions:(NSDictionary *)acceptOptions
|
|
850
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
851
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
852
|
+
{
|
|
853
|
+
[self answerCallInvite:[[NSUUID alloc] initWithUUIDString:callInviteUuid]
|
|
854
|
+
completion:^(BOOL success) {
|
|
855
|
+
if (success) {
|
|
856
|
+
BOOL found = NO;
|
|
857
|
+
for (NSString *uuidKey in [self.callMap allKeys]) {
|
|
858
|
+
if ([uuidKey isEqualToString:callInviteUuid]) {
|
|
859
|
+
found = YES;
|
|
860
|
+
TVOCall *call = self.callMap[uuidKey];
|
|
861
|
+
resolve([self callInfo:call]);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
if (!found) {
|
|
866
|
+
reject(kTwilioVoiceReactNativeVoiceError, @"No matching call", nil);
|
|
867
|
+
}
|
|
868
|
+
} else {
|
|
869
|
+
reject(kTwilioVoiceReactNativeVoiceError, @"Failed to answer the call invite", nil);
|
|
870
|
+
}
|
|
871
|
+
}];
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
RCT_EXPORT_METHOD(callInvite_reject:(NSString *)callInviteUuid
|
|
875
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
876
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
877
|
+
{
|
|
878
|
+
[self endCallWithUuid:[[NSUUID alloc] initWithUUIDString:callInviteUuid]];
|
|
879
|
+
resolve(nil);
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
RCT_EXPORT_METHOD(callInvite_isValid:(NSString *)callInviteUuid
|
|
883
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
884
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
885
|
+
{
|
|
886
|
+
resolve(@(YES));
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
RCT_EXPORT_METHOD(callInvite_getCallSid:(NSString *)callInviteUuid
|
|
890
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
891
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
892
|
+
{
|
|
893
|
+
if (self.callInviteMap[callInviteUuid]) {
|
|
894
|
+
TVOCallInvite *callInvite = self.callInviteMap[callInviteUuid];
|
|
895
|
+
resolve(callInvite.callSid);
|
|
896
|
+
} else {
|
|
897
|
+
reject(kTwilioVoiceReactNativeVoiceError, @"No matching call invite", nil);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
RCT_EXPORT_METHOD(callInvite_getFrom:(NSString *)callInviteUuid
|
|
902
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
903
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
904
|
+
{
|
|
905
|
+
if (self.callInviteMap[callInviteUuid]) {
|
|
906
|
+
TVOCallInvite *callInvite = self.callInviteMap[callInviteUuid];
|
|
907
|
+
resolve(callInvite.from);
|
|
908
|
+
} else {
|
|
909
|
+
reject(kTwilioVoiceReactNativeVoiceError, @"No matching call invite", nil);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
RCT_EXPORT_METHOD(callInvite_getTo:(NSString *)callInviteUuid
|
|
914
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
915
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
916
|
+
{
|
|
917
|
+
if (self.callInviteMap[callInviteUuid]) {
|
|
918
|
+
TVOCallInvite *callInvite = self.callInviteMap[callInviteUuid];
|
|
919
|
+
resolve(callInvite.to);
|
|
920
|
+
} else {
|
|
921
|
+
reject(kTwilioVoiceReactNativeVoiceError, @"No matching call invite", nil);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
RCT_EXPORT_METHOD(callInvite_updateCallerHandle:(NSString *)callInviteUuid
|
|
926
|
+
handle:(NSString *)handle
|
|
927
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
928
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
929
|
+
{
|
|
930
|
+
if (self.callInviteMap[callInviteUuid]) {
|
|
931
|
+
[self updateCall:callInviteUuid callerHandle:handle];
|
|
932
|
+
resolve(nil);
|
|
933
|
+
} else {
|
|
934
|
+
reject(kTwilioVoiceReactNativeVoiceError, @"No matching call invite", nil);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
RCT_EXPORT_METHOD(cancelledCallInvite_getCallSid:(NSString *)cancelledCallInviteUuid
|
|
939
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
940
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
941
|
+
{
|
|
942
|
+
if (self.cancelledCallInviteMap[cancelledCallInviteUuid]) {
|
|
943
|
+
TVOCancelledCallInvite *cancelledCallInvite = self.cancelledCallInviteMap[cancelledCallInviteUuid];
|
|
944
|
+
resolve(cancelledCallInvite.callSid);
|
|
945
|
+
} else {
|
|
946
|
+
reject(kTwilioVoiceReactNativeVoiceError, @"No matching cancelled call invite", nil);
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
RCT_EXPORT_METHOD(cancelledCallInvite_getFrom:(NSString *)cancelledCallInviteUuid
|
|
951
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
952
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
953
|
+
{
|
|
954
|
+
if (self.cancelledCallInviteMap[cancelledCallInviteUuid]) {
|
|
955
|
+
TVOCancelledCallInvite *cancelledCallInvite = self.cancelledCallInviteMap[cancelledCallInviteUuid];
|
|
956
|
+
resolve(cancelledCallInvite.from);
|
|
957
|
+
} else {
|
|
958
|
+
reject(kTwilioVoiceReactNativeVoiceError, @"No matching cancelled call invite", nil);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
RCT_EXPORT_METHOD(cancelledCallInvite_getTo:(NSString *)cancelledCallInviteUuid
|
|
963
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
964
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
965
|
+
{
|
|
966
|
+
if (self.cancelledCallInviteMap[cancelledCallInviteUuid]) {
|
|
967
|
+
TVOCancelledCallInvite *cancelledCallInvite = self.cancelledCallInviteMap[cancelledCallInviteUuid];
|
|
968
|
+
resolve(cancelledCallInvite.to);
|
|
969
|
+
} else {
|
|
970
|
+
reject(kTwilioVoiceReactNativeVoiceError, @"No matching cancelled call invite", nil);
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
#pragma mark - utility
|
|
975
|
+
|
|
976
|
+
RCT_EXPORT_METHOD(util_generateId:(RCTPromiseResolveBlock)resolve
|
|
977
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
978
|
+
{
|
|
979
|
+
resolve([NSUUID UUID].UUIDString);
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
RCT_EXPORT_METHOD(voice_setIncomingCallContactHandleTemplate:(NSString *)template
|
|
983
|
+
resolver:(RCTPromiseResolveBlock)resolve
|
|
984
|
+
rejecter:(RCTPromiseRejectBlock)reject)
|
|
985
|
+
{
|
|
986
|
+
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];
|
|
987
|
+
NSString *preferenceKey = @"incomingCallContactHandleTemplate";
|
|
988
|
+
[preferences setObject:template forKey:preferenceKey];
|
|
989
|
+
[preferences synchronize];
|
|
990
|
+
resolve(NULL);
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
- (NSString *)stringOfState:(TVOCallState)state {
|
|
994
|
+
switch (state) {
|
|
995
|
+
case TVOCallStateConnecting:
|
|
996
|
+
return kTwilioVoiceReactNativeCallStateConnecting;
|
|
997
|
+
case TVOCallStateRinging:
|
|
998
|
+
return kTwilioVoiceReactNativeCallStateRinging;
|
|
999
|
+
case TVOCallStateConnected:
|
|
1000
|
+
return kTwilioVoiceReactNativeCallStateConnected;
|
|
1001
|
+
case TVOCallStateReconnecting:
|
|
1002
|
+
return kTwilioVoiceReactNativeCallStateReconnecting;
|
|
1003
|
+
case TVOCallStateDisconnected:
|
|
1004
|
+
return kTwilioVoiceReactNativeCallStateDisconnected;
|
|
1005
|
+
default:
|
|
1006
|
+
return kTwilioVoiceReactNativeCallStateConnecting;
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
- (TVOCallFeedbackScore)scoreFromString:(NSString *)score {
|
|
1011
|
+
if ([score isEqualToString:kTwilioVoiceReactNativeCallFeedbackScoreNotReported]) {
|
|
1012
|
+
return TVOCallFeedbackScoreNotReported;
|
|
1013
|
+
} else if ([score isEqualToString:kTwilioVoiceReactNativeCallFeedbackScoreOne]) {
|
|
1014
|
+
return TVOCallFeedbackScoreOnePoint;
|
|
1015
|
+
} else if ([score isEqualToString:kTwilioVoiceReactNativeCallFeedbackScoreTwo]) {
|
|
1016
|
+
return TVOCallFeedbackScoreTwoPoints;
|
|
1017
|
+
} else if ([score isEqualToString:kTwilioVoiceReactNativeCallFeedbackScoreThree]) {
|
|
1018
|
+
return TVOCallFeedbackScoreThreePoints;
|
|
1019
|
+
} else if ([score isEqualToString:kTwilioVoiceReactNativeCallFeedbackScoreFour]) {
|
|
1020
|
+
return TVOCallFeedbackScoreFourPoints;
|
|
1021
|
+
} else if ([score isEqualToString:kTwilioVoiceReactNativeCallFeedbackScoreFive]) {
|
|
1022
|
+
return TVOCallFeedbackScoreFivePoints;
|
|
1023
|
+
}
|
|
1024
|
+
return TVOCallFeedbackScoreNotReported;
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
- (TVOCallFeedbackIssue)issueFromString:(NSString *)issue {
|
|
1028
|
+
if ([issue isEqualToString:kTwilioVoiceReactNativeCallFeedbackIssueNotReported]) {
|
|
1029
|
+
return TVOCallFeedbackIssueNotReported;
|
|
1030
|
+
} else if ([issue isEqualToString:kTwilioVoiceReactNativeCallFeedbackIssueDroppedCall]) {
|
|
1031
|
+
return TVOCallFeedbackIssueDroppedCall;
|
|
1032
|
+
} else if ([issue isEqualToString:kTwilioVoiceReactNativeCallFeedbackIssueAudioLatency]) {
|
|
1033
|
+
return TVOCallFeedbackIssueAudioLatency;
|
|
1034
|
+
} else if ([issue isEqualToString:kTwilioVoiceReactNativeCallFeedbackIssueOneWayAudio]) {
|
|
1035
|
+
return TVOCallFeedbackIssueOneWayAudio;
|
|
1036
|
+
} else if ([issue isEqualToString:kTwilioVoiceReactNativeCallFeedbackIssueChoppyAudio]) {
|
|
1037
|
+
return TVOCallFeedbackIssueChoppyAudio;
|
|
1038
|
+
} else if ([issue isEqualToString:kTwilioVoiceReactNativeCallFeedbackIssueNoisyCall]) {
|
|
1039
|
+
return TVOCallFeedbackIssueNoisyCall;
|
|
1040
|
+
} else if ([issue isEqualToString:kTwilioVoiceReactNativeCallFeedbackIssueEcho]) {
|
|
1041
|
+
return TVOCallFeedbackIssueEcho;
|
|
1042
|
+
}
|
|
1043
|
+
return TVOCallFeedbackIssueNotReported;
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
@end
|