@capgo/capacitor-twilio-voice 7.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 (31) hide show
  1. package/CapgoCapacitorTwilioVoice.podspec +19 -0
  2. package/Package.swift +35 -0
  3. package/README.md +280 -0
  4. package/android/build.gradle +65 -0
  5. package/android/src/main/AndroidManifest.xml +43 -0
  6. package/android/src/main/java/ee/forgr/capacitor_twilio_voice/CapacitorTwilioVoicePlugin.java +1230 -0
  7. package/android/src/main/java/ee/forgr/capacitor_twilio_voice/NotificationActionReceiver.java +64 -0
  8. package/android/src/main/java/ee/forgr/capacitor_twilio_voice/VoiceCallService.java +503 -0
  9. package/android/src/main/java/ee/forgr/capacitor_twilio_voice/VoiceFirebaseMessagingService.java +81 -0
  10. package/android/src/main/res/.gitkeep +0 -0
  11. package/android/src/main/res/drawable/ic_notification_call.xml +11 -0
  12. package/android/src/main/res/drawable/ic_phone_accept.xml +11 -0
  13. package/android/src/main/res/drawable/ic_phone_reject.xml +11 -0
  14. package/dist/docs.json +411 -0
  15. package/dist/esm/definitions.d.ts +95 -0
  16. package/dist/esm/definitions.js +2 -0
  17. package/dist/esm/definitions.js.map +1 -0
  18. package/dist/esm/index.d.ts +4 -0
  19. package/dist/esm/index.js +7 -0
  20. package/dist/esm/index.js.map +1 -0
  21. package/dist/esm/web.d.ts +62 -0
  22. package/dist/esm/web.js +45 -0
  23. package/dist/esm/web.js.map +1 -0
  24. package/dist/plugin.cjs.js +59 -0
  25. package/dist/plugin.cjs.js.map +1 -0
  26. package/dist/plugin.js +62 -0
  27. package/dist/plugin.js.map +1 -0
  28. package/ios/Sources/CapacitorTwilioVoicePlugin/CapacitorTwilioVoice.swift +8 -0
  29. package/ios/Sources/CapacitorTwilioVoicePlugin/CapacitorTwilioVoicePlugin.swift +922 -0
  30. package/ios/Tests/CapacitorTwilioVoicePluginTests/CapacitorTwilioVoicePluginTests.swift +15 -0
  31. package/package.json +81 -0
@@ -0,0 +1,19 @@
1
+ require 'json'
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = 'CapgoCapacitorTwilioVoice'
7
+ s.version = package['version']
8
+ s.summary = package['description']
9
+ s.license = package['license']
10
+ s.homepage = package['repository']['url']
11
+ s.author = package['author']
12
+ s.source = { :git => package['repository']['url'], :tag => s.version.to_s }
13
+ s.source_files = 'ios/Sources/**/*.{swift,h,m,c,cc,mm,cpp}'
14
+ s.ios.deployment_target = '14.0'
15
+ s.dependency 'Capacitor'
16
+ s.dependency 'TwilioVoice', '~> 6.13'
17
+ s.frameworks = 'PushKit', 'CallKit', 'AVFoundation'
18
+ s.swift_version = '5.1'
19
+ end
package/Package.swift ADDED
@@ -0,0 +1,35 @@
1
+ // swift-tools-version: 5.9
2
+ import PackageDescription
3
+
4
+ let package = Package(
5
+ name: "CapgoCapacitorTwilioVoice",
6
+ platforms: [.iOS(.v14)],
7
+ products: [
8
+ .library(
9
+ name: "CapgoCapacitorTwilioVoice",
10
+ targets: ["CapacitorTwilioVoicePlugin"])
11
+ ],
12
+ dependencies: [
13
+ .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "7.0.0"),
14
+ .package(url: "https://github.com/twilio/twilio-voice-ios", from: "6.13.0")
15
+ ],
16
+ targets: [
17
+ .target(
18
+ name: "CapacitorTwilioVoicePlugin",
19
+ dependencies: [
20
+ .product(name: "Capacitor", package: "capacitor-swift-pm"),
21
+ .product(name: "Cordova", package: "capacitor-swift-pm"),
22
+ .product(name: "TwilioVoice", package: "twilio-voice-ios")
23
+ ],
24
+ path: "ios/Sources/CapacitorTwilioVoicePlugin",
25
+ linkerSettings: [
26
+ .linkedFramework("PushKit"),
27
+ .linkedFramework("CallKit"),
28
+ .linkedFramework("AVFoundation")
29
+ ]),
30
+ .testTarget(
31
+ name: "CapacitorTwilioVoicePluginTests",
32
+ dependencies: ["CapacitorTwilioVoicePlugin"],
33
+ path: "ios/Tests/CapacitorTwilioVoicePluginTests")
34
+ ]
35
+ )
package/README.md ADDED
@@ -0,0 +1,280 @@
1
+ ## Capacitor Twilio Voice Plugin
2
+ <a href="https://capgo.app/"><img src='https://raw.githubusercontent.com/Cap-go/capgo/main/assets/capgo_banner.png' alt='Capgo - Instant updates for capacitor'/></a>
3
+
4
+ <div align="center">
5
+ <h2><a href="https://capgo.app/?ref=plugin"> ➡️ Get Instant updates for your App with Capgo</a></h2>
6
+ <h2><a href="https://capgo.app/consulting/?ref=plugin"> Missing a feature? We’ll build the plugin for you 💪</a></h2>
7
+ </div>
8
+
9
+ A Capacitor plugin for integrating Twilio Voice calling functionality into iOS and Android applications.
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm install @capgo/capacitor-twilio-voice
15
+ npx cap sync
16
+ ```
17
+
18
+ ## iOS Setup
19
+
20
+ ### 1. Install Dependencies
21
+
22
+ #### Option A: CocoaPods (Recommended)
23
+ Add to your app's `ios/App/Podfile`:
24
+ ```ruby
25
+ pod 'TwilioVoice', '~> 6.13'
26
+ ```
27
+
28
+ #### Option B: Swift Package Manager
29
+ Add to your `Package.swift`:
30
+ ```swift
31
+ dependencies: [
32
+ .package(url: "https://github.com/twilio/twilio-voice-ios", from: "6.13.0")
33
+ ]
34
+ ```
35
+
36
+ ### 2. Configure Info.plist
37
+ Add to `ios/App/App/Info.plist`:
38
+ ```xml
39
+ <key>NSMicrophoneUsageDescription</key>
40
+ <string>This app uses the microphone for voice calls</string>
41
+
42
+ <key>UIBackgroundModes</key>
43
+ <array>
44
+ <string>voip</string>
45
+ <string>audio</string>
46
+ </array>
47
+ ```
48
+
49
+ ### 3. Enable Push Notifications Capability
50
+ In Xcode, select your app target → Signing & Capabilities → Add Capability → Push Notifications
51
+
52
+ ### 4. PushKit Setup
53
+ PushKit is automatically configured by the plugin. No additional setup required.
54
+
55
+ ## Android Setup
56
+
57
+ ### 1. Firebase Setup
58
+ Add Firebase to your Android project:
59
+
60
+ 1. Add `google-services.json` to `android/app/`
61
+ 2. Update `android/app/build.gradle`:
62
+ ```gradle
63
+ apply plugin: 'com.google.gms.google-services'
64
+
65
+ dependencies {
66
+ // ... existing dependencies
67
+ implementation 'com.google.firebase:firebase-messaging:25.0.0'
68
+ implementation platform('com.google.firebase:firebase-bom:34.0.0')
69
+ implementation 'com.twilio:audioswitch:1.2.2'
70
+ implementation 'androidx.core:core:1.13.1'
71
+ implementation 'androidx.media:media:1.6.0'
72
+ }
73
+ ```
74
+
75
+ 3. Update project-level `android/build.gradle`:
76
+ ```gradle
77
+ buildscript {
78
+ dependencies {
79
+ classpath 'com.google.gms:google-services:4.4.2'
80
+ }
81
+ }
82
+ ```
83
+
84
+ ### 2. AndroidManifest.xml
85
+ Required permissions are automatically added by the plugin:
86
+ - `RECORD_AUDIO`
87
+ - `INTERNET`
88
+ - `ACCESS_NETWORK_STATE`
89
+ - `WAKE_LOCK`
90
+ - `USE_FULL_SCREEN_INTENT`
91
+ - `VIBRATE`
92
+ - `SYSTEM_ALERT_WINDOW`
93
+ - `FOREGROUND_SERVICE`
94
+
95
+ ### 3. Firebase Messaging Service
96
+ The plugin automatically registers a Firebase Messaging Service to handle incoming calls.
97
+
98
+ ### 4. Android Notification Features 📱
99
+ The Android implementation includes comprehensive notification support:
100
+
101
+ - **🔔 System Notifications**: Incoming calls appear as proper Android notifications with caller information
102
+ - **📱 Full-Screen Intent**: Calls can launch the app on lock screen for immediate visibility
103
+ - **🎵 Ringtone & Vibration**: Uses system default ringtone with vibration pattern for audio/haptic alerts
104
+ - **⚡ Action Buttons**: Accept/Reject buttons directly in notification for quick response
105
+ - **🔕 Auto-Dismiss**: Notifications automatically clear when calls end or are answered
106
+ - **🌙 Do Not Disturb**: Respects system notification settings and priority modes
107
+
108
+ **Note**: Notification functionality requires notification permissions. The app will request these permissions during login if not already granted.
109
+
110
+ ## Usage
111
+
112
+ ### Authentication
113
+
114
+ #### `login(options: { accessToken: string })`
115
+ Authenticates with Twilio using a JWT access token:
116
+ - Validates token expiration automatically
117
+ - Stores token securely for app restarts
118
+ - Registers for VoIP push notifications
119
+ - **Note**: The plugin will reject expired tokens
120
+
121
+ #### `logout()`
122
+ Logs out the current user and cleans up all session data:
123
+ - Unregisters from VoIP push notifications
124
+ - Clears stored access tokens
125
+ - Ends any active calls
126
+ - Resets all call state
127
+
128
+ #### `isLoggedIn()`
129
+ Checks if user is currently logged in with a valid (non-expired) token.
130
+ Returns: `{ isLoggedIn: boolean, hasValidToken: boolean, identity?: string }`
131
+
132
+ The `identity` field contains the user identity extracted from the JWT token if logged in.
133
+
134
+ #### `makeCall(options: { to: string })`
135
+ Initiates an outgoing call. Requires prior authentication via `login()`.
136
+
137
+ #### `acceptCall(options: { callSid: string })`
138
+ Accepts an incoming call.
139
+
140
+ #### `rejectCall(options: { callSid: string })`
141
+ Rejects an incoming call.
142
+
143
+ #### `endCall(options?: { callSid?: string })`
144
+ Ends the active call or a specific call.
145
+
146
+ #### `muteCall(options: { muted: boolean, callSid?: string })`
147
+ Mutes or unmutes the microphone.
148
+
149
+ #### `setSpeaker(options: { enabled: boolean })`
150
+ Enables or disables the speaker. On Android, uses Twilio AudioSwitch to manage audio routing between earpiece, speaker, and connected devices (headsets, Bluetooth, etc.).
151
+
152
+ #### `getCallStatus()`
153
+ Gets the current call status.
154
+
155
+ #### `checkMicrophonePermission()`
156
+ Checks if microphone permission is granted.
157
+
158
+ #### `requestMicrophonePermission()`
159
+ Requests microphone permission from the user.
160
+
161
+ ### Event Listeners
162
+
163
+ ```typescript
164
+ import { CapacitorTwilioVoice } from '@capgo/capacitor-twilio-voice';
165
+
166
+ // Registration events
167
+ CapacitorTwilioVoice.addListener('registrationSuccess', (data) => {
168
+ console.log('Successfully registered:', data);
169
+ });
170
+
171
+ CapacitorTwilioVoice.addListener('registrationFailure', (data) => {
172
+ console.error('Registration failed:', data);
173
+ });
174
+
175
+ // Call events
176
+ CapacitorTwilioVoice.addListener('callInviteReceived', (data) => {
177
+ console.log('Incoming call from:', data.from);
178
+ });
179
+
180
+ CapacitorTwilioVoice.addListener('callConnected', (data) => {
181
+ console.log('Call connected:', data);
182
+ });
183
+
184
+ CapacitorTwilioVoice.addListener('callDisconnected', (data) => {
185
+ console.log('Call ended:', data);
186
+ });
187
+
188
+ CapacitorTwilioVoice.addListener('callRinging', (data) => {
189
+ console.log('Call is ringing:', data);
190
+ });
191
+
192
+ CapacitorTwilioVoice.addListener('callReconnecting', (data) => {
193
+ console.log('Call reconnecting:', data);
194
+ });
195
+
196
+ CapacitorTwilioVoice.addListener('callReconnected', (data) => {
197
+ console.log('Call reconnected:', data);
198
+ });
199
+
200
+ CapacitorTwilioVoice.addListener('callQualityWarningsChanged', (data) => {
201
+ console.log('Quality warnings:', data);
202
+ });
203
+ ```
204
+
205
+ ## JWT Token Management
206
+
207
+ ### Token Format
208
+ The plugin expects Twilio access tokens in JWT format with this structure:
209
+ ```json
210
+ {
211
+ "iss": "your-account-sid",
212
+ "exp": 1234567890,
213
+ "grants": {
214
+ "voice": {
215
+ "outgoing": {
216
+ "application_sid": "your-app-sid"
217
+ },
218
+ "push_credential_sid": "your-push-credential-sid"
219
+ },
220
+ "identity": "user-identity"
221
+ }
222
+ }
223
+ ```
224
+
225
+ ### Token Validation
226
+ - Tokens are automatically validated for expiration
227
+ - Invalid or expired tokens will be rejected
228
+ - Use `isLoggedIn()` to check token status
229
+
230
+ ### Backend Integration
231
+ Fetch access tokens from your backend server:
232
+ ```typescript
233
+ async function fetchAccessToken(identity: string): Promise<string> {
234
+ const response = await fetch(`/accessToken?identity=${identity}`);
235
+ return response.text();
236
+ }
237
+ ```
238
+
239
+ ## Testing Requirements
240
+
241
+ ### iOS Simulator Limitations
242
+ - VoIP push notifications don't work in the iOS Simulator
243
+ - Use a physical iOS device for testing incoming calls
244
+ - Outgoing calls work in both Simulator and device
245
+
246
+ ### Android Emulator
247
+ - Requires Google Play Services
248
+ - Firebase messaging works in Android Emulator with Google APIs
249
+
250
+ ## Error Handling
251
+
252
+ The plugin provides detailed error information:
253
+ ```typescript
254
+ try {
255
+ await CapacitorTwilioVoice.makeCall({ to: '+1234567890' });
256
+ } catch (error) {
257
+ console.error('Call failed:', error);
258
+ }
259
+ ```
260
+
261
+ Common error scenarios:
262
+ - **Invalid token**: Check token format and expiration
263
+ - **No microphone permission**: Call `requestMicrophonePermission()`
264
+ - **Network issues**: Verify internet connectivity
265
+ - **Invalid phone number**: Use E.164 format (+1234567890)
266
+
267
+ ## Security Notes
268
+
269
+ - Access tokens are stored in secure device storage
270
+ - Tokens are automatically validated before use
271
+ - No sensitive data is logged in production builds
272
+ - Always use HTTPS for token fetching from your backend
273
+
274
+ ## Platform Support
275
+
276
+ | Platform | Support | Notes |
277
+ |----------|---------|-------|
278
+ | iOS | ✅ | Requires iOS 13.0+ |
279
+ | Android | ✅ | Requires API level 23+ |
280
+ | Web | ❌ | Not supported |
@@ -0,0 +1,65 @@
1
+ ext {
2
+ junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
3
+ androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.1'
4
+ androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.3.0'
5
+ androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.7.0'
6
+ }
7
+
8
+ buildscript {
9
+ repositories {
10
+ google()
11
+ mavenCentral()
12
+ }
13
+ dependencies {
14
+ classpath 'com.android.tools.build:gradle:8.7.2'
15
+ }
16
+ }
17
+
18
+ apply plugin: 'com.android.library'
19
+
20
+ android {
21
+ namespace "ee.forgr.capacitor_twilio_voice"
22
+ compileSdk project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 35
23
+ defaultConfig {
24
+ minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 23
25
+ targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 35
26
+ versionCode 1
27
+ versionName "1.0"
28
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
29
+ }
30
+ buildTypes {
31
+ release {
32
+ minifyEnabled false
33
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
34
+ }
35
+ }
36
+ lintOptions {
37
+ abortOnError false
38
+ }
39
+ compileOptions {
40
+ sourceCompatibility JavaVersion.VERSION_21
41
+ targetCompatibility JavaVersion.VERSION_21
42
+ }
43
+ }
44
+
45
+ repositories {
46
+ google()
47
+ mavenCentral()
48
+ }
49
+
50
+
51
+ dependencies {
52
+ implementation 'com.twilio:voice-android:6.9.+'
53
+ implementation 'com.twilio:audioswitch:1.2.2'
54
+ implementation 'com.google.firebase:firebase-messaging:25.0.0'
55
+ implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
56
+ implementation platform('com.google.firebase:firebase-bom:34.0.0')
57
+ implementation 'androidx.core:core:1.13.1'
58
+ implementation 'androidx.media:media:1.6.0'
59
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
60
+ implementation project(':capacitor-android')
61
+ implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
62
+ testImplementation "junit:junit:$junitVersion"
63
+ androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
64
+ androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
65
+ }
@@ -0,0 +1,43 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+
3
+ <!-- Permissions for Twilio Voice -->
4
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
5
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
6
+ <uses-permission android:name="android.permission.INTERNET" />
7
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
8
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
9
+ <uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
10
+ <uses-permission android:name="android.permission.VIBRATE" />
11
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
12
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
13
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
14
+
15
+ <application>
16
+ <!-- Firebase Messaging Service for Twilio Voice -->
17
+ <service
18
+ android:name="ee.forgr.capacitor_twilio_voice.VoiceFirebaseMessagingService"
19
+ android:exported="false">
20
+ <intent-filter>
21
+ <action android:name="com.google.firebase.MESSAGING_EVENT" />
22
+ </intent-filter>
23
+ </service>
24
+
25
+ <!-- Notification Action Receiver for accept/reject buttons -->
26
+ <receiver
27
+ android:name="ee.forgr.capacitor_twilio_voice.NotificationActionReceiver"
28
+ android:exported="false">
29
+ <intent-filter>
30
+ <action android:name="ACTION_ACCEPT_CALL" />
31
+ <action android:name="ACTION_REJECT_CALL" />
32
+ </intent-filter>
33
+ </receiver>
34
+
35
+ <!-- Voice Call Foreground Service -->
36
+ <service
37
+ android:name="ee.forgr.capacitor_twilio_voice.VoiceCallService"
38
+ android:enabled="true"
39
+ android:exported="false"
40
+ android:foregroundServiceType="microphone" />
41
+ </application>
42
+
43
+ </manifest>