@kapsula-chat/capacitor-push-calls 1.0.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/Package.swift +31 -0
- package/README.md +252 -0
- package/android/build.gradle +46 -0
- package/android/src/main/AndroidManifest.xml +31 -0
- package/android/src/main/java/com/capacitor/voipcalls/CallManager.kt +213 -0
- package/android/src/main/java/com/capacitor/voipcalls/CapacitorVoipCallsPlugin.kt +584 -0
- package/android/src/main/java/com/capacitor/voipcalls/PushRouterMessagingService.kt +26 -0
- package/android/src/main/java/com/capacitor/voipcalls/VoipConnection.kt +112 -0
- package/android/src/main/java/com/capacitor/voipcalls/VoipConnectionService.kt +101 -0
- package/dist/esm/definitions.d.ts +279 -0
- package/dist/esm/definitions.d.ts.map +1 -0
- package/dist/esm/definitions.js +1 -0
- package/dist/esm/index.d.ts +5 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +6 -0
- package/dist/esm/web.d.ts +67 -0
- package/dist/esm/web.d.ts.map +1 -0
- package/dist/esm/web.js +81 -0
- package/dist/plugin.cjs.js +96 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +99 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Plugin/CallManager.swift +226 -0
- package/ios/Plugin/CapacitorVoipCallsPlugin.swift +517 -0
- package/ios/Plugin/Plugin.m +31 -0
- package/package.json +95 -0
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
package com.capacitor.voipcalls
|
|
2
|
+
|
|
3
|
+
import android.Manifest
|
|
4
|
+
import android.app.Notification
|
|
5
|
+
import android.app.NotificationChannel
|
|
6
|
+
import android.app.NotificationManager
|
|
7
|
+
import android.content.Context
|
|
8
|
+
import android.content.Intent
|
|
9
|
+
import android.content.pm.PackageManager
|
|
10
|
+
import android.os.Build
|
|
11
|
+
import androidx.core.content.ContextCompat
|
|
12
|
+
import com.getcapacitor.JSArray
|
|
13
|
+
import com.getcapacitor.JSObject
|
|
14
|
+
import com.getcapacitor.PermissionState
|
|
15
|
+
import com.getcapacitor.Plugin
|
|
16
|
+
import com.getcapacitor.PluginCall
|
|
17
|
+
import com.getcapacitor.PluginMethod
|
|
18
|
+
import com.getcapacitor.annotation.CapacitorPlugin
|
|
19
|
+
import com.getcapacitor.annotation.Permission
|
|
20
|
+
import com.getcapacitor.annotation.PermissionCallback
|
|
21
|
+
import com.google.firebase.messaging.FirebaseMessaging
|
|
22
|
+
import org.json.JSONObject
|
|
23
|
+
import java.util.UUID
|
|
24
|
+
|
|
25
|
+
@CapacitorPlugin(
|
|
26
|
+
name = "CapacitorPushCalls",
|
|
27
|
+
permissions = [
|
|
28
|
+
Permission(strings = [Manifest.permission.RECORD_AUDIO], alias = "audio"),
|
|
29
|
+
Permission(strings = [Manifest.permission.CAMERA], alias = "camera"),
|
|
30
|
+
Permission(strings = [Manifest.permission.POST_NOTIFICATIONS], alias = "receive")
|
|
31
|
+
]
|
|
32
|
+
)
|
|
33
|
+
class CapacitorVoipCallsPlugin : Plugin() {
|
|
34
|
+
internal var callManager: CallManager? = null
|
|
35
|
+
|
|
36
|
+
companion object {
|
|
37
|
+
private const val BADGE_PREFS = "capacitor_push_calls"
|
|
38
|
+
private const val BADGE_KEY = "badge_count"
|
|
39
|
+
|
|
40
|
+
@Volatile
|
|
41
|
+
private var pluginInstance: CapacitorVoipCallsPlugin? = null
|
|
42
|
+
|
|
43
|
+
private val pendingEvents = mutableListOf<Pair<String, JSObject>>()
|
|
44
|
+
|
|
45
|
+
fun emitFromService(event: String, data: JSObject) {
|
|
46
|
+
val instance = pluginInstance
|
|
47
|
+
if (instance != null) {
|
|
48
|
+
instance.emitEvent(event, data)
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
synchronized(pendingEvents) {
|
|
53
|
+
pendingEvents.add(event to data)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
fun routeRemoteMessage(context: Context, data: Map<String, String>, title: String?, body: String?) {
|
|
58
|
+
val type = data["type"]?.lowercase()
|
|
59
|
+
if (type == "call") {
|
|
60
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
VoipConnectionService.registerPhoneAccount(context)
|
|
66
|
+
} catch (_: SecurityException) {
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
val callId = data["callId"] ?: UUID.randomUUID().toString()
|
|
70
|
+
val handle = data["handle"] ?: return
|
|
71
|
+
val displayName = data["displayName"] ?: handle
|
|
72
|
+
val handleType = data["handleType"] ?: "generic"
|
|
73
|
+
val video = parseBoolean(data["video"])
|
|
74
|
+
|
|
75
|
+
val metadata = JSObject()
|
|
76
|
+
for ((key, value) in data) {
|
|
77
|
+
if (key in setOf("type", "callId", "handle", "displayName", "handleType", "video")) {
|
|
78
|
+
continue
|
|
79
|
+
}
|
|
80
|
+
metadata.put(key, value)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
val manager = CallManager(context)
|
|
84
|
+
manager.reportIncomingCall(
|
|
85
|
+
callId = callId,
|
|
86
|
+
handle = handle,
|
|
87
|
+
displayName = displayName,
|
|
88
|
+
handleType = handleType,
|
|
89
|
+
video = video,
|
|
90
|
+
metadata = if (metadata.length() > 0) metadata else null
|
|
91
|
+
)
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
val notification = JSObject().apply {
|
|
96
|
+
put("id", data["messageId"] ?: UUID.randomUUID().toString())
|
|
97
|
+
if (title != null) put("title", title)
|
|
98
|
+
if (body != null) put("body", body)
|
|
99
|
+
put("data", JSObject().apply {
|
|
100
|
+
data.forEach { (key, value) -> put(key, value) }
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
emitFromService("pushNotificationReceived", notification)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private fun parseBoolean(value: String?): Boolean {
|
|
108
|
+
return value.equals("true", ignoreCase = true) || value == "1"
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
override fun load() {
|
|
113
|
+
callManager = CallManager(this)
|
|
114
|
+
pluginInstance = this
|
|
115
|
+
flushPendingEvents()
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
override fun handleOnDestroy() {
|
|
119
|
+
if (pluginInstance === this) {
|
|
120
|
+
pluginInstance = null
|
|
121
|
+
}
|
|
122
|
+
super.handleOnDestroy()
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
@PluginMethod
|
|
126
|
+
fun register(call: PluginCall) {
|
|
127
|
+
FirebaseMessaging.getInstance().token
|
|
128
|
+
.addOnSuccessListener { token ->
|
|
129
|
+
emitEvent("registration", JSObject().apply { put("value", token) })
|
|
130
|
+
call.resolve()
|
|
131
|
+
}
|
|
132
|
+
.addOnFailureListener { error ->
|
|
133
|
+
emitEvent("registrationError", JSObject().apply {
|
|
134
|
+
put("error", error.localizedMessage ?: "Failed to get FCM token")
|
|
135
|
+
})
|
|
136
|
+
call.reject("Failed to register for push notifications", error)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
@PluginMethod
|
|
141
|
+
fun unregister(call: PluginCall) {
|
|
142
|
+
FirebaseMessaging.getInstance().deleteToken()
|
|
143
|
+
.addOnSuccessListener { call.resolve() }
|
|
144
|
+
.addOnFailureListener { error ->
|
|
145
|
+
call.reject("Failed to unregister from push notifications", error)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
@PluginMethod
|
|
150
|
+
override fun checkPermissions(call: PluginCall) {
|
|
151
|
+
val receive = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
|
152
|
+
PermissionState.GRANTED.toString()
|
|
153
|
+
} else {
|
|
154
|
+
getPermissionState("receive").toString()
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
call.resolve(JSObject().apply {
|
|
158
|
+
put("receive", receive)
|
|
159
|
+
})
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
@PluginMethod
|
|
163
|
+
override fun requestPermissions(call: PluginCall) {
|
|
164
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
|
165
|
+
call.resolve(JSObject().apply {
|
|
166
|
+
put("receive", PermissionState.GRANTED.toString())
|
|
167
|
+
})
|
|
168
|
+
return
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
requestPermissionForAlias("receive", call, "receivePermissionCallback")
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
@PermissionCallback
|
|
175
|
+
private fun receivePermissionCallback(call: PluginCall) {
|
|
176
|
+
checkPermissions(call)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
@PluginMethod
|
|
180
|
+
fun getDeliveredNotifications(call: PluginCall) {
|
|
181
|
+
val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
182
|
+
val notifications = JSArray()
|
|
183
|
+
|
|
184
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
185
|
+
manager.activeNotifications.forEach { sbn ->
|
|
186
|
+
val extras = sbn.notification.extras
|
|
187
|
+
val notification = JSObject().apply {
|
|
188
|
+
put("id", sbn.id.toString())
|
|
189
|
+
put("title", extras.getString("android.title"))
|
|
190
|
+
put("body", extras.getCharSequence("android.text")?.toString())
|
|
191
|
+
put("tag", sbn.tag)
|
|
192
|
+
put("data", JSObject())
|
|
193
|
+
}
|
|
194
|
+
notifications.put(notification)
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
call.resolve(JSObject().apply {
|
|
199
|
+
put("notifications", notifications)
|
|
200
|
+
})
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
@PluginMethod
|
|
204
|
+
fun removeDeliveredNotifications(call: PluginCall) {
|
|
205
|
+
val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
206
|
+
val payload = call.getArray("notifications")
|
|
207
|
+
|
|
208
|
+
if (payload == null) {
|
|
209
|
+
call.reject("Missing notifications parameter")
|
|
210
|
+
return
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
for (index in 0 until payload.length()) {
|
|
214
|
+
when (val item = payload.opt(index)) {
|
|
215
|
+
is String -> manager.cancel(item.toIntOrNull() ?: continue)
|
|
216
|
+
is JSObject -> {
|
|
217
|
+
val id = item.getString("id")
|
|
218
|
+
if (id != null) {
|
|
219
|
+
manager.cancel(id.toIntOrNull() ?: continue)
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
is JSONObject -> {
|
|
223
|
+
val id = item.optString("id", null)
|
|
224
|
+
if (id != null) {
|
|
225
|
+
manager.cancel(id.toIntOrNull() ?: continue)
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
call.resolve()
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
@PluginMethod
|
|
235
|
+
fun removeAllDeliveredNotifications(call: PluginCall) {
|
|
236
|
+
val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
237
|
+
manager.cancelAll()
|
|
238
|
+
call.resolve()
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
@PluginMethod
|
|
242
|
+
fun getBadgeCount(call: PluginCall) {
|
|
243
|
+
val prefs = context.getSharedPreferences(BADGE_PREFS, Context.MODE_PRIVATE)
|
|
244
|
+
val count = prefs.getInt(BADGE_KEY, 0)
|
|
245
|
+
call.resolve(JSObject().apply {
|
|
246
|
+
put("count", count)
|
|
247
|
+
})
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
@PluginMethod
|
|
251
|
+
fun getBadgeNumber(call: PluginCall) {
|
|
252
|
+
getBadgeCount(call)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
@PluginMethod
|
|
256
|
+
fun setBadgeCount(call: PluginCall) {
|
|
257
|
+
val count = call.getInt("count")
|
|
258
|
+
if (count == null || count < 0) {
|
|
259
|
+
call.reject("Invalid count parameter")
|
|
260
|
+
return
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
val prefs = context.getSharedPreferences(BADGE_PREFS, Context.MODE_PRIVATE)
|
|
264
|
+
prefs.edit().putInt(BADGE_KEY, count).apply()
|
|
265
|
+
call.resolve()
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
@PluginMethod
|
|
269
|
+
fun setBadgeNumber(call: PluginCall) {
|
|
270
|
+
setBadgeCount(call)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
@PluginMethod
|
|
274
|
+
fun clearBadgeCount(call: PluginCall) {
|
|
275
|
+
val prefs = context.getSharedPreferences(BADGE_PREFS, Context.MODE_PRIVATE)
|
|
276
|
+
prefs.edit().putInt(BADGE_KEY, 0).apply()
|
|
277
|
+
call.resolve()
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
@PluginMethod
|
|
281
|
+
fun createChannel(call: PluginCall) {
|
|
282
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
|
283
|
+
call.resolve()
|
|
284
|
+
return
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
val id = call.getString("id")
|
|
288
|
+
val name = call.getString("name")
|
|
289
|
+
if (id == null || name == null) {
|
|
290
|
+
call.reject("Missing required channel fields: id and name")
|
|
291
|
+
return
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
val importance = call.getInt("importance") ?: NotificationManager.IMPORTANCE_DEFAULT
|
|
295
|
+
val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
296
|
+
val channel = NotificationChannel(id, name, importance).apply {
|
|
297
|
+
description = call.getString("description")
|
|
298
|
+
enableLights(call.getBoolean("lights") ?: false)
|
|
299
|
+
enableVibration(call.getBoolean("vibration") ?: true)
|
|
300
|
+
lockscreenVisibility = call.getInt("visibility") ?: Notification.VISIBILITY_PRIVATE
|
|
301
|
+
val sound = call.getString("sound")
|
|
302
|
+
if (sound == null || sound == "default") {
|
|
303
|
+
setSound(null, null)
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
manager.createNotificationChannel(channel)
|
|
308
|
+
call.resolve()
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
@PluginMethod
|
|
312
|
+
fun deleteChannel(call: PluginCall) {
|
|
313
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
|
314
|
+
call.resolve()
|
|
315
|
+
return
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
val id = call.getString("id")
|
|
319
|
+
if (id == null) {
|
|
320
|
+
call.reject("Missing channel id")
|
|
321
|
+
return
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
325
|
+
manager.deleteNotificationChannel(id)
|
|
326
|
+
call.resolve()
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
@PluginMethod
|
|
330
|
+
fun listChannels(call: PluginCall) {
|
|
331
|
+
val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
332
|
+
val channels = JSArray()
|
|
333
|
+
|
|
334
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
335
|
+
manager.notificationChannels.forEach { channel ->
|
|
336
|
+
channels.put(JSObject().apply {
|
|
337
|
+
put("id", channel.id)
|
|
338
|
+
put("name", channel.name.toString())
|
|
339
|
+
put("description", channel.description)
|
|
340
|
+
put("importance", channel.importance)
|
|
341
|
+
put("visibility", channel.lockscreenVisibility)
|
|
342
|
+
put("lights", channel.shouldShowLights())
|
|
343
|
+
put("vibration", channel.shouldVibrate())
|
|
344
|
+
})
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
call.resolve(JSObject().apply {
|
|
349
|
+
put("channels", channels)
|
|
350
|
+
})
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
@PluginMethod
|
|
354
|
+
fun registerVoipNotifications(call: PluginCall) {
|
|
355
|
+
// Android uses FCM for call signaling via PushRouterMessagingService.
|
|
356
|
+
call.resolve()
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
@PluginMethod
|
|
360
|
+
fun startCall(call: PluginCall) {
|
|
361
|
+
startCallInternal(call, canRequestPermissions = true)
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
private fun startCallInternal(call: PluginCall, canRequestPermissions: Boolean) {
|
|
365
|
+
val handle = call.getString("handle") ?: run {
|
|
366
|
+
call.reject("Missing handle parameter")
|
|
367
|
+
return
|
|
368
|
+
}
|
|
369
|
+
val displayName = call.getString("displayName") ?: run {
|
|
370
|
+
call.reject("Missing displayName parameter")
|
|
371
|
+
return
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
val handleType = call.getString("handleType") ?: "generic"
|
|
375
|
+
val video = call.getBoolean("video", false) ?: false
|
|
376
|
+
|
|
377
|
+
if (!hasRequiredPermissions()) {
|
|
378
|
+
if (!canRequestPermissions) {
|
|
379
|
+
call.reject("Missing required RECORD_AUDIO permission")
|
|
380
|
+
return
|
|
381
|
+
}
|
|
382
|
+
requestPermissionForAliases(arrayOf("audio"), call, "startCallPermissions")
|
|
383
|
+
return
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
val callId = UUID.randomUUID().toString()
|
|
387
|
+
|
|
388
|
+
callManager?.startCall(
|
|
389
|
+
callId = callId,
|
|
390
|
+
handle = handle,
|
|
391
|
+
displayName = displayName,
|
|
392
|
+
handleType = handleType,
|
|
393
|
+
video = video
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
call.resolve(JSObject().apply {
|
|
397
|
+
put("callId", callId)
|
|
398
|
+
})
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
@PermissionCallback
|
|
402
|
+
private fun startCallPermissions(call: PluginCall) {
|
|
403
|
+
startCallInternal(call, canRequestPermissions = false)
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
@PluginMethod
|
|
407
|
+
fun endCall(call: PluginCall) {
|
|
408
|
+
val callId = call.getString("callId") ?: run {
|
|
409
|
+
call.reject("Missing callId parameter")
|
|
410
|
+
return
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
callManager?.endCall(callId)
|
|
414
|
+
call.resolve()
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
@PluginMethod
|
|
418
|
+
fun answerCall(call: PluginCall) {
|
|
419
|
+
answerCallInternal(call, canRequestPermissions = true)
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
private fun answerCallInternal(call: PluginCall, canRequestPermissions: Boolean) {
|
|
423
|
+
val callId = call.getString("callId") ?: run {
|
|
424
|
+
call.reject("Missing callId parameter")
|
|
425
|
+
return
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if (!hasRequiredPermissions()) {
|
|
429
|
+
if (!canRequestPermissions) {
|
|
430
|
+
call.reject("Missing required RECORD_AUDIO permission")
|
|
431
|
+
return
|
|
432
|
+
}
|
|
433
|
+
requestPermissionForAliases(arrayOf("audio"), call, "answerCallPermissions")
|
|
434
|
+
return
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
callManager?.answerCall(callId)
|
|
438
|
+
call.resolve()
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
@PermissionCallback
|
|
442
|
+
private fun answerCallPermissions(call: PluginCall) {
|
|
443
|
+
answerCallInternal(call, canRequestPermissions = false)
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
@PluginMethod
|
|
447
|
+
fun rejectCall(call: PluginCall) {
|
|
448
|
+
val callId = call.getString("callId") ?: run {
|
|
449
|
+
call.reject("Missing callId parameter")
|
|
450
|
+
return
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
callManager?.rejectCall(callId)
|
|
454
|
+
call.resolve()
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
@PluginMethod
|
|
458
|
+
fun setCallOnHold(call: PluginCall) {
|
|
459
|
+
val callId = call.getString("callId") ?: run {
|
|
460
|
+
call.reject("Missing callId parameter")
|
|
461
|
+
return
|
|
462
|
+
}
|
|
463
|
+
val onHold = call.getBoolean("onHold", false) ?: false
|
|
464
|
+
|
|
465
|
+
callManager?.setCallOnHold(callId, onHold)
|
|
466
|
+
call.resolve()
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
@PluginMethod
|
|
470
|
+
fun setAudioRoute(call: PluginCall) {
|
|
471
|
+
val route = call.getString("route") ?: run {
|
|
472
|
+
call.reject("Missing route parameter")
|
|
473
|
+
return
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
callManager?.setAudioRoute(route)
|
|
477
|
+
call.resolve()
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
@PluginMethod
|
|
481
|
+
fun setMuted(call: PluginCall) {
|
|
482
|
+
val muted = call.getBoolean("muted", false) ?: false
|
|
483
|
+
|
|
484
|
+
callManager?.setMuted(muted)
|
|
485
|
+
call.resolve()
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
@PluginMethod
|
|
489
|
+
fun handleIncomingCall(call: PluginCall) {
|
|
490
|
+
val callId = call.getString("callId") ?: UUID.randomUUID().toString()
|
|
491
|
+
val handle = call.getString("handle") ?: run {
|
|
492
|
+
call.reject("Missing handle parameter")
|
|
493
|
+
return
|
|
494
|
+
}
|
|
495
|
+
val displayName = call.getString("displayName") ?: run {
|
|
496
|
+
call.reject("Missing displayName parameter")
|
|
497
|
+
return
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
val handleType = call.getString("handleType") ?: "generic"
|
|
501
|
+
val video = call.getBoolean("video", false) ?: false
|
|
502
|
+
val metadata = call.getObject("metadata")
|
|
503
|
+
|
|
504
|
+
callManager?.reportIncomingCall(
|
|
505
|
+
callId = callId,
|
|
506
|
+
handle = handle,
|
|
507
|
+
displayName = displayName,
|
|
508
|
+
handleType = handleType,
|
|
509
|
+
video = video,
|
|
510
|
+
metadata = metadata
|
|
511
|
+
)
|
|
512
|
+
|
|
513
|
+
call.resolve(JSObject().apply {
|
|
514
|
+
put("callId", callId)
|
|
515
|
+
})
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
@PluginMethod
|
|
519
|
+
fun updateCallStatus(call: PluginCall) {
|
|
520
|
+
val callId = call.getString("callId") ?: run {
|
|
521
|
+
call.reject("Missing callId parameter")
|
|
522
|
+
return
|
|
523
|
+
}
|
|
524
|
+
val status = call.getString("status") ?: run {
|
|
525
|
+
call.reject("Missing status parameter")
|
|
526
|
+
return
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
callManager?.updateCallStatus(callId, status)
|
|
530
|
+
call.resolve()
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
@PluginMethod
|
|
534
|
+
fun isSupported(call: PluginCall) {
|
|
535
|
+
call.resolve(JSObject().apply {
|
|
536
|
+
put("supported", Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
|
|
537
|
+
})
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
override fun handleOnNewIntent(intent: Intent?) {
|
|
541
|
+
super.handleOnNewIntent(intent)
|
|
542
|
+
val extras = intent?.extras ?: return
|
|
543
|
+
|
|
544
|
+
if (!extras.containsKey("google.message_id") && !extras.containsKey("type")) {
|
|
545
|
+
return
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
val data = JSObject()
|
|
549
|
+
for (key in extras.keySet()) {
|
|
550
|
+
data.put(key, extras.get(key))
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
val actionPayload = JSObject().apply {
|
|
554
|
+
put("actionId", "tap")
|
|
555
|
+
put("notification", JSObject().apply {
|
|
556
|
+
put("id", extras.getString("google.message_id") ?: UUID.randomUUID().toString())
|
|
557
|
+
put("title", extras.getString("title"))
|
|
558
|
+
put("body", extras.getString("body"))
|
|
559
|
+
put("data", data)
|
|
560
|
+
})
|
|
561
|
+
}
|
|
562
|
+
emitEvent("pushNotificationActionPerformed", actionPayload)
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
private fun hasRequiredPermissions(): Boolean {
|
|
566
|
+
return ContextCompat.checkSelfPermission(
|
|
567
|
+
context,
|
|
568
|
+
Manifest.permission.RECORD_AUDIO
|
|
569
|
+
) == PackageManager.PERMISSION_GRANTED
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
internal fun emitEvent(event: String, data: JSObject) {
|
|
573
|
+
super.notifyListeners(event, data)
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
private fun flushPendingEvents() {
|
|
577
|
+
synchronized(pendingEvents) {
|
|
578
|
+
pendingEvents.forEach { (event, payload) ->
|
|
579
|
+
emitEvent(event, payload)
|
|
580
|
+
}
|
|
581
|
+
pendingEvents.clear()
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
package com.capacitor.voipcalls
|
|
2
|
+
|
|
3
|
+
import com.getcapacitor.JSObject
|
|
4
|
+
import com.google.firebase.messaging.FirebaseMessagingService
|
|
5
|
+
import com.google.firebase.messaging.RemoteMessage
|
|
6
|
+
|
|
7
|
+
class PushRouterMessagingService : FirebaseMessagingService() {
|
|
8
|
+
override fun onNewToken(token: String) {
|
|
9
|
+
super.onNewToken(token)
|
|
10
|
+
CapacitorVoipCallsPlugin.emitFromService(
|
|
11
|
+
"registration",
|
|
12
|
+
JSObject().apply { put("value", token) }
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
override fun onMessageReceived(message: RemoteMessage) {
|
|
17
|
+
super.onMessageReceived(message)
|
|
18
|
+
|
|
19
|
+
CapacitorVoipCallsPlugin.routeRemoteMessage(
|
|
20
|
+
context = applicationContext,
|
|
21
|
+
data = message.data,
|
|
22
|
+
title = message.notification?.title,
|
|
23
|
+
body = message.notification?.body
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
}
|