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.
Files changed (227) hide show
  1. package/LICENSE +206 -0
  2. package/README.md +179 -0
  3. package/android/build.gradle +113 -0
  4. package/android/gradle.properties +29 -0
  5. package/android/src/main/AndroidManifest.xml +30 -0
  6. package/android/src/main/java/com/twiliovoicereactnative/AudioSwitchManager.java +141 -0
  7. package/android/src/main/java/com/twiliovoicereactnative/CallListenerProxy.java +186 -0
  8. package/android/src/main/java/com/twiliovoicereactnative/CallMessageListenerProxy.java +103 -0
  9. package/android/src/main/java/com/twiliovoicereactnative/CallRecordDatabase.java +183 -0
  10. package/android/src/main/java/com/twiliovoicereactnative/CommonConstants.java +195 -0
  11. package/android/src/main/java/com/twiliovoicereactnative/ConfigurationProperties.java +41 -0
  12. package/android/src/main/java/com/twiliovoicereactnative/Constants.java +23 -0
  13. package/android/src/main/java/com/twiliovoicereactnative/ExpoActivityLifecycleListener.java +27 -0
  14. package/android/src/main/java/com/twiliovoicereactnative/ExpoApplicationLifecycleListener.java +14 -0
  15. package/android/src/main/java/com/twiliovoicereactnative/ExpoModule.kt +36 -0
  16. package/android/src/main/java/com/twiliovoicereactnative/ExpoPackage.java +20 -0
  17. package/android/src/main/java/com/twiliovoicereactnative/JSEventEmitter.java +98 -0
  18. package/android/src/main/java/com/twiliovoicereactnative/MediaPlayerManager.java +58 -0
  19. package/android/src/main/java/com/twiliovoicereactnative/NotificationUtility.java +363 -0
  20. package/android/src/main/java/com/twiliovoicereactnative/ReactNativeArgumentsSerializer.java +276 -0
  21. package/android/src/main/java/com/twiliovoicereactnative/SDKLog.java +74 -0
  22. package/android/src/main/java/com/twiliovoicereactnative/StatsListenerProxy.java +178 -0
  23. package/android/src/main/java/com/twiliovoicereactnative/TwilioVoiceReactNativeModule.java +778 -0
  24. package/android/src/main/java/com/twiliovoicereactnative/TwilioVoiceReactNativePackage.java +28 -0
  25. package/android/src/main/java/com/twiliovoicereactnative/VoiceActivityProxy.java +101 -0
  26. package/android/src/main/java/com/twiliovoicereactnative/VoiceApplicationProxy.java +133 -0
  27. package/android/src/main/java/com/twiliovoicereactnative/VoiceFirebaseMessagingService.java +88 -0
  28. package/android/src/main/java/com/twiliovoicereactnative/VoiceService.java +403 -0
  29. package/android/src/main/res/drawable/answered_call_small_icon.png +0 -0
  30. package/android/src/main/res/drawable/ic_launcher_round.png +0 -0
  31. package/android/src/main/res/drawable/ic_launcher_sdk.png +0 -0
  32. package/android/src/main/res/drawable/incoming_call_small_icon.png +0 -0
  33. package/android/src/main/res/drawable/outgoing_call_small_icon.png +0 -0
  34. package/android/src/main/res/raw/disconnect.wav +0 -0
  35. package/android/src/main/res/raw/incoming.wav +0 -0
  36. package/android/src/main/res/raw/outgoing.wav +0 -0
  37. package/android/src/main/res/raw/ringtone.wav +0 -0
  38. package/android/src/main/res/raw/silent.wav +0 -0
  39. package/android/src/main/res/values/colors.xml +11 -0
  40. package/android/src/main/res/values/config.xml +5 -0
  41. package/android/src/main/res/values/dimens.xml +7 -0
  42. package/android/src/main/res/values/strings.xml +25 -0
  43. package/android/src/main/res/values/styles.xml +10 -0
  44. package/android/src/main/res/values-night/colors.xml +11 -0
  45. package/expo-config-plugin/android.js +191 -0
  46. package/expo-config-plugin/ios.js +22 -0
  47. package/expo-module.config.json +6 -0
  48. package/ios/TwilioVoicePushRegistry.h +19 -0
  49. package/ios/TwilioVoicePushRegistry.m +72 -0
  50. package/ios/TwilioVoiceReactNative+CallInvite.m +56 -0
  51. package/ios/TwilioVoiceReactNative+CallKit.m +533 -0
  52. package/ios/TwilioVoiceReactNative+CallMessage.m +81 -0
  53. package/ios/TwilioVoiceReactNative.h +74 -0
  54. package/ios/TwilioVoiceReactNative.m +1046 -0
  55. package/ios/TwilioVoiceReactNative.xcodeproj/project.pbxproj +328 -0
  56. package/ios/TwilioVoiceReactNativeConstants.h +200 -0
  57. package/ios/Utilities/TwilioVoiceStatsReport.h +175 -0
  58. package/lib/commonjs/AudioDevice.js +101 -0
  59. package/lib/commonjs/AudioDevice.js.map +1 -0
  60. package/lib/commonjs/Call.js +765 -0
  61. package/lib/commonjs/Call.js.map +1 -0
  62. package/lib/commonjs/CallInvite.js +452 -0
  63. package/lib/commonjs/CallInvite.js.map +1 -0
  64. package/lib/commonjs/CallMessage/CallMessage.js +54 -0
  65. package/lib/commonjs/CallMessage/CallMessage.js.map +1 -0
  66. package/lib/commonjs/CallMessage/IncomingCallMessage.js +120 -0
  67. package/lib/commonjs/CallMessage/IncomingCallMessage.js.map +1 -0
  68. package/lib/commonjs/CallMessage/OutgoingCallMessage.js +148 -0
  69. package/lib/commonjs/CallMessage/OutgoingCallMessage.js.map +1 -0
  70. package/lib/commonjs/ExpoModule.js +69 -0
  71. package/lib/commonjs/ExpoModule.js.map +1 -0
  72. package/lib/commonjs/Voice.js +611 -0
  73. package/lib/commonjs/Voice.js.map +1 -0
  74. package/lib/commonjs/common.js +26 -0
  75. package/lib/commonjs/common.js.map +1 -0
  76. package/lib/commonjs/constants.js +158 -0
  77. package/lib/commonjs/constants.js.map +1 -0
  78. package/lib/commonjs/error/InvalidArgumentError.js +32 -0
  79. package/lib/commonjs/error/InvalidArgumentError.js.map +1 -0
  80. package/lib/commonjs/error/InvalidStateError.js +33 -0
  81. package/lib/commonjs/error/InvalidStateError.js.map +1 -0
  82. package/lib/commonjs/error/TwilioError.js +38 -0
  83. package/lib/commonjs/error/TwilioError.js.map +1 -0
  84. package/lib/commonjs/error/UnsupportedPlatformError.js +33 -0
  85. package/lib/commonjs/error/UnsupportedPlatformError.js.map +1 -0
  86. package/lib/commonjs/error/generated.js +2343 -0
  87. package/lib/commonjs/error/generated.js.map +1 -0
  88. package/lib/commonjs/error/index.js +112 -0
  89. package/lib/commonjs/error/index.js.map +1 -0
  90. package/lib/commonjs/error/utility.js +37 -0
  91. package/lib/commonjs/error/utility.js.map +1 -0
  92. package/lib/commonjs/index.js +95 -0
  93. package/lib/commonjs/index.js.map +1 -0
  94. package/lib/commonjs/type/AudioDevice.js +6 -0
  95. package/lib/commonjs/type/AudioDevice.js.map +1 -0
  96. package/lib/commonjs/type/Call.js +6 -0
  97. package/lib/commonjs/type/Call.js.map +1 -0
  98. package/lib/commonjs/type/CallInvite.js +6 -0
  99. package/lib/commonjs/type/CallInvite.js.map +1 -0
  100. package/lib/commonjs/type/CallKit.js +30 -0
  101. package/lib/commonjs/type/CallKit.js.map +1 -0
  102. package/lib/commonjs/type/CallMessage.js +6 -0
  103. package/lib/commonjs/type/CallMessage.js.map +1 -0
  104. package/lib/commonjs/type/Error.js +6 -0
  105. package/lib/commonjs/type/Error.js.map +1 -0
  106. package/lib/commonjs/type/NativeModule.js +6 -0
  107. package/lib/commonjs/type/NativeModule.js.map +1 -0
  108. package/lib/commonjs/type/RTCStats.js +29 -0
  109. package/lib/commonjs/type/RTCStats.js.map +1 -0
  110. package/lib/commonjs/type/Voice.js +6 -0
  111. package/lib/commonjs/type/Voice.js.map +1 -0
  112. package/lib/commonjs/type/common.js +2 -0
  113. package/lib/commonjs/type/common.js.map +1 -0
  114. package/lib/module/AudioDevice.js +97 -0
  115. package/lib/module/AudioDevice.js.map +1 -0
  116. package/lib/module/Call.js +766 -0
  117. package/lib/module/Call.js.map +1 -0
  118. package/lib/module/CallInvite.js +450 -0
  119. package/lib/module/CallInvite.js.map +1 -0
  120. package/lib/module/CallMessage/CallMessage.js +51 -0
  121. package/lib/module/CallMessage/CallMessage.js.map +1 -0
  122. package/lib/module/CallMessage/IncomingCallMessage.js +116 -0
  123. package/lib/module/CallMessage/IncomingCallMessage.js.map +1 -0
  124. package/lib/module/CallMessage/OutgoingCallMessage.js +154 -0
  125. package/lib/module/CallMessage/OutgoingCallMessage.js.map +1 -0
  126. package/lib/module/ExpoModule.js +58 -0
  127. package/lib/module/ExpoModule.js.map +1 -0
  128. package/lib/module/Voice.js +598 -0
  129. package/lib/module/Voice.js.map +1 -0
  130. package/lib/module/common.js +11 -0
  131. package/lib/module/common.js.map +1 -0
  132. package/lib/module/constants.js +151 -0
  133. package/lib/module/constants.js.map +1 -0
  134. package/lib/module/error/InvalidArgumentError.js +23 -0
  135. package/lib/module/error/InvalidArgumentError.js.map +1 -0
  136. package/lib/module/error/InvalidStateError.js +24 -0
  137. package/lib/module/error/InvalidStateError.js.map +1 -0
  138. package/lib/module/error/TwilioError.js +29 -0
  139. package/lib/module/error/TwilioError.js.map +1 -0
  140. package/lib/module/error/UnsupportedPlatformError.js +24 -0
  141. package/lib/module/error/UnsupportedPlatformError.js.map +1 -0
  142. package/lib/module/error/generated.js +2315 -0
  143. package/lib/module/error/generated.js.map +1 -0
  144. package/lib/module/error/index.js +6 -0
  145. package/lib/module/error/index.js.map +1 -0
  146. package/lib/module/error/utility.js +27 -0
  147. package/lib/module/error/utility.js.map +1 -0
  148. package/lib/module/index.js +23 -0
  149. package/lib/module/index.js.map +1 -0
  150. package/lib/module/type/AudioDevice.js +2 -0
  151. package/lib/module/type/AudioDevice.js.map +1 -0
  152. package/lib/module/type/Call.js +2 -0
  153. package/lib/module/type/Call.js.map +1 -0
  154. package/lib/module/type/CallInvite.js +2 -0
  155. package/lib/module/type/CallInvite.js.map +1 -0
  156. package/lib/module/type/CallKit.js +22 -0
  157. package/lib/module/type/CallKit.js.map +1 -0
  158. package/lib/module/type/CallMessage.js +2 -0
  159. package/lib/module/type/CallMessage.js.map +1 -0
  160. package/lib/module/type/Error.js +2 -0
  161. package/lib/module/type/Error.js.map +1 -0
  162. package/lib/module/type/NativeModule.js +2 -0
  163. package/lib/module/type/NativeModule.js.map +1 -0
  164. package/lib/module/type/RTCStats.js +21 -0
  165. package/lib/module/type/RTCStats.js.map +1 -0
  166. package/lib/module/type/Voice.js +2 -0
  167. package/lib/module/type/Voice.js.map +1 -0
  168. package/lib/module/type/common.js +2 -0
  169. package/lib/module/type/common.js.map +1 -0
  170. package/lib/typescript/AudioDevice.d.ts +74 -0
  171. package/lib/typescript/Call.d.ts +874 -0
  172. package/lib/typescript/CallInvite.d.ts +484 -0
  173. package/lib/typescript/CallMessage/CallMessage.d.ts +53 -0
  174. package/lib/typescript/CallMessage/IncomingCallMessage.d.ts +75 -0
  175. package/lib/typescript/CallMessage/OutgoingCallMessage.d.ts +198 -0
  176. package/lib/typescript/ExpoModule.d.ts +13 -0
  177. package/lib/typescript/Voice.d.ts +620 -0
  178. package/lib/typescript/common.d.ts +11 -0
  179. package/lib/typescript/constants.d.ts +148 -0
  180. package/lib/typescript/error/InvalidArgumentError.d.ts +11 -0
  181. package/lib/typescript/error/InvalidStateError.d.ts +12 -0
  182. package/lib/typescript/error/TwilioError.d.ts +14 -0
  183. package/lib/typescript/error/UnsupportedPlatformError.d.ts +12 -0
  184. package/lib/typescript/error/generated.d.ts +1558 -0
  185. package/lib/typescript/error/index.d.ts +5 -0
  186. package/lib/typescript/error/utility.d.ts +12 -0
  187. package/lib/typescript/index.d.ts +18 -0
  188. package/lib/typescript/type/AudioDevice.d.ts +15 -0
  189. package/lib/typescript/type/Call.d.ts +59 -0
  190. package/lib/typescript/type/CallInvite.d.ts +40 -0
  191. package/lib/typescript/type/CallKit.d.ts +60 -0
  192. package/lib/typescript/type/CallMessage.d.ts +20 -0
  193. package/lib/typescript/type/Error.d.ts +9 -0
  194. package/lib/typescript/type/NativeModule.d.ts +59 -0
  195. package/lib/typescript/type/RTCStats.d.ts +91 -0
  196. package/lib/typescript/type/Voice.d.ts +16 -0
  197. package/lib/typescript/type/common.d.ts +15 -0
  198. package/package.json +167 -0
  199. package/src/AudioDevice.tsx +88 -0
  200. package/src/Call.tsx +1343 -0
  201. package/src/CallInvite.tsx +757 -0
  202. package/src/CallMessage/CallMessage.ts +83 -0
  203. package/src/CallMessage/IncomingCallMessage.ts +104 -0
  204. package/src/CallMessage/OutgoingCallMessage.ts +308 -0
  205. package/src/ExpoModule.ts +59 -0
  206. package/src/Voice.tsx +1010 -0
  207. package/src/common.ts +16 -0
  208. package/src/constants.ts +193 -0
  209. package/src/error/InvalidArgumentError.ts +19 -0
  210. package/src/error/InvalidStateError.ts +19 -0
  211. package/src/error/TwilioError.ts +22 -0
  212. package/src/error/UnsupportedPlatformError.ts +19 -0
  213. package/src/error/generated.ts +2277 -0
  214. package/src/error/index.ts +18 -0
  215. package/src/error/utility.ts +36 -0
  216. package/src/index.tsx +24 -0
  217. package/src/type/AudioDevice.ts +18 -0
  218. package/src/type/Call.ts +102 -0
  219. package/src/type/CallInvite.ts +59 -0
  220. package/src/type/CallKit.ts +61 -0
  221. package/src/type/CallMessage.ts +31 -0
  222. package/src/type/Error.ts +11 -0
  223. package/src/type/NativeModule.ts +93 -0
  224. package/src/type/RTCStats.ts +99 -0
  225. package/src/type/Voice.ts +31 -0
  226. package/src/type/common.ts +16 -0
  227. package/twilio-voice-react-native.podspec +22 -0
@@ -0,0 +1,778 @@
1
+ package com.twiliovoicereactnative;
2
+
3
+ import androidx.annotation.NonNull;
4
+ import androidx.core.app.NotificationManagerCompat;
5
+
6
+ import com.facebook.react.bridge.Arguments;
7
+ import com.facebook.react.bridge.Promise;
8
+ import com.facebook.react.bridge.ReactApplicationContext;
9
+ import com.facebook.react.bridge.ReactContextBaseJavaModule;
10
+ import com.facebook.react.bridge.ReactMethod;
11
+ import com.facebook.react.bridge.ReadableMap;
12
+ import com.facebook.react.bridge.ReadableMapKeySetIterator;
13
+ import com.facebook.react.bridge.ReadableType;
14
+ import com.facebook.react.bridge.WritableArray;
15
+ import com.facebook.react.bridge.WritableMap;
16
+ import com.facebook.react.module.annotations.ReactModule;
17
+ import com.google.firebase.messaging.FirebaseMessaging;
18
+ import com.twilio.audioswitch.AudioDevice;
19
+ import com.twilio.voice.Call;
20
+ import com.twilio.voice.CallMessage;
21
+ import com.twilio.voice.ConnectOptions;
22
+ import com.twilio.voice.LogLevel;
23
+ import com.twilio.voice.RegistrationException;
24
+ import com.twilio.voice.RegistrationListener;
25
+ import com.twilio.voice.UnregistrationListener;
26
+ import com.twilio.voice.Voice;
27
+
28
+ import java.util.HashMap;
29
+ import java.util.Map;
30
+ import java.util.UUID;
31
+
32
+ import static com.twiliovoicereactnative.CommonConstants.ReactNativeVoiceSDK;
33
+ import static com.twiliovoicereactnative.CommonConstants.ReactNativeVoiceSDKVer;
34
+ import static com.twiliovoicereactnative.CommonConstants.VoiceEventType;
35
+ import static com.twiliovoicereactnative.CommonConstants.VoiceErrorKeyError;
36
+ import static com.twiliovoicereactnative.CommonConstants.ScopeVoice;
37
+ import static com.twiliovoicereactnative.CommonConstants.VoiceEventAudioDevicesUpdated;
38
+ import static com.twiliovoicereactnative.CommonConstants.VoiceEventError;
39
+ import static com.twiliovoicereactnative.CommonConstants.VoiceEventRegistered;
40
+ import static com.twiliovoicereactnative.CommonConstants.VoiceEventUnregistered;
41
+ import static com.twiliovoicereactnative.ConfigurationProperties.isFullScreenNotificationEnabled;
42
+ import static com.twiliovoicereactnative.JSEventEmitter.constructJSMap;
43
+ import static com.twiliovoicereactnative.ReactNativeArgumentsSerializer.serializeCall;
44
+ import static com.twiliovoicereactnative.ReactNativeArgumentsSerializer.serializeCallInvite;
45
+ import static com.twiliovoicereactnative.VoiceApplicationProxy.getCallRecordDatabase;
46
+ import static com.twiliovoicereactnative.VoiceApplicationProxy.getJSEventEmitter;
47
+ import static com.twiliovoicereactnative.VoiceApplicationProxy.getVoiceServiceApi;
48
+ import static com.twiliovoicereactnative.ReactNativeArgumentsSerializer.*;
49
+
50
+ import android.annotation.SuppressLint;
51
+ import android.content.Intent;
52
+ import android.net.Uri;
53
+ import android.os.Build;
54
+ import android.os.Handler;
55
+ import android.os.Looper;
56
+ import android.provider.Settings;
57
+ import android.util.Pair;
58
+
59
+ import com.twiliovoicereactnative.CallRecordDatabase.CallRecord;
60
+
61
+ @ReactModule(name = TwilioVoiceReactNativeModule.TAG)
62
+ public class TwilioVoiceReactNativeModule extends ReactContextBaseJavaModule {
63
+ static final String TAG = "TwilioVoiceReactNative";
64
+
65
+ private static final SDKLog logger = new SDKLog(TwilioVoiceReactNativeModule.class);
66
+ private static final String GLOBAL_ENV = "com.twilio.voice.env";
67
+ private static final String SDK_VERSION = "com.twilio.voice.env.sdk.version";
68
+ private final Handler mainHandler = new Handler(Looper.getMainLooper());
69
+
70
+ /**
71
+ * Map of common constant score strings to the Call.Score enum.
72
+ */
73
+ private static final Map<String, Call.Score> scoreMap = Map.of(
74
+ CommonConstants.CallFeedbackScoreNotReported, Call.Score.NOT_REPORTED,
75
+ CommonConstants.CallFeedbackScoreOne, Call.Score.ONE,
76
+ CommonConstants.CallFeedbackScoreTwo, Call.Score.TWO,
77
+ CommonConstants.CallFeedbackScoreThree, Call.Score.THREE,
78
+ CommonConstants.CallFeedbackScoreFour, Call.Score.FOUR,
79
+ CommonConstants.CallFeedbackScoreFive, Call.Score.FIVE
80
+ );
81
+
82
+ /**
83
+ * Map of common constant issue strings to the Call.Issue enum.
84
+ */
85
+ private static final Map<String, Call.Issue> issueMap = Map.of(
86
+ CommonConstants.CallFeedbackIssueAudioLatency, Call.Issue.AUDIO_LATENCY,
87
+ CommonConstants.CallFeedbackIssueChoppyAudio, Call.Issue.CHOPPY_AUDIO,
88
+ CommonConstants.CallFeedbackIssueEcho, Call.Issue.ECHO,
89
+ CommonConstants.CallFeedbackIssueDroppedCall, Call.Issue.DROPPED_CALL,
90
+ CommonConstants.CallFeedbackIssueNoisyCall, Call.Issue.NOISY_CALL,
91
+ CommonConstants.CallFeedbackIssueNotReported, Call.Issue.NOT_REPORTED,
92
+ CommonConstants.CallFeedbackIssueOneWayAudio, Call.Issue.ONE_WAY_AUDIO
93
+ );
94
+
95
+ private final ReactApplicationContext reactContext;
96
+ private final AudioSwitchManager audioSwitchManager;
97
+
98
+ public TwilioVoiceReactNativeModule(ReactApplicationContext reactContext) {
99
+ super(reactContext);
100
+
101
+ logger.log("instantiation of TwilioVoiceReactNativeModule");
102
+ this.reactContext = reactContext;
103
+ System.setProperty(GLOBAL_ENV, ReactNativeVoiceSDK);
104
+ System.setProperty(SDK_VERSION, ReactNativeVoiceSDKVer);
105
+ Voice.setLogLevel(BuildConfig.DEBUG ? LogLevel.DEBUG : LogLevel.ERROR);
106
+
107
+ getJSEventEmitter().setContext(reactContext);
108
+
109
+ audioSwitchManager = VoiceApplicationProxy.getAudioSwitchManager()
110
+ .setListener((audioDevices, selectedDeviceUuid, selectedDevice) -> {
111
+ WritableMap audioDeviceInfo = serializeAudioDeviceInfo(
112
+ audioDevices,
113
+ selectedDeviceUuid,
114
+ selectedDevice
115
+ );
116
+ audioDeviceInfo.putString(VoiceEventType, VoiceEventAudioDevicesUpdated);
117
+ getJSEventEmitter().sendEvent(ScopeVoice, audioDeviceInfo);
118
+ });
119
+ }
120
+
121
+ /**
122
+ * Invoked by React Native, necessary when passing this NativeModule to the constructor of a
123
+ * NativeEventEmitter on the JS layer.
124
+ * <p>
125
+ * Invoked when a listener is added to the NativeEventEmitter.
126
+ *
127
+ * @param eventName The string representation of the event.
128
+ */
129
+ @ReactMethod
130
+ public void addListener(String eventName) {
131
+ logger.debug(String.format("Calling addListener: %s", eventName));
132
+ }
133
+
134
+ /**
135
+ * Invoked by React Native, necessary when passing this NativeModule to the constructor of a
136
+ * NativeEventEmitter on the JS layer.
137
+ * <p>
138
+ * Invoked when listeners are removed from the NativeEventEmitter.
139
+ *
140
+ * @param count The number of event listeners removed.
141
+ */
142
+ @ReactMethod
143
+ public void removeListeners(Integer count) {
144
+ logger.debug("Calling removeListeners: " + count);
145
+ }
146
+
147
+ @ReactMethod
148
+ public void voice_connect_android(
149
+ String accessToken,
150
+ ReadableMap twimlParams,
151
+ String notificationDisplayName,
152
+ Promise promise
153
+ ) {
154
+ logger.debug(".voice_connect_android()");
155
+
156
+ mainHandler.post(() -> {
157
+ logger.debug(".voice_connect_android() > runnable");
158
+
159
+ HashMap<String, String> parsedTwimlParams = new HashMap<>();
160
+
161
+ ReadableMapKeySetIterator iterator = twimlParams.keySetIterator();
162
+ while (iterator.hasNextKey()) {
163
+ String key = iterator.nextKey();
164
+ ReadableType readableType = twimlParams.getType(key);
165
+ switch (readableType) {
166
+ case Boolean:
167
+ parsedTwimlParams.put(key, String.valueOf(twimlParams.getBoolean(key)));
168
+ break;
169
+ case Number:
170
+ // Can be int or double.
171
+ parsedTwimlParams.put(key, String.valueOf(twimlParams.getDouble(key)));
172
+ break;
173
+ case String:
174
+ parsedTwimlParams.put(key, twimlParams.getString(key));
175
+ break;
176
+ default:
177
+ logger.warning("Could not convert with key: " + key + ".");
178
+ break;
179
+ }
180
+ }
181
+
182
+ // connect & create call record
183
+ final UUID uuid = UUID.randomUUID();
184
+ final String callRecipient =
185
+ (parsedTwimlParams.containsKey("to") && !(parsedTwimlParams.get("to").isBlank()))
186
+ ? parsedTwimlParams.get("to")
187
+ : reactContext.getString(R.string.unknown_call_recipient);
188
+ ConnectOptions connectOptions = new ConnectOptions.Builder(accessToken)
189
+ .enableDscp(true)
190
+ .params(parsedTwimlParams)
191
+ .callMessageListener(new CallMessageListenerProxy())
192
+ .build();
193
+ try {
194
+ CallRecord callRecord = new CallRecord(
195
+ uuid,
196
+ getVoiceServiceApi().connect(
197
+ connectOptions,
198
+ new CallListenerProxy(uuid, getVoiceServiceApi().getServiceContext())),
199
+ callRecipient,
200
+ parsedTwimlParams,
201
+ CallRecord.Direction.OUTGOING,
202
+ notificationDisplayName);
203
+ getCallRecordDatabase().add(callRecord);
204
+ // notify JS layer
205
+ promise.resolve(serializeCall(callRecord));
206
+ } catch (SecurityException e) {
207
+ promise.reject(e, serializeError(31401, e.getMessage()));
208
+ }
209
+ });
210
+ }
211
+
212
+ @ReactMethod
213
+ public void voice_getVersion(Promise promise) {
214
+ promise.resolve(Voice.getVersion());
215
+ }
216
+
217
+ @ReactMethod
218
+ public void voice_getDeviceToken(Promise promise) {
219
+ FirebaseMessaging.getInstance().getToken()
220
+ .addOnCompleteListener(task -> {
221
+ if (!task.isSuccessful()) {
222
+ final String warningMsg =
223
+ reactContext.getString(R.string.fcm_token_registration_fail, task.getException());
224
+ logger.warning(warningMsg);
225
+ promise.reject(warningMsg);
226
+ return;
227
+ }
228
+
229
+ // Get FCM registration token
230
+ String fcmToken = task.getResult();
231
+
232
+ if (fcmToken == null) {
233
+ final String warningMsg = reactContext.getString(R.string.fcm_token_null);
234
+ logger.warning(warningMsg);
235
+ promise.reject(warningMsg);
236
+ } else {
237
+ promise.resolve(fcmToken);
238
+ }
239
+ });
240
+ }
241
+
242
+ @ReactMethod
243
+ public void voice_showNativeAvRoutePicker(Promise promise) {
244
+ // This API is iOS specific.
245
+ promise.resolve(null);
246
+ }
247
+
248
+ @ReactMethod
249
+ public void voice_getCalls(Promise promise) {
250
+ logger.debug(".voice_getCalls()");
251
+
252
+ mainHandler.post(() -> {
253
+ logger.debug(".voice_getCalls() > runnable");
254
+
255
+ WritableArray callInfos = Arguments.createArray();
256
+ for (CallRecord callRecord : getCallRecordDatabase().getCollection()) {
257
+ // incoming calls that have not been acted on do not have call-objects
258
+ if (null != callRecord.getVoiceCall()) {
259
+ callInfos.pushMap(serializeCall(callRecord));
260
+ }
261
+ }
262
+
263
+ promise.resolve(callInfos);
264
+ });
265
+ }
266
+
267
+ @ReactMethod
268
+ public void voice_getCallInvites(Promise promise) {
269
+ logger.debug(".voice_getCallInvites()");
270
+
271
+ mainHandler.post(() -> {
272
+ logger.debug(".voice_getCallInvites() > runnable");
273
+
274
+ WritableArray callInviteInfos = Arguments.createArray();
275
+ for (CallRecord callRecord : getCallRecordDatabase().getCollection()) {
276
+ if (null != callRecord.getCallInvite() &&
277
+ CallRecord.CallInviteState.ACTIVE == callRecord.getCallInviteState()) {
278
+ callInviteInfos.pushMap(serializeCallInvite(callRecord));
279
+ }
280
+ }
281
+
282
+ promise.resolve(callInviteInfos);
283
+ });
284
+ }
285
+
286
+ @ReactMethod
287
+ public void voice_getAudioDevices(Promise promise) {
288
+ Map<String, AudioDevice> audioDevices = audioSwitchManager.getAudioDevices();
289
+ String selectedAudioDeviceUuid = audioSwitchManager.getSelectedAudioDeviceUuid();
290
+ AudioDevice selectedAudioDevice = audioSwitchManager.getSelectedAudioDevice();
291
+
292
+ WritableMap audioDeviceInfo = serializeAudioDeviceInfo(
293
+ audioDevices,
294
+ selectedAudioDeviceUuid,
295
+ selectedAudioDevice
296
+ );
297
+
298
+ promise.resolve(audioDeviceInfo);
299
+ }
300
+
301
+ @ReactMethod
302
+ public void voice_selectAudioDevice(String uuid, Promise promise) {
303
+ AudioDevice audioDevice = audioSwitchManager.getAudioDevices().get(uuid);
304
+ if (audioDevice == null) {
305
+ promise.reject(reactContext.getString(R.string.missing_audiodevice_uuid, uuid));
306
+ return;
307
+ }
308
+
309
+ audioSwitchManager.getAudioSwitch().selectDevice(audioDevice);
310
+
311
+ promise.resolve(null);
312
+ }
313
+
314
+ @ReactMethod
315
+ public void voice_setIncomingCallContactHandleTemplate(String template, Promise promise) {
316
+ ConfigurationProperties.setIncomingCallContactHandleTemplate(reactContext, template);
317
+ promise.resolve(null);
318
+ }
319
+
320
+ /**
321
+ * Call methods.
322
+ */
323
+
324
+ @ReactMethod
325
+ public void call_getState(String uuid, Promise promise) {
326
+ logger.debug(".call_getState()");
327
+
328
+ mainHandler.post(() -> {
329
+ logger.debug(".call_getState() > runnable");
330
+
331
+ final CallRecord callRecord = validateCallRecord(UUID.fromString(uuid), promise);
332
+
333
+ if (null != callRecord) {
334
+ promise.resolve(callRecord.getVoiceCall().getState().toString().toLowerCase());
335
+ }
336
+ });
337
+ }
338
+
339
+ @ReactMethod
340
+ public void call_isMuted(String uuid, Promise promise) {
341
+ logger.debug(".call_isMuted()");
342
+
343
+ mainHandler.post(() -> {
344
+ logger.debug(".call_isMuted() > runnable");
345
+
346
+ final CallRecord callRecord = validateCallRecord(UUID.fromString(uuid), promise);
347
+
348
+ if (null != callRecord) {
349
+ promise.resolve(callRecord.getVoiceCall().isMuted());
350
+ }
351
+ });
352
+ }
353
+
354
+ @ReactMethod
355
+ public void call_isOnHold(String uuid, Promise promise) {
356
+ logger.debug(".call_isOnHold()");
357
+
358
+ mainHandler.post(() -> {
359
+ logger.debug(".call_isOnHold() > runnable");
360
+
361
+ final CallRecord callRecord = validateCallRecord(UUID.fromString(uuid), promise);
362
+
363
+ if (null != callRecord) {
364
+ promise.resolve(callRecord.getVoiceCall().isOnHold());
365
+ }
366
+ });
367
+ }
368
+
369
+ @ReactMethod
370
+ public void call_disconnect(String uuid, Promise promise) {
371
+ logger.debug(".call_disconnect()");
372
+
373
+ mainHandler.post(() -> {
374
+ logger.debug(".call_disconnect() > runnable");
375
+
376
+ final CallRecordDatabase.CallRecord callRecord =
377
+ validateCallRecord(UUID.fromString(uuid), promise);
378
+
379
+ if (null != callRecord) {
380
+ getVoiceServiceApi().disconnect(callRecord);
381
+ promise.resolve(uuid);
382
+ }
383
+ });
384
+ }
385
+
386
+ @ReactMethod
387
+ public void call_hold(String uuid, boolean hold, Promise promise) {
388
+ logger.debug(".call_hold()");
389
+
390
+ mainHandler.post(() -> {
391
+ logger.debug(".call_hold() > runnable");
392
+
393
+ final CallRecord callRecord = validateCallRecord(UUID.fromString(uuid), promise);
394
+
395
+ if (null != callRecord) {
396
+ callRecord.getVoiceCall().hold(hold);
397
+ promise.resolve(callRecord.getVoiceCall().isOnHold());
398
+ }
399
+ });
400
+ }
401
+
402
+ @ReactMethod
403
+ public void call_mute(String uuid, boolean mute, Promise promise) {
404
+ logger.debug(".call_mute()");
405
+
406
+ mainHandler.post(() -> {
407
+ logger.debug(".call_mute() > runnable");
408
+
409
+ final CallRecord callRecord = validateCallRecord(UUID.fromString(uuid), promise);
410
+
411
+ if (null != callRecord) {
412
+ callRecord.getVoiceCall().mute(mute);
413
+ promise.resolve(callRecord.getVoiceCall().isMuted());
414
+ }
415
+ });
416
+ }
417
+
418
+ @ReactMethod
419
+ public void call_sendDigits(String uuid, String digits, Promise promise) {
420
+ logger.debug(".call_sendDigits()");
421
+
422
+ mainHandler.post(() -> {
423
+ logger.debug(".call_sendDigits() > runnable");
424
+
425
+ final CallRecord callRecord = validateCallRecord(UUID.fromString(uuid), promise);
426
+
427
+ if (null != callRecord) {
428
+ callRecord.getVoiceCall().sendDigits(digits);
429
+ promise.resolve(uuid);
430
+ }
431
+ });
432
+ }
433
+
434
+ @ReactMethod
435
+ public void call_postFeedback(String uuid, String score, String issue, Promise promise) {
436
+ logger.debug(".call_postFeedback()");
437
+
438
+ mainHandler.post(() -> {
439
+ logger.debug(".call_postFeedback() > runnable");
440
+
441
+ final CallRecord callRecord = validateCallRecord(UUID.fromString(uuid), promise);
442
+
443
+ if (null != callRecord) {
444
+ Call.Score parsedScore = getScoreFromString(score);
445
+ Call.Issue parsedIssue = getIssueFromString(issue);
446
+
447
+ callRecord.getVoiceCall().postFeedback(parsedScore, parsedIssue);
448
+
449
+ promise.resolve(uuid);
450
+ }
451
+ });
452
+ }
453
+
454
+
455
+ @ReactMethod
456
+ public void call_getStats(String uuid, Promise promise) {
457
+ logger.debug(".call_getStats()");
458
+
459
+ mainHandler.post(() -> {
460
+ logger.debug(".call_getStats() > runnable");
461
+
462
+ final CallRecord callRecord = validateCallRecord(UUID.fromString(uuid), promise);
463
+
464
+ if (null != callRecord) {
465
+ callRecord.getVoiceCall().getStats(new StatsListenerProxy(uuid, reactContext, promise));
466
+ }
467
+ });
468
+ }
469
+
470
+ @ReactMethod
471
+ public void call_sendMessage(String uuid, String content, String contentType, String messageType, Promise promise) {
472
+ logger.debug(".call_sendMessage()");
473
+
474
+ mainHandler.post(() -> {
475
+ logger.debug(".call_sendMessage() > runnable");
476
+
477
+ final CallRecord callRecord = getCallRecordDatabase().get(new CallRecord(UUID.fromString(uuid)));
478
+
479
+ final CallMessage callMessage = new CallMessage.Builder(messageType)
480
+ .contentType(contentType).content(content).build();
481
+
482
+ promise.resolve((CallRecord.CallInviteState.ACTIVE == callRecord.getCallInviteState())
483
+ ? callRecord.getCallInvite().sendMessage(callMessage)
484
+ : callRecord.getVoiceCall().sendMessage(callMessage));
485
+ });
486
+ }
487
+
488
+ // Register/UnRegister
489
+
490
+ @ReactMethod
491
+ public void voice_register(String token, Promise promise) {
492
+ logger.debug(".voice_register()");
493
+
494
+ mainHandler.post(() -> {
495
+ logger.debug(".voice_register() > runnable");
496
+
497
+ FirebaseMessaging.getInstance().getToken()
498
+ .addOnCompleteListener(task -> {
499
+ if (!task.isSuccessful()) {
500
+ final String warningMsg =
501
+ reactContext.getString(R.string.fcm_token_registration_fail, task.getException());
502
+ logger.warning(warningMsg);
503
+ promise.reject(warningMsg);
504
+ return;
505
+ }
506
+
507
+ // Get new FCM registration token
508
+ String fcmToken = task.getResult();
509
+
510
+ if (fcmToken == null) {
511
+ final String warningMsg = reactContext.getString(R.string.fcm_token_null);
512
+ logger.warning(warningMsg);
513
+ promise.reject(warningMsg);
514
+ return;
515
+ }
516
+
517
+ // Log and toast
518
+ logger.debug("Registering with FCM with token " + fcmToken);
519
+ RegistrationListener registrationListener = createRegistrationListener(promise);
520
+ Voice.register(token, Voice.RegistrationChannel.FCM, fcmToken, registrationListener);
521
+ });
522
+ });
523
+ }
524
+
525
+ @ReactMethod
526
+ public void voice_unregister(String token, Promise promise) {
527
+ logger.debug(".voice_unregister()");
528
+
529
+ mainHandler.post(() -> {
530
+ logger.debug(".voice_unregister() > runnable");
531
+
532
+ FirebaseMessaging.getInstance().getToken()
533
+ .addOnCompleteListener(task -> {
534
+ if (!task.isSuccessful()) {
535
+ final String warningMsg =
536
+ reactContext.getString(R.string.fcm_token_registration_fail, task.getException());
537
+ logger.warning(warningMsg);
538
+ promise.reject(warningMsg);
539
+ return;
540
+ }
541
+
542
+ // Get new FCM registration token
543
+ String fcmToken = task.getResult();
544
+
545
+ if (fcmToken == null) {
546
+ final String warningMsg = reactContext.getString(R.string.fcm_token_null);
547
+ logger.warning(warningMsg);
548
+ promise.reject(warningMsg);
549
+ return;
550
+ }
551
+
552
+ // Log and toast
553
+ logger.debug("Registering with FCM with token " + fcmToken);
554
+ UnregistrationListener unregistrationListener = createUnregistrationListener(promise);
555
+ Voice.unregister(token, Voice.RegistrationChannel.FCM, fcmToken, unregistrationListener);
556
+ });
557
+ });
558
+ }
559
+
560
+ @ReactMethod void voice_handleEvent(ReadableMap messageData, Promise promise) {
561
+ logger.debug(".voice_handleEvent()");
562
+
563
+ mainHandler.post(() -> {
564
+ logger.debug(".voice_handleEvent() > runnable");
565
+
566
+ // validate embedded firebase module is disabled
567
+ if (ConfigurationProperties.isFirebaseServiceEnabled(reactContext)) {
568
+ final String errorMsg = reactContext.getString(R.string.method_invocation_invalid);
569
+ logger.warning("Embedded firebase messaging enabled, handleEvent invocation invalid!");
570
+ promise.reject(errorMsg);
571
+ return;
572
+ }
573
+ // parse data to string map
574
+ final HashMap<String, String> parsedMessageData = new HashMap<>();
575
+ ReadableMapKeySetIterator iterator = messageData.keySetIterator();
576
+ while (iterator.hasNextKey()) {
577
+ String key = iterator.nextKey();
578
+ parsedMessageData.put(key, messageData.getString(key));
579
+ }
580
+ // attempt to parse message
581
+ if (Voice.handleMessage(
582
+ reactContext,
583
+ parsedMessageData,
584
+ new VoiceFirebaseMessagingService.MessageHandler(),
585
+ new CallMessageListenerProxy())) {
586
+ promise.resolve(true);
587
+ } else {
588
+ promise.resolve(false);
589
+ }
590
+ });
591
+ }
592
+
593
+ // CallInvite
594
+
595
+ @ReactMethod
596
+ public void callInvite_accept(String callInviteUuid, ReadableMap options, Promise promise) {
597
+ logger.debug("callInvite_accept uuid" + callInviteUuid);
598
+
599
+ mainHandler.post(() -> {
600
+ logger.debug(".callInvite_accept() > runnable");
601
+
602
+ final CallRecord callRecord =
603
+ validateCallInviteRecord(UUID.fromString(callInviteUuid), promise);
604
+
605
+ if (null != callRecord) {
606
+ // Store promise for callback
607
+ callRecord.setCallAcceptedPromise(promise);
608
+
609
+ // Send Event to service
610
+ try {
611
+ getVoiceServiceApi().acceptCall(callRecord);
612
+ } catch (SecurityException e) {
613
+ promise.reject(e, serializeError(31401, e.getMessage()));
614
+ }
615
+ }
616
+ });
617
+ }
618
+
619
+ @ReactMethod
620
+ public void callInvite_reject(String uuid, Promise promise) {
621
+ logger.debug("callInvite_reject uuid" + uuid);
622
+
623
+ mainHandler.post(() -> {
624
+ logger.debug(".callInvite_reject() > runnable");
625
+
626
+ final CallRecord callRecord = validateCallInviteRecord(UUID.fromString(uuid), promise);
627
+
628
+ if (null != callRecord) {
629
+ // Store promise for callback
630
+ callRecord.setCallRejectedPromise(promise);
631
+
632
+ // Send Event to service
633
+ getVoiceServiceApi().rejectCall(callRecord);
634
+ }
635
+ });
636
+ }
637
+
638
+ @ReactMethod
639
+ public void system_isFullScreenNotificationEnabled(Promise promise) {
640
+ boolean isEnabled =
641
+ isFullScreenNotificationEnabled(reactContext) &&
642
+ NotificationManagerCompat.from(reactContext).canUseFullScreenIntent();
643
+
644
+ promise.resolve(isEnabled);
645
+ }
646
+
647
+ @ReactMethod
648
+ public void system_requestFullScreenNotificationPermission(Promise promise) {
649
+ final boolean shouldStartActivity =
650
+ Build.VERSION.SDK_INT > Build.VERSION_CODES.TIRAMISU &&
651
+ isFullScreenNotificationEnabled(reactContext);
652
+
653
+ if (shouldStartActivity) {
654
+ try {
655
+ Intent intent = new Intent(
656
+ Settings.ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT,
657
+ Uri.parse("package:" + reactContext.getPackageName()));
658
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
659
+ reactContext.startActivity(intent);
660
+ } catch (Exception e) {
661
+ promise.reject(e);
662
+ }
663
+ }
664
+
665
+ promise.resolve(null);
666
+ }
667
+
668
+ @Override
669
+ @NonNull
670
+ public String getName() {
671
+ return TAG;
672
+ }
673
+
674
+ private RegistrationListener createRegistrationListener(Promise promise) {
675
+ return new RegistrationListener() {
676
+ @Override
677
+ public void onRegistered(@NonNull String accessToken, @NonNull String fcmToken) {
678
+ logger.log("Successfully registered FCM");
679
+ sendJSEvent(constructJSMap(new Pair<>(VoiceEventType, VoiceEventRegistered)));
680
+ promise.resolve(null);
681
+ }
682
+
683
+ @Override
684
+ public void onError(@NonNull RegistrationException registrationException,
685
+ @NonNull String accessToken,
686
+ @NonNull String fcmToken) {
687
+ String errorMessage = reactContext.getString(
688
+ R.string.registration_error,
689
+ registrationException.getErrorCode(),
690
+ registrationException.getMessage());
691
+ logger.error(errorMessage);
692
+
693
+ sendJSEvent(constructJSMap(
694
+ new Pair<>(VoiceEventType, VoiceEventError),
695
+ new Pair<>(VoiceErrorKeyError, serializeVoiceException(registrationException))));
696
+
697
+ promise.reject(errorMessage);
698
+ }
699
+ };
700
+ }
701
+
702
+ private UnregistrationListener createUnregistrationListener(Promise promise) {
703
+ return new UnregistrationListener() {
704
+ @Override
705
+ public void onUnregistered(String accessToken, String fcmToken) {
706
+ logger.log("Successfully unregistered FCM");
707
+ sendJSEvent(constructJSMap(new Pair<>(VoiceEventType, VoiceEventUnregistered)));
708
+ promise.resolve(null);
709
+ }
710
+
711
+ @Override
712
+ public void onError(RegistrationException registrationException, String accessToken, String fcmToken) {
713
+ @SuppressLint("DefaultLocale")
714
+ String errorMessage = reactContext.getString(
715
+ R.string.unregistration_error,
716
+ registrationException.getErrorCode(),
717
+ registrationException.getMessage());
718
+ logger.error(errorMessage);
719
+
720
+ sendJSEvent(constructJSMap(
721
+ new Pair<>(VoiceEventType, VoiceEventError),
722
+ new Pair<>(VoiceErrorKeyError, serializeVoiceException(registrationException))));
723
+
724
+ promise.reject(errorMessage);
725
+ }
726
+ };
727
+ }
728
+
729
+ /**
730
+ * Use the score map to get a Call.Score value from a string.
731
+ * @param score The score as a string passed from the JS layer.
732
+ * @return a Call.Score enum value. If the passed string is not in the enum, defaults to
733
+ * Call.Score.NOT_REPORTED.
734
+ */
735
+ private static Call.Score getScoreFromString(String score) {
736
+ return scoreMap.containsKey(score)
737
+ ? scoreMap.get(score)
738
+ : Call.Score.NOT_REPORTED;
739
+ }
740
+
741
+ /**
742
+ * Use the issue map to get a Call.Issue value from a string.
743
+ * @param issue The issue as a string passed from the JS layer.
744
+ * @return a Call.Issue enum value. If the passed string is not in the enum, defaults to
745
+ * Call.Issue.NOT_REPORTED.
746
+ */
747
+ private static Call.Issue getIssueFromString(String issue) {
748
+ return issueMap.containsKey(issue)
749
+ ? issueMap.get(issue)
750
+ : Call.Issue.NOT_REPORTED;
751
+ }
752
+
753
+ private CallRecord validateCallRecord(@NonNull final UUID uuid,
754
+ @NonNull final Promise promise) {
755
+ CallRecord callRecord = getCallRecordDatabase().get(new CallRecord(uuid));
756
+
757
+ if (null == callRecord || null == callRecord.getVoiceCall()) {
758
+ promise.reject(reactContext.getString(R.string.missing_call_uuid, uuid));
759
+ return null;
760
+ }
761
+ return callRecord;
762
+ }
763
+
764
+ private CallRecord validateCallInviteRecord(@NonNull final UUID uuid,
765
+ @NonNull final Promise promise) {
766
+ CallRecord callRecord = getCallRecordDatabase().get(new CallRecord(uuid));
767
+
768
+ if (null == callRecord || null == callRecord.getCallInvite()) {
769
+ promise.reject(reactContext.getString(R.string.missing_callinvite_uuid, uuid));
770
+ return null;
771
+ }
772
+ return callRecord;
773
+ }
774
+
775
+ private void sendJSEvent(@NonNull WritableMap event) {
776
+ getJSEventEmitter().sendEvent(ScopeVoice, event);
777
+ }
778
+ }