expo-callkit-telecom 0.1.0

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 (93) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +197 -0
  3. package/android/build.gradle +32 -0
  4. package/android/src/main/AndroidManifest.xml +33 -0
  5. package/android/src/main/java/expo/modules/callkittelecom/ExpoCallKitTelecomModule.kt +384 -0
  6. package/android/src/main/java/expo/modules/callkittelecom/IncomingCallActivity.kt +275 -0
  7. package/android/src/main/java/expo/modules/callkittelecom/events/CallEventEmitter.kt +151 -0
  8. package/android/src/main/java/expo/modules/callkittelecom/events/CallEvents.kt +59 -0
  9. package/android/src/main/java/expo/modules/callkittelecom/managers/CallAudioManager.kt +361 -0
  10. package/android/src/main/java/expo/modules/callkittelecom/managers/CallManager.kt +891 -0
  11. package/android/src/main/java/expo/modules/callkittelecom/managers/CallNotificationManager.kt +445 -0
  12. package/android/src/main/java/expo/modules/callkittelecom/managers/CaptureSessionManager.kt +27 -0
  13. package/android/src/main/java/expo/modules/callkittelecom/managers/DialtonePlayer.kt +171 -0
  14. package/android/src/main/java/expo/modules/callkittelecom/managers/FulfillRequestManager.kt +150 -0
  15. package/android/src/main/java/expo/modules/callkittelecom/managers/VoIPPushManager.kt +54 -0
  16. package/android/src/main/java/expo/modules/callkittelecom/models/CallModels.kt +269 -0
  17. package/android/src/main/java/expo/modules/callkittelecom/services/CallNotificationReceiver.kt +54 -0
  18. package/android/src/main/java/expo/modules/callkittelecom/services/ExpoCallKitTelecomMessagingService.kt +161 -0
  19. package/android/src/main/java/expo/modules/callkittelecom/store/CallStore.kt +181 -0
  20. package/android/src/main/java/expo/modules/callkittelecom/utils/CallKitTelecomLog.kt +52 -0
  21. package/android/src/main/java/expo/modules/callkittelecom/utils/PermissionUtils.kt +28 -0
  22. package/android/src/main/res/drawable/expo_callkit_telecom_bg_answer.xml +9 -0
  23. package/android/src/main/res/drawable/expo_callkit_telecom_bg_avatar.xml +5 -0
  24. package/android/src/main/res/drawable/expo_callkit_telecom_bg_decline.xml +9 -0
  25. package/android/src/main/res/drawable/expo_callkit_telecom_ic_answer.xml +9 -0
  26. package/android/src/main/res/drawable/expo_callkit_telecom_ic_decline.xml +9 -0
  27. package/android/src/main/res/drawable/expo_callkit_telecom_ic_videocam.xml +9 -0
  28. package/android/src/main/res/layout/activity_incoming_call.xml +169 -0
  29. package/app.json +8 -0
  30. package/app.plugin.js +1 -0
  31. package/build/Calls.d.ts +577 -0
  32. package/build/Calls.d.ts.map +1 -0
  33. package/build/Calls.js +715 -0
  34. package/build/Calls.js.map +1 -0
  35. package/build/Calls.types.d.ts +203 -0
  36. package/build/Calls.types.d.ts.map +1 -0
  37. package/build/Calls.types.js +2 -0
  38. package/build/Calls.types.js.map +1 -0
  39. package/build/ExpoCallKitTelecomModule.d.ts +3 -0
  40. package/build/ExpoCallKitTelecomModule.d.ts.map +1 -0
  41. package/build/ExpoCallKitTelecomModule.js +4 -0
  42. package/build/ExpoCallKitTelecomModule.js.map +1 -0
  43. package/build/hooks/index.d.ts +2 -0
  44. package/build/hooks/index.d.ts.map +1 -0
  45. package/build/hooks/index.js +2 -0
  46. package/build/hooks/index.js.map +1 -0
  47. package/build/hooks/useVoIPPushToken.d.ts +14 -0
  48. package/build/hooks/useVoIPPushToken.d.ts.map +1 -0
  49. package/build/hooks/useVoIPPushToken.js +26 -0
  50. package/build/hooks/useVoIPPushToken.js.map +1 -0
  51. package/build/index.d.ts +4 -0
  52. package/build/index.d.ts.map +1 -0
  53. package/build/index.js +4 -0
  54. package/build/index.js.map +1 -0
  55. package/expo-module.config.json +10 -0
  56. package/ios/AppDelegateSubscriber.swift +93 -0
  57. package/ios/ExpoCallKitTelecom.podspec +31 -0
  58. package/ios/ExpoCallKitTelecomLogger.swift +55 -0
  59. package/ios/ExpoCallKitTelecomModule.swift +503 -0
  60. package/ios/Managers/AudioManager.swift +363 -0
  61. package/ios/Managers/CallEventEmitter.swift +199 -0
  62. package/ios/Managers/CallManager+CXProviderDelegate.swift +195 -0
  63. package/ios/Managers/CallManager.swift +714 -0
  64. package/ios/Managers/CaptureSessionManager.swift +54 -0
  65. package/ios/Managers/DialtonePlayer.swift +126 -0
  66. package/ios/Managers/FulfillRequestManager.swift +154 -0
  67. package/ios/Managers/VoIPPushManager+PKPushRegistryDelegate.swift +123 -0
  68. package/ios/Managers/VoIPPushManager.swift +58 -0
  69. package/ios/Models/CallEvents.swift +263 -0
  70. package/ios/Models/CallOptions.swift +15 -0
  71. package/ios/Models/CallParticipant.swift +37 -0
  72. package/ios/Models/CallSession.swift +80 -0
  73. package/ios/Models/IncomingCallEvent.swift +196 -0
  74. package/ios/Stores/CallStore.swift +149 -0
  75. package/package.json +56 -0
  76. package/plugin/build/constants.d.ts +3 -0
  77. package/plugin/build/constants.js +7 -0
  78. package/plugin/build/withExpoCallKitTelecom.d.ts +67 -0
  79. package/plugin/build/withExpoCallKitTelecom.js +16 -0
  80. package/plugin/build/withExpoCallKitTelecomAndroid.d.ts +3 -0
  81. package/plugin/build/withExpoCallKitTelecomAndroid.js +177 -0
  82. package/plugin/build/withExpoCallKitTelecomIos.d.ts +3 -0
  83. package/plugin/build/withExpoCallKitTelecomIos.js +195 -0
  84. package/plugin/src/constants.ts +4 -0
  85. package/plugin/src/withExpoCallKitTelecom.ts +83 -0
  86. package/plugin/src/withExpoCallKitTelecomAndroid.ts +293 -0
  87. package/plugin/src/withExpoCallKitTelecomIos.ts +276 -0
  88. package/src/Calls.ts +848 -0
  89. package/src/Calls.types.ts +275 -0
  90. package/src/ExpoCallKitTelecomModule.ts +4 -0
  91. package/src/hooks/index.ts +1 -0
  92. package/src/hooks/useVoIPPushToken.ts +34 -0
  93. package/src/index.ts +3 -0
@@ -0,0 +1,275 @@
1
+ /**
2
+ * Type of VoIP push token reported by `getVoIPPushToken`.
3
+ *
4
+ * - `"APNS_VOIP"` — Apple Push Notification service VoIP channel (iOS)
5
+ * - `"FCM"` — Firebase Cloud Messaging (Android)
6
+ */
7
+ export type PushTokenType = "APNS_VOIP" | "FCM";
8
+
9
+ // Native Event Metadata
10
+
11
+ /** Metadata attached to all native events */
12
+ export interface NativeEventMeta {
13
+ /** Whether the event was flushed from queue (true) or sent in real-time (false) */
14
+ flushed: boolean;
15
+ /** ISO8601 timestamp of when the event was created */
16
+ timestamp: string;
17
+ }
18
+
19
+ /** Base type for all native events with metadata */
20
+ export interface NativeEvent {
21
+ meta: NativeEventMeta;
22
+ }
23
+
24
+ // Call Session
25
+ export interface CallSession {
26
+ id: string;
27
+ options: CallOptions;
28
+ origin: CallSessionOrigin;
29
+ remoteParticipants: CallParticipant[];
30
+ incomingCallEvent?: IncomingCallEvent;
31
+ status: CallSessionStatus;
32
+ connectedAt?: string;
33
+ isMuted: boolean;
34
+ isOnHold: boolean;
35
+ dtmfDigits?: string;
36
+ }
37
+
38
+ export interface CallOptions {
39
+ hasVideo: boolean;
40
+ }
41
+
42
+ export type CallSessionOrigin = "incoming" | "outgoingApp" | "outgoingSystem";
43
+
44
+ export interface CallParticipant {
45
+ /** Opaque, stable app identifier for this participant. */
46
+ id: string;
47
+ /** Display name. */
48
+ displayName?: string;
49
+ /** Avatar URL. */
50
+ avatarUrl?: string;
51
+ /** Phone number in E.164 (e.g. "+14155551234"). When present on iOS, the
52
+ * CallKit handle is set to this number, enabling Recents and Siri. */
53
+ phoneNumber?: string;
54
+ /** Email address. */
55
+ email?: string;
56
+ }
57
+
58
+ /**
59
+ * Call session status representing the lifecycle of a call.
60
+ *
61
+ * Outgoing call flow: requesting → connecting → connected → ended
62
+ * Incoming call flow: ringing → connecting → connected → ended
63
+ *
64
+ * - `requesting` — Outgoing only. The call request has been submitted to
65
+ * CallKit/Telecom and is awaiting system acceptance.
66
+ * - `ringing` — Incoming only. The call has been reported to CallKit/Telecom
67
+ * and the user sees the native incoming call UI or notification.
68
+ * - `connecting` — Both directions. For outgoing calls, the system accepted the
69
+ * call and the dialtone is playing while waiting for the remote party to answer.
70
+ * For incoming calls, the user answered and media is being established.
71
+ * - `connected` — Both directions. Media is flowing and the call is active.
72
+ * - `ended` — Both directions. Transient state during teardown before the
73
+ * session is removed from the store.
74
+ */
75
+ export type CallSessionStatus =
76
+ | "requesting"
77
+ | "connecting"
78
+ | "ringing"
79
+ | "connected"
80
+ | "ended";
81
+
82
+ // Call Session events
83
+ export interface CallSessionAddedEvent extends NativeEvent {
84
+ session: CallSession;
85
+ }
86
+
87
+ export interface CallSessionUpdatedEvent extends NativeEvent {
88
+ session: CallSession;
89
+ }
90
+
91
+ export interface CallSessionRemovedEvent extends NativeEvent {
92
+ id: string;
93
+ }
94
+
95
+ // Microphone/Camera Permission Status
96
+ export type PermissionStatus =
97
+ | "granted"
98
+ | "denied"
99
+ | "undetermined"
100
+ | "restricted"
101
+ | "unknown";
102
+
103
+ // Audio Session
104
+
105
+ export interface AudioSession {
106
+ isActive: boolean;
107
+ /** iOS only: whether the WebRTC RTCAudioSession is active. */
108
+ rtcSessionIsActive?: boolean;
109
+ /** iOS only: whether the AVAudioSession is active. */
110
+ avSessionIsActive?: boolean;
111
+ /** iOS only: whether the RTCAudioSession audio track is enabled. */
112
+ isAudioEnabled?: boolean;
113
+ /** iOS only: whether manual audio mode is enabled on RTCAudioSession. */
114
+ useManualAudio?: boolean;
115
+ isOtherAudioPlaying: boolean;
116
+ category: string;
117
+ mode: string;
118
+ /** iOS only: AVAudioSession category options. */
119
+ categoryOptions?: string[];
120
+ sampleRate: number;
121
+ ioBufferDuration: number;
122
+ inputNumberOfChannels: number;
123
+ outputNumberOfChannels: number;
124
+ microphonePermission: PermissionStatus;
125
+ currentRoute: AudioRoute;
126
+ /** Available audio output devices. Populated on Android; undefined on iOS. */
127
+ availableRoutes?: AudioPort[];
128
+ }
129
+
130
+ export interface AudioRoute {
131
+ inputs: AudioPort[];
132
+ outputs: AudioPort[];
133
+ }
134
+
135
+ export interface AudioPort {
136
+ portType: AudioOutputPortType;
137
+ portName: string;
138
+ uid: string;
139
+ }
140
+
141
+ /**
142
+ * Cross-platform audio output port type identifiers.
143
+ * Both iOS and Android map their native audio device types to these shared values.
144
+ */
145
+ export type AudioOutputPortType =
146
+ | "builtInReceiver" // Earpiece
147
+ | "builtInSpeaker" // Speaker
148
+ | "headphones" // Wired headphones
149
+ | "bluetoothA2DP" // Bluetooth A2DP audio
150
+ | "bluetoothLE" // Bluetooth Low Energy audio
151
+ | "bluetoothHFP" // Bluetooth Hands-Free Profile
152
+ | "airPlay" // AirPlay
153
+ | "hdmi" // HDMI output
154
+ | "carAudio" // CarPlay
155
+ | "usbAudio" // USB audio
156
+ | "lineOut" // Line out
157
+ | (string & {}); // Allow other unknown port types
158
+
159
+ // Audio Session Events
160
+ export interface AudioSessionCallInfo {
161
+ id: string;
162
+ status: CallSessionStatus;
163
+ }
164
+
165
+ export interface AudioSessionActivatedEvent extends NativeEvent {
166
+ calls: AudioSessionCallInfo[];
167
+ }
168
+
169
+ export interface AudioSessionDeactivatedEvent extends NativeEvent {
170
+ calls: AudioSessionCallInfo[];
171
+ }
172
+
173
+ export interface AudioRouteChangedEvent extends NativeEvent {
174
+ currentRoute: AudioRoute;
175
+ /** Available audio output devices. Populated on Android; undefined on iOS. */
176
+ availableRoutes?: AudioPort[];
177
+ }
178
+
179
+ // Capture Session
180
+ export interface CaptureSession {
181
+ cameraPermission: PermissionStatus;
182
+ /** Whether the device supports multitasking camera access (iOS 16+). */
183
+ isMultitaskingCameraAccessSupported?: boolean;
184
+ }
185
+
186
+ // Call Action events
187
+ export interface CallActionEvent extends NativeEvent {
188
+ id: string;
189
+ }
190
+
191
+ export interface OutgoingCallStartedEvent extends CallActionEvent {}
192
+
193
+ export interface IncomingCallEvent {
194
+ /** Unique event identifier (UUID). Used for dedup. */
195
+ eventId: string;
196
+ /** Your backend's id for this call. Distinct from {@link CallSession.id},
197
+ * which is the OS-assigned native call UUID. Use this id to talk to your
198
+ * server about the call (e.g. POST /calls/:serverCallId/answer). */
199
+ serverCallId: string;
200
+ /** True for video calls, false for audio. */
201
+ hasVideo: boolean;
202
+ /** RFC 3339 timestamp of when the call was placed. Optional; defaults to now. */
203
+ startedAt?: string;
204
+ /** Caller identity and addressing. */
205
+ caller: CallParticipant;
206
+ /**
207
+ * App-defined extra fields, forwarded verbatim from the push payload.
208
+ *
209
+ * The library treats this as opaque — put whatever your app needs here
210
+ * (chatId, tenantId, room name, etc.). Cast to your own type at the
211
+ * read site.
212
+ */
213
+ metadata?: Record<string, unknown>;
214
+ }
215
+
216
+ export interface IncomingCallReportedEvent extends CallActionEvent {}
217
+
218
+ export interface CallAnsweredEvent extends CallActionEvent {
219
+ requestId: string;
220
+ }
221
+
222
+ export interface CallEndedEvent extends CallActionEvent {}
223
+
224
+ export type CallEndedReason =
225
+ | "failed"
226
+ | "remoteEnded"
227
+ | "unanswered"
228
+ | "answeredElsewhere"
229
+ | "declinedElsewhere"
230
+ | "unknown";
231
+
232
+ export interface CallReportedEnded extends CallActionEvent {
233
+ reason: CallEndedReason;
234
+ }
235
+ export interface SetMutedActionEvent extends CallActionEvent {
236
+ isMuted: boolean;
237
+ }
238
+ export interface VideoChangedEvent extends CallActionEvent {
239
+ hasVideo: boolean;
240
+ }
241
+ export interface SetHeldActionEvent extends CallActionEvent {
242
+ isOnHold: boolean;
243
+ }
244
+ export interface DTMFEvent extends CallActionEvent {
245
+ digits: string;
246
+ }
247
+
248
+ // Call Intent Events
249
+
250
+ export type CallIntentHandleType = "phoneNumber" | "email" | "unknown";
251
+
252
+ export interface CallIntentReceivedEvent extends NativeEvent {
253
+ handle: string;
254
+ handleType: CallIntentHandleType;
255
+ hasVideo: boolean;
256
+ }
257
+
258
+ // VoIP Push
259
+
260
+ /** A VoIP push token bundled with its type. */
261
+ export interface VoIPPushToken {
262
+ /** The VoIP push token string. */
263
+ token: string;
264
+ /** The type of token this platform provides. */
265
+ type: PushTokenType;
266
+ }
267
+
268
+ // VoIP Push Events
269
+
270
+ export interface VoIPPushTokenUpdatedEvent extends NativeEvent {
271
+ /** The VoIP push token string, or undefined if invalidated */
272
+ token?: string;
273
+ /** The type of VoIP push token (e.g. "APNS_VOIP", "FCM"). */
274
+ type: PushTokenType;
275
+ }
@@ -0,0 +1,4 @@
1
+ import { requireNativeModule } from "expo";
2
+
3
+ // This call loads the native module object from the JSI.
4
+ export default requireNativeModule("ExpoCallKitTelecom");
@@ -0,0 +1 @@
1
+ export * from "./useVoIPPushToken";
@@ -0,0 +1,34 @@
1
+ import { useEffect, useState } from "react";
2
+
3
+ import { addVoIPPushTokenUpdatedListener, getVoIPPushToken } from "../Calls";
4
+ import type { PushTokenType, VoIPPushToken } from "../Calls.types";
5
+
6
+ /**
7
+ * Hook that returns the current VoIP push token and subscribes to updates.
8
+ *
9
+ * Reads the initial token synchronously and re-renders whenever the native
10
+ * `onVoIPPushTokenUpdated` event fires (e.g. when the OS provides a new token
11
+ * or invalidates the existing one).
12
+ *
13
+ * On iOS this returns an APNs VoIP token; on Android it returns an FCM token.
14
+ *
15
+ * @returns The current token, or `null` if not yet available.
16
+ */
17
+ export function useVoIPPushToken(): VoIPPushToken | null {
18
+ const [token, setToken] = useState<VoIPPushToken | null>(() =>
19
+ getVoIPPushToken(),
20
+ );
21
+
22
+ useEffect(() => {
23
+ const subscription = addVoIPPushTokenUpdatedListener((event) => {
24
+ setToken(
25
+ event.token
26
+ ? { token: event.token, type: event.type as PushTokenType }
27
+ : null,
28
+ );
29
+ });
30
+ return () => subscription.remove();
31
+ }, []);
32
+
33
+ return token;
34
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from "./Calls";
2
+ export * from "./Calls.types";
3
+ export * from "./hooks";