@telnyx/react-voice-commons-sdk 0.1.1 → 0.1.3

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 (51) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +483 -0
  3. package/TelnyxVoiceCommons.podspec +31 -31
  4. package/ios/CallKitBridge.m +43 -43
  5. package/ios/CallKitBridge.swift +874 -879
  6. package/ios/README.md +211 -211
  7. package/ios/VoicePnBridge.m +30 -30
  8. package/ios/VoicePnBridge.swift +86 -86
  9. package/lib/callkit/callkit-coordinator.d.ts +2 -5
  10. package/lib/callkit/callkit-coordinator.js +15 -32
  11. package/lib/callkit/use-callkit-coordinator.d.ts +21 -21
  12. package/lib/callkit/use-callkit-coordinator.js +53 -53
  13. package/lib/hooks/useNetworkStateHandler.d.ts +0 -0
  14. package/lib/hooks/useNetworkStateHandler.js +0 -0
  15. package/lib/internal/calls/call-state-controller.d.ts +2 -10
  16. package/lib/internal/calls/call-state-controller.js +48 -54
  17. package/lib/internal/session/session-manager.d.ts +1 -5
  18. package/lib/internal/session/session-manager.js +35 -25
  19. package/lib/internal/voice-pn-bridge.d.ts +103 -1
  20. package/lib/internal/voice-pn-bridge.js +209 -2
  21. package/lib/models/call-state.d.ts +3 -1
  22. package/lib/models/call-state.js +5 -1
  23. package/lib/models/call.d.ts +31 -3
  24. package/lib/models/call.js +105 -5
  25. package/lib/telnyx-voice-app.js +78 -38
  26. package/lib/telnyx-voip-client.d.ts +4 -2
  27. package/lib/telnyx-voip-client.js +5 -3
  28. package/package.json +111 -104
  29. package/src/callkit/callkit-coordinator.ts +830 -846
  30. package/src/callkit/callkit.ts +322 -322
  31. package/src/callkit/index.ts +4 -4
  32. package/src/callkit/use-callkit.ts +345 -345
  33. package/src/context/TelnyxVoiceContext.tsx +33 -33
  34. package/src/hooks/use-callkit-coordinator.ts +60 -60
  35. package/src/hooks/useAppReadyNotifier.ts +25 -25
  36. package/src/hooks/useAppStateHandler.ts +134 -134
  37. package/src/hooks/useNetworkStateHandler.ts +0 -0
  38. package/src/index.ts +56 -56
  39. package/src/internal/CallKitHandler.tsx +149 -149
  40. package/src/internal/callkit-manager.ts +335 -335
  41. package/src/internal/calls/call-state-controller.ts +407 -384
  42. package/src/internal/session/session-manager.ts +483 -467
  43. package/src/internal/user-defaults-helpers.ts +58 -58
  44. package/src/internal/voice-pn-bridge.ts +266 -18
  45. package/src/models/call-state.ts +105 -98
  46. package/src/models/call.ts +502 -388
  47. package/src/models/config.ts +125 -125
  48. package/src/models/connection-state.ts +50 -50
  49. package/src/telnyx-voice-app.tsx +737 -690
  50. package/src/telnyx-voip-client.ts +551 -539
  51. package/src/types/telnyx-sdk.d.ts +93 -79
@@ -1,3 +1,9 @@
1
+ import { EmitterSubscription } from 'react-native';
2
+ export interface CallActionEvent {
3
+ action: string;
4
+ callId?: string;
5
+ timestamp: number;
6
+ }
1
7
  export interface VoicePnBridgeInterface {
2
8
  getPendingPushAction(): Promise<{
3
9
  action: string | null;
@@ -5,10 +11,106 @@ export interface VoicePnBridgeInterface {
5
11
  }>;
6
12
  setPendingPushAction(action: string, metadata: string): Promise<boolean>;
7
13
  clearPendingPushAction(): Promise<boolean>;
14
+ getPendingCallAction(): Promise<{
15
+ action: string | null;
16
+ callId: string | null;
17
+ timestamp: number | null;
18
+ }>;
19
+ clearPendingCallAction(): Promise<boolean>;
20
+ endCall(callId: string | null): Promise<boolean>;
21
+ showOngoingCallNotification(callerName: string | null, callerNumber: string | null, callId: string | null): Promise<boolean>;
22
+ hideOngoingCallNotification(): Promise<boolean>;
23
+ hideIncomingCallNotification(): Promise<boolean>;
8
24
  getVoipToken(): Promise<string | null>;
9
25
  getPendingVoipPush(): Promise<string | null>;
10
26
  clearPendingVoipPush(): Promise<boolean>;
11
27
  getPendingVoipAction(): Promise<string | null>;
12
28
  clearPendingVoipAction(): Promise<boolean>;
13
29
  }
14
- export declare const VoicePnBridge: VoicePnBridgeInterface;
30
+ declare const NativeBridge: VoicePnBridgeInterface;
31
+ /**
32
+ * Enhanced VoicePnBridge with call control and event handling capabilities
33
+ */
34
+ export declare class VoicePnBridge {
35
+ /**
36
+ * Get any pending push notification action from native side
37
+ */
38
+ static getPendingPushAction(): Promise<{
39
+ action?: string;
40
+ metadata?: string;
41
+ }>;
42
+ /**
43
+ * Set a pending push notification action to native side
44
+ */
45
+ static setPendingPushAction(action: string, metadata: string): Promise<boolean>;
46
+ /**
47
+ * Get any pending call action from native side (reliable polling pattern)
48
+ */
49
+ static getPendingCallAction(): Promise<{
50
+ action?: string;
51
+ callId?: string;
52
+ timestamp?: number;
53
+ }>;
54
+ /**
55
+ * Clear any pending call action
56
+ */
57
+ static clearPendingCallAction(): Promise<boolean>;
58
+ /**
59
+ * Clear any pending push notification action
60
+ */
61
+ static clearPendingPushAction(): Promise<boolean>;
62
+ /**
63
+ * React Native → Android: End/hang up the current call
64
+ * This will hide the ongoing call notification and notify the native side
65
+ */
66
+ static endCall(callId?: string): Promise<boolean>;
67
+ /**
68
+ * React Native → Android: Show ongoing call notification to keep app alive
69
+ * Should be called when a call becomes active to prevent background termination
70
+ */
71
+ static showOngoingCallNotification(callerName?: string, callerNumber?: string, callId?: string): Promise<boolean>;
72
+ /**
73
+ * React Native → Android: Hide ongoing call notification
74
+ * Should be called when a call ends to clean up notifications
75
+ */
76
+ static hideOngoingCallNotification(): Promise<boolean>;
77
+ /**
78
+ * React Native → Android: Hide incoming call notification
79
+ * Useful for dismissing notifications when call is answered/rejected in app
80
+ */
81
+ static hideIncomingCallNotification(): Promise<boolean>;
82
+ /**
83
+ * Get VoIP token from native storage
84
+ */
85
+ static getVoipToken(): Promise<string | null>;
86
+ /**
87
+ * Get pending VoIP push from native storage
88
+ */
89
+ static getPendingVoipPush(): Promise<string | null>;
90
+ /**
91
+ * Clear pending VoIP push from native storage
92
+ */
93
+ static clearPendingVoipPush(): Promise<boolean>;
94
+ /**
95
+ * Get pending VoIP action from native storage
96
+ */
97
+ static getPendingVoipAction(): Promise<string | null>;
98
+ /**
99
+ * Clear pending VoIP action from native storage
100
+ */
101
+ static clearPendingVoipAction(): Promise<boolean>;
102
+ /**
103
+ * Android → React Native: Listen for immediate call action events from notification buttons
104
+ * Use this for active calls where immediate response is needed (e.g., ending ongoing calls)
105
+ */
106
+ static addCallActionListener(listener: (event: CallActionEvent) => void): EmitterSubscription;
107
+ /**
108
+ * Remove call action listener
109
+ */
110
+ static removeCallActionListener(subscription: EmitterSubscription): void;
111
+ /**
112
+ * Remove all call action listeners
113
+ */
114
+ static removeAllCallActionListeners(): void;
115
+ }
116
+ export { NativeBridge as VoicePnBridgeNative };
@@ -1,5 +1,212 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.VoicePnBridge = void 0;
3
+ exports.VoicePnBridgeNative = exports.VoicePnBridge = void 0;
4
4
  const react_native_1 = require("react-native");
5
- exports.VoicePnBridge = react_native_1.NativeModules.VoicePnBridge;
5
+ const NativeBridge = react_native_1.NativeModules.VoicePnBridge;
6
+ exports.VoicePnBridgeNative = NativeBridge;
7
+ /**
8
+ * Enhanced VoicePnBridge with call control and event handling capabilities
9
+ */
10
+ class VoicePnBridge {
11
+ /**
12
+ * Get any pending push notification action from native side
13
+ */
14
+ static async getPendingPushAction() {
15
+ try {
16
+ const result = await NativeBridge.getPendingPushAction();
17
+ return {
18
+ action: result?.action || undefined,
19
+ metadata: result?.metadata || undefined,
20
+ };
21
+ }
22
+ catch (error) {
23
+ console.error('VoicePnBridge: Error getting pending push action:', error);
24
+ return {};
25
+ }
26
+ }
27
+ /**
28
+ * Set a pending push notification action to native side
29
+ */
30
+ static async setPendingPushAction(action, metadata) {
31
+ try {
32
+ return await NativeBridge.setPendingPushAction(action, metadata);
33
+ }
34
+ catch (error) {
35
+ console.error('VoicePnBridge: Error setting pending push action:', error);
36
+ return false;
37
+ }
38
+ }
39
+ /**
40
+ * Get any pending call action from native side (reliable polling pattern)
41
+ */
42
+ static async getPendingCallAction() {
43
+ try {
44
+ const result = await NativeBridge.getPendingCallAction();
45
+ return {
46
+ action: result?.action || undefined,
47
+ callId: result?.callId || undefined,
48
+ timestamp: result?.timestamp || undefined,
49
+ };
50
+ }
51
+ catch (error) {
52
+ console.error('VoicePnBridge: Error getting pending call action:', error);
53
+ return {};
54
+ }
55
+ }
56
+ /**
57
+ * Clear any pending call action
58
+ */
59
+ static async clearPendingCallAction() {
60
+ try {
61
+ return await NativeBridge.clearPendingCallAction();
62
+ }
63
+ catch (error) {
64
+ console.error('VoicePnBridge: Error clearing pending call action:', error);
65
+ return false;
66
+ }
67
+ }
68
+ /**
69
+ * Clear any pending push notification action
70
+ */
71
+ static async clearPendingPushAction() {
72
+ try {
73
+ return await NativeBridge.clearPendingPushAction();
74
+ }
75
+ catch (error) {
76
+ console.error('VoicePnBridge: Error clearing pending push action:', error);
77
+ return false;
78
+ }
79
+ }
80
+ /**
81
+ * React Native → Android: End/hang up the current call
82
+ * This will hide the ongoing call notification and notify the native side
83
+ */
84
+ static async endCall(callId) {
85
+ try {
86
+ return await NativeBridge.endCall(callId || null);
87
+ }
88
+ catch (error) {
89
+ console.error('VoicePnBridge: Error ending call:', error);
90
+ return false;
91
+ }
92
+ }
93
+ /**
94
+ * React Native → Android: Show ongoing call notification to keep app alive
95
+ * Should be called when a call becomes active to prevent background termination
96
+ */
97
+ static async showOngoingCallNotification(callerName, callerNumber, callId) {
98
+ try {
99
+ return await NativeBridge.showOngoingCallNotification(callerName || null, callerNumber || null, callId || null);
100
+ }
101
+ catch (error) {
102
+ console.error('VoicePnBridge: Error showing ongoing call notification:', error);
103
+ return false;
104
+ }
105
+ }
106
+ /**
107
+ * React Native → Android: Hide ongoing call notification
108
+ * Should be called when a call ends to clean up notifications
109
+ */
110
+ static async hideOngoingCallNotification() {
111
+ try {
112
+ return await NativeBridge.hideOngoingCallNotification();
113
+ }
114
+ catch (error) {
115
+ console.error('VoicePnBridge: Error hiding ongoing call notification:', error);
116
+ return false;
117
+ }
118
+ }
119
+ /**
120
+ * React Native → Android: Hide incoming call notification
121
+ * Useful for dismissing notifications when call is answered/rejected in app
122
+ */
123
+ static async hideIncomingCallNotification() {
124
+ try {
125
+ return await NativeBridge.hideIncomingCallNotification();
126
+ }
127
+ catch (error) {
128
+ console.error('VoicePnBridge: Error hiding incoming call notification:', error);
129
+ return false;
130
+ }
131
+ }
132
+ /**
133
+ * Get VoIP token from native storage
134
+ */
135
+ static async getVoipToken() {
136
+ try {
137
+ return await NativeBridge.getVoipToken();
138
+ }
139
+ catch (error) {
140
+ console.error('VoicePnBridge: Error getting VoIP token:', error);
141
+ return null;
142
+ }
143
+ }
144
+ /**
145
+ * Get pending VoIP push from native storage
146
+ */
147
+ static async getPendingVoipPush() {
148
+ try {
149
+ return await NativeBridge.getPendingVoipPush();
150
+ }
151
+ catch (error) {
152
+ console.error('VoicePnBridge: Error getting pending VoIP push:', error);
153
+ return null;
154
+ }
155
+ }
156
+ /**
157
+ * Clear pending VoIP push from native storage
158
+ */
159
+ static async clearPendingVoipPush() {
160
+ try {
161
+ return await NativeBridge.clearPendingVoipPush();
162
+ }
163
+ catch (error) {
164
+ console.error('VoicePnBridge: Error clearing pending VoIP push:', error);
165
+ return false;
166
+ }
167
+ }
168
+ /**
169
+ * Get pending VoIP action from native storage
170
+ */
171
+ static async getPendingVoipAction() {
172
+ try {
173
+ return await NativeBridge.getPendingVoipAction();
174
+ }
175
+ catch (error) {
176
+ console.error('VoicePnBridge: Error getting pending VoIP action:', error);
177
+ return null;
178
+ }
179
+ }
180
+ /**
181
+ * Clear pending VoIP action from native storage
182
+ */
183
+ static async clearPendingVoipAction() {
184
+ try {
185
+ return await NativeBridge.clearPendingVoipAction();
186
+ }
187
+ catch (error) {
188
+ console.error('VoicePnBridge: Error clearing pending VoIP action:', error);
189
+ return false;
190
+ }
191
+ }
192
+ /**
193
+ * Android → React Native: Listen for immediate call action events from notification buttons
194
+ * Use this for active calls where immediate response is needed (e.g., ending ongoing calls)
195
+ */
196
+ static addCallActionListener(listener) {
197
+ return react_native_1.DeviceEventEmitter.addListener('TelnyxCallAction', listener);
198
+ }
199
+ /**
200
+ * Remove call action listener
201
+ */
202
+ static removeCallActionListener(subscription) {
203
+ subscription.remove();
204
+ }
205
+ /**
206
+ * Remove all call action listeners
207
+ */
208
+ static removeAllCallActionListeners() {
209
+ react_native_1.DeviceEventEmitter.removeAllListeners('TelnyxCallAction');
210
+ }
211
+ }
212
+ exports.VoicePnBridge = VoicePnBridge;
@@ -16,7 +16,9 @@ export declare enum TelnyxCallState {
16
16
  /** Call has ended normally */
17
17
  ENDED = "ENDED",
18
18
  /** Call failed to connect or was rejected */
19
- FAILED = "FAILED"
19
+ FAILED = "FAILED",
20
+ /** Call was dropped due to network issues */
21
+ DROPPED = "DROPPED"
20
22
  }
21
23
  /**
22
24
  * Type guard to check if a value is a valid TelnyxCallState
@@ -22,6 +22,8 @@ var TelnyxCallState;
22
22
  TelnyxCallState["ENDED"] = "ENDED";
23
23
  /** Call failed to connect or was rejected */
24
24
  TelnyxCallState["FAILED"] = "FAILED";
25
+ /** Call was dropped due to network issues */
26
+ TelnyxCallState["DROPPED"] = "DROPPED";
25
27
  })(TelnyxCallState || (exports.TelnyxCallState = TelnyxCallState = {}));
26
28
  /**
27
29
  * Type guard to check if a value is a valid TelnyxCallState
@@ -70,7 +72,9 @@ exports.CallStateHelpers = {
70
72
  * Is the call in a terminated state?
71
73
  */
72
74
  isTerminated(state) {
73
- return state === TelnyxCallState.ENDED || state === TelnyxCallState.FAILED;
75
+ return (state === TelnyxCallState.ENDED ||
76
+ state === TelnyxCallState.FAILED ||
77
+ state === TelnyxCallState.DROPPED);
74
78
  },
75
79
  /**
76
80
  * Is the call in an active state (can have media)?
@@ -13,13 +13,15 @@ export declare class Call {
13
13
  private readonly _callId;
14
14
  private readonly _destination;
15
15
  private readonly _isIncoming;
16
+ private readonly _originalCallerName?;
17
+ private readonly _originalCallerNumber?;
16
18
  private readonly _callState;
17
19
  private readonly _isMuted;
18
20
  private readonly _isHeld;
19
21
  private readonly _duration;
20
22
  private _durationTimer?;
21
23
  private _startTime?;
22
- constructor(_telnyxCall: TelnyxCall, _callId: string, _destination: string, _isIncoming: boolean);
24
+ constructor(_telnyxCall: TelnyxCall, _callId: string, _destination: string, _isIncoming: boolean, isReattached?: boolean, _originalCallerName?: string, _originalCallerNumber?: string);
23
25
  /**
24
26
  * Unique identifier for this call
25
27
  */
@@ -52,6 +54,24 @@ export declare class Call {
52
54
  * Current call duration in seconds (synchronous access)
53
55
  */
54
56
  get currentDuration(): number;
57
+ /**
58
+ * Custom headers received from the WebRTC INVITE message.
59
+ * These headers are passed during call initiation and can contain application-specific information.
60
+ * Format should be [{"name": "X-Header-Name", "value": "Value"}] where header names must start with "X-".
61
+ */
62
+ get inviteCustomHeaders(): {
63
+ name: string;
64
+ value: string;
65
+ }[] | null;
66
+ /**
67
+ * Custom headers received from the WebRTC ANSWER message.
68
+ * These headers are passed during call acceptance and can contain application-specific information.
69
+ * Format should be [{"name": "X-Header-Name", "value": "Value"}] where header names must start with "X-".
70
+ */
71
+ get answerCustomHeaders(): {
72
+ name: string;
73
+ value: string;
74
+ }[] | null;
55
75
  /**
56
76
  * Get the underlying Telnyx Call object (for internal use)
57
77
  * @internal
@@ -91,12 +111,20 @@ export declare class Call {
91
111
  get canResume$(): Observable<boolean>;
92
112
  /**
93
113
  * Answer the incoming call
114
+ * @param customHeaders Optional custom headers to include with the answer
94
115
  */
95
- answer(): Promise<void>;
116
+ answer(customHeaders?: {
117
+ name: string;
118
+ value: string;
119
+ }[]): Promise<void>;
96
120
  /**
97
121
  * Hang up the call
122
+ * @param customHeaders Optional custom headers to include with the hangup request
98
123
  */
99
- hangup(): Promise<void>;
124
+ hangup(customHeaders?: {
125
+ name: string;
126
+ value: string;
127
+ }[]): Promise<void>;
100
128
  /**
101
129
  * Put the call on hold
102
130
  */
@@ -46,15 +46,22 @@ const react_native_1 = require("react-native");
46
46
  * integrate with any state management solution.
47
47
  */
48
48
  class Call {
49
- constructor(_telnyxCall, _callId, _destination, _isIncoming) {
49
+ constructor(_telnyxCall, _callId, _destination, _isIncoming, isReattached = false, _originalCallerName, _originalCallerNumber) {
50
50
  this._telnyxCall = _telnyxCall;
51
51
  this._callId = _callId;
52
52
  this._destination = _destination;
53
53
  this._isIncoming = _isIncoming;
54
+ this._originalCallerName = _originalCallerName;
55
+ this._originalCallerNumber = _originalCallerNumber;
54
56
  this._callState = new rxjs_1.BehaviorSubject(call_state_1.TelnyxCallState.RINGING);
55
57
  this._isMuted = new rxjs_1.BehaviorSubject(false);
56
58
  this._isHeld = new rxjs_1.BehaviorSubject(false);
57
59
  this._duration = new rxjs_1.BehaviorSubject(0);
60
+ // Set initial state based on whether this is a reattached call
61
+ if (isReattached) {
62
+ console.log('Call: Setting initial state to ACTIVE for reattached call');
63
+ this._callState.next(call_state_1.TelnyxCallState.ACTIVE);
64
+ }
58
65
  this._setupCallListeners();
59
66
  }
60
67
  /**
@@ -105,6 +112,22 @@ class Call {
105
112
  get currentDuration() {
106
113
  return this._duration.value;
107
114
  }
115
+ /**
116
+ * Custom headers received from the WebRTC INVITE message.
117
+ * These headers are passed during call initiation and can contain application-specific information.
118
+ * Format should be [{"name": "X-Header-Name", "value": "Value"}] where header names must start with "X-".
119
+ */
120
+ get inviteCustomHeaders() {
121
+ return this._telnyxCall.inviteCustomHeaders;
122
+ }
123
+ /**
124
+ * Custom headers received from the WebRTC ANSWER message.
125
+ * These headers are passed during call acceptance and can contain application-specific information.
126
+ * Format should be [{"name": "X-Header-Name", "value": "Value"}] where header names must start with "X-".
127
+ */
128
+ get answerCustomHeaders() {
129
+ return this._telnyxCall.answerCustomHeaders;
130
+ }
108
131
  /**
109
132
  * Get the underlying Telnyx Call object (for internal use)
110
133
  * @internal
@@ -162,8 +185,9 @@ class Call {
162
185
  }
163
186
  /**
164
187
  * Answer the incoming call
188
+ * @param customHeaders Optional custom headers to include with the answer
165
189
  */
166
- async answer() {
190
+ async answer(customHeaders) {
167
191
  if (!call_state_1.CallStateHelpers.canAnswer(this.currentState)) {
168
192
  throw new Error(`Cannot answer call in state: ${this.currentState}`);
169
193
  }
@@ -180,7 +204,8 @@ class Call {
180
204
  // Fallback for Android or when CallKit is not available
181
205
  console.log('Call: Setting state to CONNECTING before answering');
182
206
  this._callState.next(call_state_1.TelnyxCallState.CONNECTING);
183
- await this._telnyxCall.answer();
207
+ // Pass custom headers to the underlying Telnyx call
208
+ await this._telnyxCall.answer(customHeaders);
184
209
  }
185
210
  catch (error) {
186
211
  console.error('Failed to answer call:', error);
@@ -189,8 +214,9 @@ class Call {
189
214
  }
190
215
  /**
191
216
  * Hang up the call
217
+ * @param customHeaders Optional custom headers to include with the hangup request
192
218
  */
193
- async hangup() {
219
+ async hangup(customHeaders) {
194
220
  if (!call_state_1.CallStateHelpers.canHangup(this.currentState)) {
195
221
  throw new Error(`Cannot hang up call in state: ${this.currentState}`);
196
222
  }
@@ -205,7 +231,19 @@ class Call {
205
231
  }
206
232
  }
207
233
  // Fallback for Android or when CallKit is not available
208
- await this._telnyxCall.hangup();
234
+ await this._telnyxCall.hangup(customHeaders);
235
+ // On Android, also notify the native side to hide ongoing notification
236
+ if (react_native_1.Platform.OS === 'android') {
237
+ try {
238
+ const { VoicePnBridge } = await Promise.resolve().then(() => __importStar(require('../internal/voice-pn-bridge')));
239
+ await VoicePnBridge.endCall(this._callId);
240
+ console.log('Call: Notified Android to hide ongoing notification');
241
+ }
242
+ catch (error) {
243
+ console.error('Call: Failed to notify Android about call end:', error);
244
+ // Don't fail the hangup if notification hiding fails
245
+ }
246
+ }
209
247
  }
210
248
  catch (error) {
211
249
  console.error('Failed to hang up call:', error);
@@ -314,10 +352,68 @@ class Call {
314
352
  // Start duration timer when call becomes active
315
353
  if (telnyxState === call_state_1.TelnyxCallState.ACTIVE && !this._startTime) {
316
354
  this._startDurationTimer();
355
+ // Show ongoing call notification on Android when call becomes active
356
+ // This covers both locally answered calls and calls that become active from remote side
357
+ if (react_native_1.Platform.OS === 'android') {
358
+ (async () => {
359
+ try {
360
+ const { VoicePnBridge } = await Promise.resolve().then(() => __importStar(require('../internal/voice-pn-bridge')));
361
+ // Extract caller information based on call direction
362
+ let callerNumber;
363
+ let callerName;
364
+ if (this._isIncoming) {
365
+ // For incoming calls, use the remote caller ID (who's calling us)
366
+ callerNumber = this._telnyxCall.remoteCallerIdNumber;
367
+ callerName = this._telnyxCall.remoteCallerIdName;
368
+ }
369
+ else {
370
+ // For outgoing calls, use our own caller ID (what we're showing to them)
371
+ // These are the values we set when making the call
372
+ callerNumber = this._telnyxCall.localCallerIdNumber || this._originalCallerNumber;
373
+ callerName = this._telnyxCall.localCallerIdName || this._originalCallerName;
374
+ }
375
+ // Fallback logic for better notification display - avoid "Unknown" when possible
376
+ let displayName;
377
+ let displayNumber;
378
+ if (this._isIncoming) {
379
+ // For incoming calls: use caller name or fall back to caller number, then destination
380
+ displayName = callerName || callerNumber || this._destination;
381
+ displayNumber = callerNumber || this._destination;
382
+ }
383
+ else {
384
+ // For outgoing calls: use our caller ID or descriptive text
385
+ displayName = callerName || `${this._destination}`;
386
+ displayNumber = callerNumber || this._destination;
387
+ }
388
+ await VoicePnBridge.showOngoingCallNotification(displayName, displayNumber, this._callId);
389
+ console.log('Call: Showed ongoing call notification on Android (call active)', {
390
+ isIncoming: this._isIncoming,
391
+ callerName: displayName,
392
+ callerNumber: displayNumber,
393
+ });
394
+ }
395
+ catch (error) {
396
+ console.error('Call: Failed to show ongoing call notification on active:', error);
397
+ }
398
+ })();
399
+ }
317
400
  }
318
401
  // Stop duration timer when call ends
319
402
  if (call_state_1.CallStateHelpers.isTerminated(telnyxState)) {
320
403
  this._stopDurationTimer();
404
+ // Clean up ongoing call notification on Android when call ends
405
+ if (react_native_1.Platform.OS === 'android') {
406
+ (async () => {
407
+ try {
408
+ const { VoicePnBridge } = await Promise.resolve().then(() => __importStar(require('../internal/voice-pn-bridge')));
409
+ await VoicePnBridge.endCall(this._callId);
410
+ console.log('Call: Cleaned up ongoing call notification (call terminated)');
411
+ }
412
+ catch (error) {
413
+ console.error('Call: Failed to clean up ongoing call notification on termination:', error);
414
+ }
415
+ })();
416
+ }
321
417
  }
322
418
  });
323
419
  }
@@ -331,6 +427,8 @@ class Call {
331
427
  case 'ringing':
332
428
  case 'new':
333
429
  return call_state_1.TelnyxCallState.RINGING;
430
+ case 'connecting':
431
+ return call_state_1.TelnyxCallState.CONNECTING;
334
432
  case 'active':
335
433
  case 'answered':
336
434
  return call_state_1.TelnyxCallState.ACTIVE;
@@ -342,6 +440,8 @@ class Call {
342
440
  case 'failed':
343
441
  case 'rejected':
344
442
  return call_state_1.TelnyxCallState.FAILED;
443
+ case 'dropped':
444
+ return call_state_1.TelnyxCallState.DROPPED;
345
445
  default:
346
446
  console.warn(`Unknown call state: ${telnyxState}`);
347
447
  return call_state_1.TelnyxCallState.RINGING;