@telnyx/react-voice-commons-sdk 0.1.5 → 0.1.7-beta.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.
- package/CHANGELOG.md +12 -0
- package/README.md +24 -24
- package/android/build.gradle +45 -0
- package/android/src/main/AndroidManifest.xml +38 -0
- package/android/src/main/java/com/telnyx/react_voice_commons/CallForegroundService.kt +83 -0
- package/android/src/main/java/com/telnyx/react_voice_commons/TelnyxFirebaseMessagingService.kt +179 -0
- package/android/src/main/java/com/telnyx/react_voice_commons/TelnyxMainActivity.kt +216 -0
- package/android/src/main/java/com/telnyx/react_voice_commons/TelnyxNotificationActionReceiver.kt +177 -0
- package/android/src/main/java/com/telnyx/react_voice_commons/TelnyxNotificationHelper.kt +277 -0
- package/android/src/main/java/com/telnyx/react_voice_commons/VoicePnBridgeModule.kt +247 -0
- package/android/src/main/java/com/telnyx/react_voice_commons/VoicePnBridgePackage.kt +17 -0
- package/android/src/main/java/com/telnyx/react_voice_commons/VoicePnManager.kt +154 -0
- package/ios/CallKitBridge.m +1 -1
- package/ios/CallKitBridge.swift +1 -11
- package/ios/README.md +2 -2
- package/lib/callkit/callkit-coordinator.js +6 -2
- package/lib/internal/calls/call-state-controller.d.ts +9 -0
- package/lib/internal/calls/call-state-controller.js +51 -24
- package/lib/telnyx-voice-app.js +127 -151
- package/lib/telnyx-voip-client.d.ts +21 -0
- package/lib/telnyx-voip-client.js +30 -0
- package/package.json +4 -1
- package/src/callkit/callkit-coordinator.ts +8 -2
- package/src/internal/calls/call-state-controller.ts +56 -24
- package/src/telnyx-voice-app.tsx +154 -170
- package/src/telnyx-voip-client.ts +31 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# CHANGELOG.md
|
|
2
2
|
|
|
3
|
+
## [0.1.6](https://github.com/team-telnyx/react-native-voice-commons/releases/tag/0.1.6) (2025-12-09)
|
|
4
|
+
|
|
5
|
+
### Enhancement
|
|
6
|
+
|
|
7
|
+
• Added Android native components to npm package for proper distribution
|
|
8
|
+
• Complete Android native integration support for Firebase messaging and call management
|
|
9
|
+
|
|
10
|
+
### Bug Fixing
|
|
11
|
+
|
|
12
|
+
• Fixed missing Android directory in npm package files array
|
|
13
|
+
• Resolved native component availability issues for Android integrations
|
|
14
|
+
|
|
3
15
|
## [0.1.5](https://github.com/team-telnyx/react-native-voice-commons/releases/tag/0.1.5) (2025-12-08)
|
|
4
16
|
|
|
5
17
|
### Enhancement
|
package/README.md
CHANGED
|
@@ -51,7 +51,7 @@ export default function App() {
|
|
|
51
51
|
|
|
52
52
|
### Core Components
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
### 1. VoIP Client Configuration
|
|
55
55
|
|
|
56
56
|
```tsx
|
|
57
57
|
const voipClient = createTelnyxVoipClient({
|
|
@@ -65,7 +65,7 @@ const voipClient = createTelnyxVoipClient({
|
|
|
65
65
|
- **`enableAppStateManagement: true`** - **Optional (default: true)**: Enables automatic background/foreground app state management. When enabled, the library automatically disconnects when the app goes to background (unless there's an active call) and handles reconnection logic. Set to `false` if you want to handle app lifecycle manually.
|
|
66
66
|
- **`debug: true`** - **Optional**: Enables detailed logging for connection states, call transitions, and push notification processing. Useful for development and troubleshooting.
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
### 2. TelnyxVoiceApp Wrapper
|
|
69
69
|
|
|
70
70
|
The `TelnyxVoiceApp` component handles:
|
|
71
71
|
|
|
@@ -74,7 +74,7 @@ The `TelnyxVoiceApp` component handles:
|
|
|
74
74
|
- Login state management with automatic reconnection
|
|
75
75
|
- Background client management for push notifications
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
### 3. Reactive State Management
|
|
78
78
|
|
|
79
79
|
```tsx
|
|
80
80
|
// Listen to connection state
|
|
@@ -93,7 +93,7 @@ call.callState$.subscribe((state) => {
|
|
|
93
93
|
});
|
|
94
94
|
```
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
### 4. Call Management
|
|
97
97
|
|
|
98
98
|
```tsx
|
|
99
99
|
// Make a call
|
|
@@ -112,9 +112,9 @@ await call.hangup();
|
|
|
112
112
|
|
|
113
113
|
The library supports both credential-based and token-based authentication with automatic persistence for seamless reconnection.
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
### Authentication Methods
|
|
116
116
|
|
|
117
|
-
|
|
117
|
+
### 1. Credential-Based Authentication
|
|
118
118
|
|
|
119
119
|
```tsx
|
|
120
120
|
import { createCredentialConfig } from '@telnyx/react-voice-commons-sdk';
|
|
@@ -127,7 +127,7 @@ const config = createCredentialConfig('your_sip_username', 'your_sip_password',
|
|
|
127
127
|
await voipClient.login(config);
|
|
128
128
|
```
|
|
129
129
|
|
|
130
|
-
|
|
130
|
+
### 2. Token-Based Authentication
|
|
131
131
|
|
|
132
132
|
```tsx
|
|
133
133
|
import { createTokenConfig } from '@telnyx/react-voice-commons-sdk';
|
|
@@ -140,11 +140,11 @@ const config = createTokenConfig('your_jwt_token', {
|
|
|
140
140
|
await voipClient.loginWithToken(config);
|
|
141
141
|
```
|
|
142
142
|
|
|
143
|
-
|
|
143
|
+
### Automatic Storage & Reconnection
|
|
144
144
|
|
|
145
145
|
The library automatically stores authentication data securely for seamless reconnection. **You don't need to manually manage these storage keys** - the library handles everything internally.
|
|
146
146
|
|
|
147
|
-
|
|
147
|
+
### Internal Storage (Managed Automatically)
|
|
148
148
|
|
|
149
149
|
The library uses these AsyncStorage keys internally:
|
|
150
150
|
|
|
@@ -155,7 +155,7 @@ The library uses these AsyncStorage keys internally:
|
|
|
155
155
|
|
|
156
156
|
**Note**: These are managed automatically by the library. You only need to call `login()` once, and the library will handle storage and future reconnections.
|
|
157
157
|
|
|
158
|
-
|
|
158
|
+
### Auto-Reconnection
|
|
159
159
|
|
|
160
160
|
```tsx
|
|
161
161
|
// Automatically reconnects using internally stored credentials or token
|
|
@@ -176,7 +176,7 @@ if (!success) {
|
|
|
176
176
|
|
|
177
177
|
**Demo App Note**: When using the library in a demo application, the `TelnyxLoginForm` component may do additional storage for UI convenience (pre-filling login forms). This is separate from the library's internal authentication storage and is not required for production apps.
|
|
178
178
|
|
|
179
|
-
|
|
179
|
+
### Manual Storage Management (Advanced Use Only)
|
|
180
180
|
|
|
181
181
|
If you need to clear stored authentication data manually:
|
|
182
182
|
|
|
@@ -198,9 +198,9 @@ await AsyncStorage.multiRemove([
|
|
|
198
198
|
|
|
199
199
|
The library provides complete native integration for both platforms. These integrations are required for production apps using the library.
|
|
200
200
|
|
|
201
|
-
|
|
201
|
+
### Android Integration
|
|
202
202
|
|
|
203
|
-
|
|
203
|
+
### 1. MainActivity Setup
|
|
204
204
|
|
|
205
205
|
Your app's MainActivity should extend `TelnyxMainActivity` for automatic push notification handling:
|
|
206
206
|
|
|
@@ -225,7 +225,7 @@ The `TelnyxMainActivity` provides:
|
|
|
225
225
|
- Proper lifecycle management for VoIP functionality
|
|
226
226
|
- Integration with `VoicePnManager` for push notification state
|
|
227
227
|
|
|
228
|
-
|
|
228
|
+
### 2. Push Notification Setup
|
|
229
229
|
|
|
230
230
|
1. Place `google-services.json` in the project root
|
|
231
231
|
2. Register background message handler:
|
|
@@ -239,9 +239,9 @@ messaging().setBackgroundMessageHandler(async (remoteMessage) => {
|
|
|
239
239
|
});
|
|
240
240
|
```
|
|
241
241
|
|
|
242
|
-
|
|
242
|
+
### iOS Integration
|
|
243
243
|
|
|
244
|
-
|
|
244
|
+
### 1. AppDelegate Setup
|
|
245
245
|
|
|
246
246
|
Your AppDelegate only needs to implement `PKPushRegistryDelegate` for VoIP push notifications. CallKit integration is automatically handled by CallBridge:
|
|
247
247
|
|
|
@@ -277,13 +277,13 @@ public class AppDelegate: ExpoAppDelegate, PKPushRegistryDelegate {
|
|
|
277
277
|
|
|
278
278
|
**Note**: CallKit integration (CXProvider, CXProviderDelegate, audio session management) is automatically handled by the internal CallBridge component. You don't need to implement any CallKit delegate methods manually.
|
|
279
279
|
|
|
280
|
-
|
|
280
|
+
### 2. VoIP Push Certificate Setup
|
|
281
281
|
|
|
282
282
|
- Configure VoIP push certificates in your Apple Developer account
|
|
283
283
|
- The `TelnyxVoipPushHandler` automatically handles token registration and push processing
|
|
284
284
|
- CallKit integration is automatically managed by CallBridge - no manual setup required
|
|
285
285
|
|
|
286
|
-
|
|
286
|
+
### Key Native Features Integrated
|
|
287
287
|
|
|
288
288
|
1. **Push Notification Handling**: Both platforms handle background push notifications properly
|
|
289
289
|
2. **Native Call UI**: CallKit (iOS, managed by CallBridge) and ConnectionService (Android) integration
|
|
@@ -416,15 +416,15 @@ npx expo run:ios
|
|
|
416
416
|
|
|
417
417
|
### Common Integration Issues
|
|
418
418
|
|
|
419
|
-
|
|
419
|
+
### Double Login
|
|
420
420
|
|
|
421
421
|
Ensure you're not calling login methods manually when using `TelnyxVoiceApp` with auto-reconnection enabled.
|
|
422
422
|
|
|
423
|
-
|
|
423
|
+
### Background Disconnection
|
|
424
424
|
|
|
425
425
|
Check if `enableAutoReconnect` is set appropriately for your use case in the `TelnyxVoiceApp` configuration.
|
|
426
426
|
|
|
427
|
-
|
|
427
|
+
### Push Notifications Not Working
|
|
428
428
|
|
|
429
429
|
- **Android**:
|
|
430
430
|
- Verify `google-services.json` is in the correct location and Firebase is properly configured
|
|
@@ -436,19 +436,19 @@ Check if `enableAutoReconnect` is set appropriately for your use case in the `Te
|
|
|
436
436
|
- Check that `TelnyxVoipPushHandler.initializeVoipRegistration()` is called in `didFinishLaunchingWithOptions`
|
|
437
437
|
- **Both**: Check that background message handlers are properly registered
|
|
438
438
|
|
|
439
|
-
|
|
439
|
+
### Native Integration Issues
|
|
440
440
|
|
|
441
441
|
- **Android**: Ensure MainActivity extends `TelnyxMainActivity` for proper intent handling
|
|
442
442
|
- **iOS**: Verify AppDelegate implements `PKPushRegistryDelegate` and delegates to `TelnyxVoipPushHandler`
|
|
443
443
|
- **CallKit**: On iOS, CallKit integration is automatically handled by CallBridge - no manual setup required
|
|
444
444
|
|
|
445
|
-
|
|
445
|
+
### Audio Issues
|
|
446
446
|
|
|
447
447
|
- **iOS**: Audio session management is automatically handled by CallBridge
|
|
448
448
|
- **Android**: Verify ConnectionService is properly configured for audio routing
|
|
449
449
|
- **Both**: Ensure proper audio permissions are granted
|
|
450
450
|
|
|
451
|
-
|
|
451
|
+
### Memory Leaks
|
|
452
452
|
|
|
453
453
|
Ensure you're unsubscribing from RxJS observables in your React components:
|
|
454
454
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
apply plugin: 'com.android.library'
|
|
2
|
+
apply plugin: 'kotlin-android'
|
|
3
|
+
|
|
4
|
+
android {
|
|
5
|
+
namespace 'com.telnyx.react_voice_commons'
|
|
6
|
+
compileSdk 34
|
|
7
|
+
|
|
8
|
+
defaultConfig {
|
|
9
|
+
minSdk 21
|
|
10
|
+
targetSdk 34
|
|
11
|
+
versionCode 1
|
|
12
|
+
versionName "1.0"
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
compileOptions {
|
|
16
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
17
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
kotlinOptions {
|
|
21
|
+
jvmTarget = "17"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
sourceSets {
|
|
25
|
+
main {
|
|
26
|
+
java.srcDirs += 'voice-pn-module/src/main/java'
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
buildTypes {
|
|
31
|
+
release {
|
|
32
|
+
minifyEnabled false
|
|
33
|
+
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
dependencies {
|
|
39
|
+
implementation "com.google.firebase:firebase-messaging:23.1.2"
|
|
40
|
+
implementation "androidx.core:core-ktx:1.8.0"
|
|
41
|
+
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.8.21"
|
|
42
|
+
|
|
43
|
+
// React Native dependencies
|
|
44
|
+
implementation "com.facebook.react:react-native:+"
|
|
45
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
3
|
+
|
|
4
|
+
<!-- Basic permissions that this module needs -->
|
|
5
|
+
<uses-permission android:name="android.permission.INTERNET" />
|
|
6
|
+
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
|
7
|
+
<uses-permission android:name="android.permission.VIBRATE" />
|
|
8
|
+
|
|
9
|
+
<!-- WebRTC and voice call permissions -->
|
|
10
|
+
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
|
11
|
+
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
|
12
|
+
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
|
13
|
+
|
|
14
|
+
<!-- Background execution permissions for call handling -->
|
|
15
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
|
16
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL" />
|
|
17
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
|
|
18
|
+
|
|
19
|
+
<!-- Call management permissions -->
|
|
20
|
+
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
|
|
21
|
+
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
|
|
22
|
+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
|
23
|
+
|
|
24
|
+
<!-- Hardware features -->
|
|
25
|
+
<uses-feature
|
|
26
|
+
android:name="android.hardware.microphone"
|
|
27
|
+
android:required="true" />
|
|
28
|
+
|
|
29
|
+
<application>
|
|
30
|
+
<!-- Foreground service for keeping WebRTC connections alive during calls -->
|
|
31
|
+
<service
|
|
32
|
+
android:name=".CallForegroundService"
|
|
33
|
+
android:enabled="true"
|
|
34
|
+
android:exported="false"
|
|
35
|
+
android:foregroundServiceType="phoneCall|microphone" />
|
|
36
|
+
</application>
|
|
37
|
+
|
|
38
|
+
</manifest>
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
package com.telnyx.react_voice_commons
|
|
2
|
+
|
|
3
|
+
import android.app.Service
|
|
4
|
+
import android.content.Intent
|
|
5
|
+
import android.os.IBinder
|
|
6
|
+
import android.util.Log
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Foreground service to keep WebRTC connections alive during calls
|
|
10
|
+
* This service prevents the system from killing the app during active calls
|
|
11
|
+
*/
|
|
12
|
+
class CallForegroundService : Service() {
|
|
13
|
+
companion object {
|
|
14
|
+
private const val TAG = "CallForegroundService"
|
|
15
|
+
const val ACTION_START_FOREGROUND_SERVICE = "START_FOREGROUND_SERVICE"
|
|
16
|
+
const val ACTION_STOP_FOREGROUND_SERVICE = "STOP_FOREGROUND_SERVICE"
|
|
17
|
+
|
|
18
|
+
const val EXTRA_CALLER_NAME = "caller_name"
|
|
19
|
+
const val EXTRA_CALLER_NUMBER = "caller_number"
|
|
20
|
+
const val EXTRA_CALL_ID = "call_id"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
override fun onBind(intent: Intent?): IBinder? {
|
|
24
|
+
return null
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
|
28
|
+
when (intent?.action) {
|
|
29
|
+
ACTION_START_FOREGROUND_SERVICE -> {
|
|
30
|
+
Log.d(TAG, "Starting foreground service for call")
|
|
31
|
+
startAsForegroundService(intent)
|
|
32
|
+
}
|
|
33
|
+
ACTION_STOP_FOREGROUND_SERVICE -> {
|
|
34
|
+
Log.d(TAG, "Stopping foreground service")
|
|
35
|
+
stopAsForegroundService()
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Restart the service if it's killed by the system
|
|
40
|
+
return START_STICKY
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private fun startAsForegroundService(intent: Intent) {
|
|
44
|
+
val callerName = intent.getStringExtra(EXTRA_CALLER_NAME) ?: "Unknown Caller"
|
|
45
|
+
val callerNumber = intent.getStringExtra(EXTRA_CALLER_NUMBER) ?: ""
|
|
46
|
+
val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: "unknown"
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
// Create ongoing call notification
|
|
50
|
+
val notificationHelper = TelnyxNotificationHelper(this)
|
|
51
|
+
val notification = notificationHelper.createOngoingCallNotification(callerName, callerNumber, callId)
|
|
52
|
+
|
|
53
|
+
// Start foreground service with the notification
|
|
54
|
+
startForeground(TelnyxNotificationHelper.ONGOING_CALL_NOTIFICATION_ID, notification)
|
|
55
|
+
|
|
56
|
+
Log.d(TAG, "Foreground service started with ongoing call notification")
|
|
57
|
+
} catch (e: Exception) {
|
|
58
|
+
Log.e(TAG, "Error starting foreground service", e)
|
|
59
|
+
// Stop the service if we can't create the notification
|
|
60
|
+
stopSelf()
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private fun stopAsForegroundService() {
|
|
65
|
+
try {
|
|
66
|
+
// Hide the ongoing call notification
|
|
67
|
+
TelnyxNotificationHelper.hideOngoingCallNotificationFromContext(this)
|
|
68
|
+
|
|
69
|
+
// Stop foreground service
|
|
70
|
+
stopForeground(true)
|
|
71
|
+
stopSelf()
|
|
72
|
+
|
|
73
|
+
Log.d(TAG, "Foreground service stopped")
|
|
74
|
+
} catch (e: Exception) {
|
|
75
|
+
Log.e(TAG, "Error stopping foreground service", e)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
override fun onDestroy() {
|
|
80
|
+
super.onDestroy()
|
|
81
|
+
Log.d(TAG, "CallForegroundService destroyed")
|
|
82
|
+
}
|
|
83
|
+
}
|
package/android/src/main/java/com/telnyx/react_voice_commons/TelnyxFirebaseMessagingService.kt
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
package com.telnyx.react_voice_commons
|
|
2
|
+
|
|
3
|
+
import android.util.Log
|
|
4
|
+
import com.google.firebase.messaging.FirebaseMessagingService
|
|
5
|
+
import com.google.firebase.messaging.RemoteMessage
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* FCM Service for handling Telnyx voice push notifications
|
|
9
|
+
* This service should be extended by the main app's FCM service
|
|
10
|
+
*/
|
|
11
|
+
open class TelnyxFirebaseMessagingService : FirebaseMessagingService() {
|
|
12
|
+
private val tag = "TelnyxFCMService"
|
|
13
|
+
|
|
14
|
+
override fun onMessageReceived(remoteMessage: RemoteMessage) {
|
|
15
|
+
super.onMessageReceived(remoteMessage)
|
|
16
|
+
|
|
17
|
+
Log.d(tag, "FCM message received from: ${remoteMessage.from}")
|
|
18
|
+
Log.d(tag, "FCM message data: ${remoteMessage.data}")
|
|
19
|
+
|
|
20
|
+
// Check if this is a Telnyx voice push notification
|
|
21
|
+
val isTelnyxVoiceMessage = remoteMessage.data.containsKey("metadata") ||
|
|
22
|
+
remoteMessage.data.containsKey("caller_id_name") ||
|
|
23
|
+
remoteMessage.data.containsKey("caller_name") ||
|
|
24
|
+
remoteMessage.data.containsKey("call_id") ||
|
|
25
|
+
remoteMessage.data.containsKey("telnyx_call_id") ||
|
|
26
|
+
remoteMessage.data.get("message_type") == "voice" ||
|
|
27
|
+
remoteMessage.data.get("message") == "Incoming call!" ||
|
|
28
|
+
remoteMessage.data.get("message") == "Missed call!"
|
|
29
|
+
|
|
30
|
+
if (isTelnyxVoiceMessage) {
|
|
31
|
+
Log.d(tag, "Handling as Telnyx voice push notification")
|
|
32
|
+
|
|
33
|
+
// Check if this is a missed call notification
|
|
34
|
+
val isMissedCall = remoteMessage.data.get("message") == "Missed call!"
|
|
35
|
+
if (isMissedCall) {
|
|
36
|
+
handleTelnyxMissedCall(remoteMessage)
|
|
37
|
+
} else {
|
|
38
|
+
handleTelnyxVoicePush(remoteMessage)
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
Log.d(tag, "Non-Telnyx message, delegating to handleNonTelnyxMessage")
|
|
42
|
+
handleNonTelnyxMessage(remoteMessage)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
override fun onNewToken(token: String) {
|
|
47
|
+
super.onNewToken(token)
|
|
48
|
+
Log.d(tag, "FCM token refreshed: $token")
|
|
49
|
+
handleTokenRefresh(token)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Handle Telnyx missed call notifications
|
|
54
|
+
*/
|
|
55
|
+
protected open fun handleTelnyxMissedCall(remoteMessage: RemoteMessage) {
|
|
56
|
+
try {
|
|
57
|
+
Log.d(tag, "Processing Telnyx missed call notification")
|
|
58
|
+
|
|
59
|
+
// Extract caller information from metadata or top-level data
|
|
60
|
+
var callId = "unknown"
|
|
61
|
+
var callerName = "Unknown Caller"
|
|
62
|
+
var callerNumber = ""
|
|
63
|
+
|
|
64
|
+
// First try to parse metadata JSON if it exists
|
|
65
|
+
val metadataJson = remoteMessage.data["metadata"]
|
|
66
|
+
if (metadataJson != null) {
|
|
67
|
+
try {
|
|
68
|
+
Log.d(tag, "Parsing missed call metadata JSON: $metadataJson")
|
|
69
|
+
val metadata = org.json.JSONObject(metadataJson)
|
|
70
|
+
callId = metadata.optString("call_id", "unknown")
|
|
71
|
+
callerName = metadata.optString("caller_name", "Unknown Caller")
|
|
72
|
+
callerNumber = metadata.optString("caller_number", "")
|
|
73
|
+
Log.d(tag, "Extracted from metadata - callId: $callId, callerName: $callerName, callerNumber: $callerNumber")
|
|
74
|
+
} catch (e: Exception) {
|
|
75
|
+
Log.e(tag, "Error parsing missed call metadata JSON", e)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Fallback to top-level data if metadata parsing failed or doesn't exist
|
|
80
|
+
if (callId == "unknown") {
|
|
81
|
+
callId = remoteMessage.data["call_id"] ?: remoteMessage.data["telnyx_call_id"] ?: "unknown"
|
|
82
|
+
callerName = remoteMessage.data["caller_id_name"] ?: remoteMessage.data["caller_name"] ?: "Unknown Caller"
|
|
83
|
+
callerNumber = remoteMessage.data["caller_id_number"] ?: remoteMessage.data["caller_number"] ?: ""
|
|
84
|
+
Log.d(tag, "Using top-level data for missed call - callId: $callId, callerName: $callerName, callerNumber: $callerNumber")
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Show missed call notification (this will hide any existing incoming call notification)
|
|
88
|
+
val notificationHelper = TelnyxNotificationHelper(this)
|
|
89
|
+
notificationHelper.showMissedCallNotification(callerName, callerNumber, callId)
|
|
90
|
+
|
|
91
|
+
Log.d(tag, "Telnyx missed call notification processed successfully")
|
|
92
|
+
} catch (e: Exception) {
|
|
93
|
+
Log.e(tag, "Error handling Telnyx missed call notification", e)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Handle Telnyx voice push notifications
|
|
99
|
+
*/
|
|
100
|
+
protected open fun handleTelnyxVoicePush(remoteMessage: RemoteMessage) {
|
|
101
|
+
try {
|
|
102
|
+
Log.d(tag, "Processing Telnyx voice push using VoicePnManager")
|
|
103
|
+
|
|
104
|
+
// Extract caller information from metadata or top-level data
|
|
105
|
+
var callId = "unknown"
|
|
106
|
+
var callerName = "Unknown Caller"
|
|
107
|
+
var callerNumber = ""
|
|
108
|
+
|
|
109
|
+
// First try to parse metadata JSON if it exists
|
|
110
|
+
val metadataJson = remoteMessage.data["metadata"]
|
|
111
|
+
if (metadataJson != null) {
|
|
112
|
+
try {
|
|
113
|
+
Log.d(tag, "Parsing metadata JSON: $metadataJson")
|
|
114
|
+
val metadata = org.json.JSONObject(metadataJson)
|
|
115
|
+
callId = metadata.optString("call_id", "unknown")
|
|
116
|
+
callerName = metadata.optString("caller_name", "Unknown Caller")
|
|
117
|
+
callerNumber = metadata.optString("caller_number", "")
|
|
118
|
+
Log.d(tag, "Extracted from metadata - callId: $callId, callerName: $callerName, callerNumber: $callerNumber")
|
|
119
|
+
} catch (e: Exception) {
|
|
120
|
+
Log.e(tag, "Error parsing metadata JSON", e)
|
|
121
|
+
// Fall back to top-level data if metadata parsing fails
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Fallback to top-level data if metadata parsing failed or doesn't exist
|
|
126
|
+
if (callId == "unknown") {
|
|
127
|
+
callId = remoteMessage.data["call_id"] ?: remoteMessage.data["telnyx_call_id"] ?: "unknown"
|
|
128
|
+
callerName = remoteMessage.data["caller_id_name"] ?: remoteMessage.data["caller_name"] ?: "Unknown Caller"
|
|
129
|
+
callerNumber = remoteMessage.data["caller_id_number"] ?: remoteMessage.data["caller_number"] ?: ""
|
|
130
|
+
Log.d(tag, "Using top-level data - callId: $callId, callerName: $callerName, callerNumber: $callerNumber")
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
var metadata = ""
|
|
135
|
+
|
|
136
|
+
// Extract metadata using the same approach as official Telnyx service
|
|
137
|
+
try {
|
|
138
|
+
val params = remoteMessage.data
|
|
139
|
+
val objects = org.json.JSONObject(params as Map<*, *>)
|
|
140
|
+
metadata = objects.getString( "metadata")
|
|
141
|
+
|
|
142
|
+
Log.d(tag, "Extracted FCM metadata string: $metadata")
|
|
143
|
+
} catch (e: Exception) {
|
|
144
|
+
Log.e(tag, "Error extracting metadata from FCM data: ${e.message}")
|
|
145
|
+
|
|
146
|
+
// Fallback to simple JSON if metadata extraction fails
|
|
147
|
+
metadata = org.json.JSONObject().apply {
|
|
148
|
+
put("call_id", callId)
|
|
149
|
+
put("caller_name", callerName)
|
|
150
|
+
put("caller_number", callerNumber)
|
|
151
|
+
}.toString()
|
|
152
|
+
|
|
153
|
+
Log.d(tag, "Using fallback metadata: $metadata")
|
|
154
|
+
}
|
|
155
|
+
//VoicePnManager.setPendingPushAction(this, "incoming_call", metadata)
|
|
156
|
+
|
|
157
|
+
// Show notification using dynamic class resolution
|
|
158
|
+
val notificationHelper = TelnyxNotificationHelper(this)
|
|
159
|
+
notificationHelper.showIncomingCallNotification(callerName, callerNumber, callId, metadata)
|
|
160
|
+
Log.d(tag, "Telnyx voice push processed successfully")
|
|
161
|
+
} catch (e: Exception) {
|
|
162
|
+
Log.e(tag, "Error handling Telnyx voice push notification", e)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Handle non-Telnyx messages - override this in your app's FCM service if needed
|
|
168
|
+
*/
|
|
169
|
+
protected open fun handleNonTelnyxMessage(remoteMessage: RemoteMessage) {
|
|
170
|
+
Log.d(tag, "Default non-Telnyx message handler - override this method if needed")
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Handle FCM token refresh - override this in your app's FCM service if needed
|
|
175
|
+
*/
|
|
176
|
+
protected open fun handleTokenRefresh(token: String) {
|
|
177
|
+
Log.d(tag, "Default token refresh handler - override this method if needed")
|
|
178
|
+
}
|
|
179
|
+
}
|