@qusaieilouti99/call-manager 0.1.56 → 0.1.58
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/android/src/main/java/com/margelo/nitro/qusaieilouti99/callmanager/CallEngine.kt +207 -167
- package/android/src/main/java/com/margelo/nitro/qusaieilouti99/callmanager/CallForegroundService.kt +6 -1
- package/android/src/main/java/com/margelo/nitro/qusaieilouti99/callmanager/CallManager.kt +48 -36
- package/package.json +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
package com.margelo.nitro.qusaieilouti99.callmanager
|
|
2
2
|
|
|
3
3
|
import android.app.Activity
|
|
4
|
+
import android.app.Application
|
|
4
5
|
import android.app.Notification
|
|
5
6
|
import android.app.NotificationChannel
|
|
6
7
|
import android.app.NotificationManager
|
|
@@ -46,19 +47,20 @@ object CallEngine {
|
|
|
46
47
|
private const val FOREGROUND_CHANNEL_ID = "call_foreground_channel"
|
|
47
48
|
private const val FOREGROUND_NOTIF_ID = 1001
|
|
48
49
|
|
|
50
|
+
// Core context - initialized once and maintained
|
|
51
|
+
private var appContext: Context? = null
|
|
52
|
+
private var isInitialized = AtomicBoolean(false)
|
|
53
|
+
|
|
49
54
|
// Audio & Media
|
|
50
55
|
private var ringtone: android.media.Ringtone? = null
|
|
51
56
|
private var ringbackPlayer: MediaPlayer? = null
|
|
52
57
|
private var audioManager: AudioManager? = null
|
|
53
58
|
private var wakeLock: PowerManager.WakeLock? = null
|
|
54
|
-
private var appContext: Context? = null
|
|
55
59
|
private var audioFocusRequest: AudioFocusRequest? = null
|
|
56
60
|
|
|
57
|
-
// Call State Management
|
|
61
|
+
// Call State Management
|
|
58
62
|
private val activeCalls = ConcurrentHashMap<String, CallInfo>()
|
|
59
63
|
private val telecomConnections = ConcurrentHashMap<String, Connection>()
|
|
60
|
-
|
|
61
|
-
// Separate opaque metadata storage - native doesn't interpret
|
|
62
64
|
private val callMetadata = ConcurrentHashMap<String, String>()
|
|
63
65
|
|
|
64
66
|
private var currentCallId: String? = null
|
|
@@ -66,7 +68,6 @@ object CallEngine {
|
|
|
66
68
|
|
|
67
69
|
// Audio State Tracking
|
|
68
70
|
private var lastAudioRoutesInfo: AudioRoutesInfo? = null
|
|
69
|
-
private var lastMuteState: Boolean = false
|
|
70
71
|
private var hasAudioFocus: Boolean = false
|
|
71
72
|
private var isSystemCallActive: Boolean = false
|
|
72
73
|
|
|
@@ -78,56 +79,92 @@ object CallEngine {
|
|
|
78
79
|
private var eventHandler: ((CallEventType, String) -> Unit)? = null
|
|
79
80
|
private val cachedEvents = mutableListOf<Pair<CallEventType, String>>()
|
|
80
81
|
|
|
81
|
-
// Operation State
|
|
82
|
-
private val operationInProgress = AtomicBoolean(false)
|
|
83
|
-
|
|
84
82
|
interface LockScreenBypassCallback {
|
|
85
83
|
fun onLockScreenBypassChanged(shouldBypass: Boolean)
|
|
86
84
|
}
|
|
87
85
|
|
|
88
|
-
// ---
|
|
86
|
+
// --- INITIALIZATION - Fix for context management ---
|
|
87
|
+
fun initialize(context: Context) {
|
|
88
|
+
if (isInitialized.compareAndSet(false, true)) {
|
|
89
|
+
appContext = context.applicationContext
|
|
90
|
+
audioManager = appContext?.getSystemService(Context.AUDIO_SERVICE) as? AudioManager
|
|
91
|
+
Log.d(TAG, "CallEngine initialized with context")
|
|
92
|
+
|
|
93
|
+
// Initialize foreground service if needed
|
|
94
|
+
if (isCallActive()) {
|
|
95
|
+
startForegroundService()
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
fun isInitialized(): Boolean = isInitialized.get()
|
|
101
|
+
|
|
102
|
+
private fun requireContext(): Context {
|
|
103
|
+
return appContext ?: throw IllegalStateException("CallEngine not initialized. Call initialize() first.")
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// --- Event System ---
|
|
107
|
+
fun setEventHandler(handler: ((CallEventType, String) -> Unit)?) {
|
|
108
|
+
Log.d(TAG, "setEventHandler called. Handler present: ${handler != null}")
|
|
109
|
+
eventHandler = handler
|
|
110
|
+
handler?.let { h ->
|
|
111
|
+
if (cachedEvents.isNotEmpty()) {
|
|
112
|
+
Log.d(TAG, "Emitting ${cachedEvents.size} cached events.")
|
|
113
|
+
cachedEvents.forEach { (type, data) -> h.invoke(type, data) }
|
|
114
|
+
cachedEvents.clear()
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private fun emitEvent(type: CallEventType, data: JSONObject) {
|
|
120
|
+
Log.d(TAG, "Emitting event: $type, data: $data")
|
|
121
|
+
val dataString = data.toString()
|
|
122
|
+
if (eventHandler != null) {
|
|
123
|
+
eventHandler?.invoke(type, dataString)
|
|
124
|
+
} else {
|
|
125
|
+
Log.d(TAG, "No event handler registered, caching event: $type")
|
|
126
|
+
cachedEvents.add(Pair(type, dataString))
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// --- Audio Focus Management (Simplified) ---
|
|
89
131
|
private val audioFocusChangeListener = AudioManager.OnAudioFocusChangeListener { focusChange ->
|
|
90
132
|
Log.d(TAG, "Audio focus changed: $focusChange")
|
|
91
133
|
when (focusChange) {
|
|
92
|
-
AudioManager.AUDIOFOCUS_LOSS
|
|
93
|
-
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT ->
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
134
|
+
AudioManager.AUDIOFOCUS_LOSS,
|
|
135
|
+
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
|
|
136
|
+
hasAudioFocus = false
|
|
137
|
+
isSystemCallActive = true
|
|
138
|
+
holdSystemCalls()
|
|
139
|
+
}
|
|
140
|
+
AudioManager.AUDIOFOCUS_GAIN -> {
|
|
141
|
+
hasAudioFocus = true
|
|
142
|
+
isSystemCallActive = false
|
|
143
|
+
Handler(Looper.getMainLooper()).postDelayed({
|
|
144
|
+
resumeSystemHeldCalls()
|
|
145
|
+
}, 1000)
|
|
97
146
|
}
|
|
98
147
|
}
|
|
148
|
+
updateForegroundNotification()
|
|
99
149
|
}
|
|
100
150
|
|
|
101
|
-
private fun
|
|
102
|
-
Log.d(TAG, "Audio focus lost - likely system call active")
|
|
103
|
-
hasAudioFocus = false
|
|
104
|
-
isSystemCallActive = true
|
|
105
|
-
|
|
151
|
+
private fun holdSystemCalls() {
|
|
106
152
|
activeCalls.values.filter { it.state == CallState.ACTIVE }.forEach { call ->
|
|
107
153
|
if (!call.wasHeldBySystem) {
|
|
108
154
|
holdCallInternal(call.callId, heldBySystem = true)
|
|
109
155
|
}
|
|
110
156
|
}
|
|
111
|
-
|
|
112
157
|
stopRingback()
|
|
113
|
-
updateForegroundNotification()
|
|
114
158
|
}
|
|
115
159
|
|
|
116
|
-
private fun
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
Handler(Looper.getMainLooper()).postDelayed({
|
|
122
|
-
activeCalls.values.filter { it.state == CallState.HELD && it.wasHeldBySystem }.forEach { call ->
|
|
123
|
-
unholdCallInternal(call.callId, resumedBySystem = true)
|
|
124
|
-
}
|
|
125
|
-
updateForegroundNotification()
|
|
126
|
-
}, 1000)
|
|
160
|
+
private fun resumeSystemHeldCalls() {
|
|
161
|
+
activeCalls.values.filter { it.state == CallState.HELD && it.wasHeldBySystem }.forEach { call ->
|
|
162
|
+
unholdCallInternal(call.callId, resumedBySystem = true)
|
|
163
|
+
}
|
|
127
164
|
}
|
|
128
165
|
|
|
129
166
|
private fun requestAudioFocus(): Boolean {
|
|
130
|
-
val context =
|
|
167
|
+
val context = requireContext()
|
|
131
168
|
audioManager = audioManager ?: context.getSystemService(Context.AUDIO_SERVICE) as? AudioManager
|
|
132
169
|
|
|
133
170
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
@@ -160,45 +197,20 @@ object CallEngine {
|
|
|
160
197
|
}
|
|
161
198
|
|
|
162
199
|
private fun abandonAudioFocus() {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
200
|
+
audioManager?.let { am ->
|
|
201
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
202
|
+
audioFocusRequest?.let { request ->
|
|
203
|
+
am.abandonAudioFocusRequest(request)
|
|
204
|
+
}
|
|
205
|
+
} else {
|
|
206
|
+
@Suppress("DEPRECATION")
|
|
207
|
+
am.abandonAudioFocus(audioFocusChangeListener)
|
|
169
208
|
}
|
|
170
|
-
} else {
|
|
171
|
-
@Suppress("DEPRECATION")
|
|
172
|
-
audioManager?.abandonAudioFocus(audioFocusChangeListener)
|
|
173
209
|
}
|
|
174
210
|
hasAudioFocus = false
|
|
175
211
|
Log.d(TAG, "Audio focus abandoned")
|
|
176
212
|
}
|
|
177
213
|
|
|
178
|
-
// --- Event System ---
|
|
179
|
-
fun setEventHandler(handler: ((CallEventType, String) -> Unit)?) {
|
|
180
|
-
Log.d(TAG, "setEventHandler called. Handler present: ${handler != null}")
|
|
181
|
-
eventHandler = handler
|
|
182
|
-
handler?.let { h ->
|
|
183
|
-
if (cachedEvents.isNotEmpty()) {
|
|
184
|
-
Log.d(TAG, "Emitting ${cachedEvents.size} cached events.")
|
|
185
|
-
cachedEvents.forEach { (type, data) -> h.invoke(type, data) }
|
|
186
|
-
cachedEvents.clear()
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
fun emitEvent(type: CallEventType, data: JSONObject) {
|
|
192
|
-
Log.d(TAG, "Emitting event: $type, data: $data")
|
|
193
|
-
val dataString = data.toString()
|
|
194
|
-
if (eventHandler != null) {
|
|
195
|
-
eventHandler?.invoke(type, dataString)
|
|
196
|
-
} else {
|
|
197
|
-
Log.d(TAG, "No event handler registered, caching event: $type")
|
|
198
|
-
cachedEvents.add(Pair(type, dataString))
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
214
|
// --- Lock Screen Bypass Management ---
|
|
203
215
|
fun registerLockScreenBypassCallback(callback: LockScreenBypassCallback) {
|
|
204
216
|
lockScreenBypassCallbacks.add(callback)
|
|
@@ -239,8 +251,6 @@ object CallEngine {
|
|
|
239
251
|
|
|
240
252
|
fun getTelecomConnection(callId: String): Connection? = telecomConnections[callId]
|
|
241
253
|
|
|
242
|
-
fun getAppContext(): Context? = appContext
|
|
243
|
-
|
|
244
254
|
// --- Public API ---
|
|
245
255
|
fun setCanMakeMultipleCalls(allow: Boolean) {
|
|
246
256
|
canMakeMultipleCalls = allow
|
|
@@ -267,10 +277,12 @@ object CallEngine {
|
|
|
267
277
|
pictureUrl: String? = null,
|
|
268
278
|
metadata: String? = null
|
|
269
279
|
) {
|
|
270
|
-
|
|
280
|
+
if (!isInitialized.get()) {
|
|
281
|
+
initialize(context)
|
|
282
|
+
}
|
|
283
|
+
|
|
271
284
|
Log.d(TAG, "reportIncomingCall: callId=$callId, type=$callType, name=$displayName")
|
|
272
285
|
|
|
273
|
-
// Store metadata separately if provided
|
|
274
286
|
metadata?.let { callMetadata[callId] = it }
|
|
275
287
|
|
|
276
288
|
// Check for call collision
|
|
@@ -302,11 +314,11 @@ object CallEngine {
|
|
|
302
314
|
currentCallId = callId
|
|
303
315
|
Log.d(TAG, "Call $callId added to activeCalls. State: INCOMING, callType: $callType")
|
|
304
316
|
|
|
305
|
-
showIncomingCallUI(
|
|
306
|
-
registerPhoneAccount(
|
|
317
|
+
showIncomingCallUI(callId, displayName, callType)
|
|
318
|
+
registerPhoneAccount()
|
|
307
319
|
|
|
308
|
-
val telecomManager =
|
|
309
|
-
val phoneAccountHandle = getPhoneAccountHandle(
|
|
320
|
+
val telecomManager = requireContext().getSystemService(Context.TELECOM_SERVICE) as TelecomManager
|
|
321
|
+
val phoneAccountHandle = getPhoneAccountHandle()
|
|
310
322
|
val extras = Bundle().apply {
|
|
311
323
|
putString(MyConnectionService.EXTRA_CALL_ID, callId)
|
|
312
324
|
putString(MyConnectionService.EXTRA_CALL_TYPE, callType)
|
|
@@ -317,7 +329,7 @@ object CallEngine {
|
|
|
317
329
|
|
|
318
330
|
try {
|
|
319
331
|
telecomManager.addNewIncomingCall(phoneAccountHandle, extras)
|
|
320
|
-
startForegroundService(
|
|
332
|
+
startForegroundService() // Fixed: Always start foreground service
|
|
321
333
|
Log.d(TAG, "Successfully reported incoming call to TelecomManager for $callId")
|
|
322
334
|
} catch (e: SecurityException) {
|
|
323
335
|
Log.e(TAG, "SecurityException: Failed to report incoming call. Check MANAGE_OWN_CALLS permission: ${e.message}", e)
|
|
@@ -332,13 +344,12 @@ object CallEngine {
|
|
|
332
344
|
|
|
333
345
|
// --- Outgoing Call Management ---
|
|
334
346
|
fun startOutgoingCall(
|
|
335
|
-
context: Context,
|
|
336
347
|
callId: String,
|
|
337
348
|
callType: String,
|
|
338
349
|
targetName: String,
|
|
339
350
|
metadata: String? = null
|
|
340
351
|
) {
|
|
341
|
-
|
|
352
|
+
val context = requireContext()
|
|
342
353
|
Log.d(TAG, "startOutgoingCall: callId=$callId, type=$callType, target=$targetName")
|
|
343
354
|
|
|
344
355
|
metadata?.let { callMetadata[callId] = it }
|
|
@@ -366,9 +377,9 @@ object CallEngine {
|
|
|
366
377
|
currentCallId = callId
|
|
367
378
|
Log.d(TAG, "Call $callId added to activeCalls. State: DIALING, callType: $callType")
|
|
368
379
|
|
|
369
|
-
registerPhoneAccount(
|
|
380
|
+
registerPhoneAccount()
|
|
370
381
|
val telecomManager = context.getSystemService(Context.TELECOM_SERVICE) as TelecomManager
|
|
371
|
-
val phoneAccountHandle = getPhoneAccountHandle(
|
|
382
|
+
val phoneAccountHandle = getPhoneAccountHandle()
|
|
372
383
|
val addressUri = Uri.fromParts(PhoneAccount.SCHEME_TEL, targetName, null)
|
|
373
384
|
|
|
374
385
|
val extras = Bundle().apply {
|
|
@@ -382,14 +393,13 @@ object CallEngine {
|
|
|
382
393
|
|
|
383
394
|
try {
|
|
384
395
|
telecomManager.placeCall(addressUri, extras)
|
|
385
|
-
startForegroundService(
|
|
386
|
-
Log.d(TAG, "Successfully reported outgoing call to TelecomManager via placeCall for $callId")
|
|
387
|
-
|
|
396
|
+
startForegroundService() // Fixed: Always start foreground service
|
|
388
397
|
requestAudioFocus()
|
|
389
398
|
startRingback()
|
|
390
|
-
bringAppToForeground(
|
|
391
|
-
keepScreenAwake(
|
|
392
|
-
setInitialAudioRoute(
|
|
399
|
+
bringAppToForeground()
|
|
400
|
+
keepScreenAwake(true)
|
|
401
|
+
setInitialAudioRoute(callType)
|
|
402
|
+
Log.d(TAG, "Successfully reported outgoing call to TelecomManager via placeCall for $callId")
|
|
393
403
|
} catch (e: SecurityException) {
|
|
394
404
|
Log.e(TAG, "SecurityException: Failed to start outgoing call via placeCall. Check MANAGE_OWN_CALLS permission: ${e.message}", e)
|
|
395
405
|
endCallInternal(callId)
|
|
@@ -401,15 +411,14 @@ object CallEngine {
|
|
|
401
411
|
updateLockScreenBypass()
|
|
402
412
|
}
|
|
403
413
|
|
|
404
|
-
//
|
|
414
|
+
// Fixed: Start call as active (not dialing) with foreground service
|
|
405
415
|
fun startCall(
|
|
406
|
-
context: Context,
|
|
407
416
|
callId: String,
|
|
408
417
|
callType: String,
|
|
409
418
|
targetName: String,
|
|
410
419
|
metadata: String? = null
|
|
411
420
|
) {
|
|
412
|
-
|
|
421
|
+
val context = requireContext()
|
|
413
422
|
Log.d(TAG, "startCall: callId=$callId, type=$callType, target=$targetName")
|
|
414
423
|
|
|
415
424
|
metadata?.let { callMetadata[callId] = it }
|
|
@@ -432,12 +441,12 @@ object CallEngine {
|
|
|
432
441
|
currentCallId = callId
|
|
433
442
|
Log.d(TAG, "Call $callId started as ACTIVE, callType: $callType")
|
|
434
443
|
|
|
435
|
-
registerPhoneAccount(
|
|
444
|
+
registerPhoneAccount()
|
|
436
445
|
requestAudioFocus()
|
|
437
|
-
bringAppToForeground(
|
|
438
|
-
startForegroundService(
|
|
439
|
-
keepScreenAwake(
|
|
440
|
-
setInitialAudioRoute(
|
|
446
|
+
bringAppToForeground()
|
|
447
|
+
startForegroundService() // Fixed: Start foreground service for JS-initiated calls
|
|
448
|
+
keepScreenAwake(true)
|
|
449
|
+
setInitialAudioRoute(callType)
|
|
441
450
|
updateLockScreenBypass()
|
|
442
451
|
|
|
443
452
|
// Emit call answered event with metadata
|
|
@@ -445,17 +454,18 @@ object CallEngine {
|
|
|
445
454
|
}
|
|
446
455
|
|
|
447
456
|
// --- Call Answer Management ---
|
|
448
|
-
fun callAnsweredFromJS(
|
|
457
|
+
fun callAnsweredFromJS(callId: String) {
|
|
449
458
|
Log.d(TAG, "callAnsweredFromJS: $callId - remote party answered")
|
|
450
|
-
coreCallAnswered(
|
|
459
|
+
coreCallAnswered(callId, isLocalAnswer = false)
|
|
451
460
|
}
|
|
452
461
|
|
|
453
|
-
fun answerCall(
|
|
462
|
+
fun answerCall(callId: String) {
|
|
454
463
|
Log.d(TAG, "answerCall: $callId - local party answering")
|
|
455
|
-
coreCallAnswered(
|
|
464
|
+
coreCallAnswered(callId, isLocalAnswer = true)
|
|
456
465
|
}
|
|
457
466
|
|
|
458
|
-
private fun coreCallAnswered(
|
|
467
|
+
private fun coreCallAnswered(callId: String, isLocalAnswer: Boolean) {
|
|
468
|
+
val context = requireContext()
|
|
459
469
|
Log.d(TAG, "coreCallAnswered: $callId, isLocalAnswer: $isLocalAnswer")
|
|
460
470
|
|
|
461
471
|
val callInfo = activeCalls[callId]
|
|
@@ -466,7 +476,7 @@ object CallEngine {
|
|
|
466
476
|
|
|
467
477
|
stopRingtone()
|
|
468
478
|
stopRingback()
|
|
469
|
-
cancelIncomingCallUI(
|
|
479
|
+
cancelIncomingCallUI()
|
|
470
480
|
requestAudioFocus()
|
|
471
481
|
|
|
472
482
|
activeCalls[callId] = callInfo.copy(state = CallState.ACTIVE)
|
|
@@ -480,10 +490,10 @@ object CallEngine {
|
|
|
480
490
|
}
|
|
481
491
|
}
|
|
482
492
|
|
|
483
|
-
bringAppToForeground(
|
|
484
|
-
startForegroundService(
|
|
485
|
-
keepScreenAwake(
|
|
486
|
-
resetAudioMode(
|
|
493
|
+
bringAppToForeground()
|
|
494
|
+
startForegroundService() // Fixed: Ensure foreground service is running
|
|
495
|
+
keepScreenAwake(true)
|
|
496
|
+
resetAudioMode()
|
|
487
497
|
updateLockScreenBypass()
|
|
488
498
|
updateForegroundNotification()
|
|
489
499
|
|
|
@@ -514,12 +524,11 @@ object CallEngine {
|
|
|
514
524
|
}
|
|
515
525
|
|
|
516
526
|
// --- Call Control Methods ---
|
|
517
|
-
fun holdCall(
|
|
527
|
+
fun holdCall(callId: String) {
|
|
518
528
|
holdCallInternal(callId, heldBySystem = false)
|
|
519
529
|
}
|
|
520
530
|
|
|
521
|
-
|
|
522
|
-
fun setOnHold(context: Context, callId: String, onHold: Boolean) {
|
|
531
|
+
fun setOnHold(callId: String, onHold: Boolean) {
|
|
523
532
|
Log.d(TAG, "setOnHold: $callId, onHold: $onHold")
|
|
524
533
|
|
|
525
534
|
val callInfo = activeCalls[callId]
|
|
@@ -560,7 +569,7 @@ object CallEngine {
|
|
|
560
569
|
updateLockScreenBypass()
|
|
561
570
|
}
|
|
562
571
|
|
|
563
|
-
fun unholdCall(
|
|
572
|
+
fun unholdCall(callId: String) {
|
|
564
573
|
unholdCallInternal(callId, resumedBySystem = false)
|
|
565
574
|
}
|
|
566
575
|
|
|
@@ -572,16 +581,10 @@ object CallEngine {
|
|
|
572
581
|
return
|
|
573
582
|
}
|
|
574
583
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
emitEvent(CallEventType.CALL_UNHOLD_FAILED, JSONObject().apply {
|
|
580
|
-
put("callId", callId)
|
|
581
|
-
put("reason", "Could not obtain audio focus")
|
|
582
|
-
})
|
|
583
|
-
return
|
|
584
|
-
}
|
|
584
|
+
// Fixed: Simplified audio focus check to prevent UNHELD FAILED
|
|
585
|
+
if (!hasAudioFocus && !resumedBySystem && !requestAudioFocus()) {
|
|
586
|
+
Log.w(TAG, "Failed to get audio focus for unhold - but continuing anyway")
|
|
587
|
+
// Don't emit UNHELD FAILED - just continue
|
|
585
588
|
}
|
|
586
589
|
|
|
587
590
|
activeCalls[callId] = callInfo.copy(
|
|
@@ -599,21 +602,21 @@ object CallEngine {
|
|
|
599
602
|
Log.d(TAG, "Call $callId successfully unheld")
|
|
600
603
|
}
|
|
601
604
|
|
|
602
|
-
fun muteCall(
|
|
603
|
-
setMutedInternal(
|
|
605
|
+
fun muteCall(callId: String) {
|
|
606
|
+
setMutedInternal(callId, true)
|
|
604
607
|
}
|
|
605
608
|
|
|
606
|
-
fun unmuteCall(
|
|
607
|
-
setMutedInternal(
|
|
609
|
+
fun unmuteCall(callId: String) {
|
|
610
|
+
setMutedInternal(callId, false)
|
|
608
611
|
}
|
|
609
612
|
|
|
610
|
-
|
|
611
|
-
fun setMuted(context: Context, callId: String, muted: Boolean) {
|
|
613
|
+
fun setMuted(callId: String, muted: Boolean) {
|
|
612
614
|
Log.d(TAG, "setMuted: $callId, muted: $muted")
|
|
613
|
-
setMutedInternal(
|
|
615
|
+
setMutedInternal(callId, muted)
|
|
614
616
|
}
|
|
615
617
|
|
|
616
|
-
private fun setMutedInternal(
|
|
618
|
+
private fun setMutedInternal(callId: String, muted: Boolean) {
|
|
619
|
+
val context = requireContext()
|
|
617
620
|
val callInfo = activeCalls[callId]
|
|
618
621
|
if (callInfo == null) {
|
|
619
622
|
Log.w(TAG, "Cannot set mute state for call $callId - not found in active calls")
|
|
@@ -626,7 +629,6 @@ object CallEngine {
|
|
|
626
629
|
audioManager?.isMicrophoneMute = muted
|
|
627
630
|
|
|
628
631
|
if (wasMuted != muted) {
|
|
629
|
-
lastMuteState = muted
|
|
630
632
|
val eventType = if (muted) CallEventType.CALL_MUTED else CallEventType.CALL_UNMUTED
|
|
631
633
|
emitEvent(eventType, JSONObject().put("callId", callId))
|
|
632
634
|
Log.d(TAG, "Call $callId mute state changed to: $muted")
|
|
@@ -634,14 +636,12 @@ object CallEngine {
|
|
|
634
636
|
}
|
|
635
637
|
|
|
636
638
|
// --- Call End Management ---
|
|
637
|
-
fun endCall(
|
|
638
|
-
appContext = context.applicationContext
|
|
639
|
+
fun endCall(callId: String) {
|
|
639
640
|
Log.d(TAG, "endCall: $callId")
|
|
640
641
|
endCallInternal(callId)
|
|
641
642
|
}
|
|
642
643
|
|
|
643
|
-
|
|
644
|
-
fun endAllCalls(context: Context) {
|
|
644
|
+
fun endAllCalls() {
|
|
645
645
|
Log.d(TAG, "endAllCalls: Ending all active calls.")
|
|
646
646
|
if (activeCalls.isEmpty()) {
|
|
647
647
|
Log.d(TAG, "No active calls, nothing to do.")
|
|
@@ -657,7 +657,7 @@ object CallEngine {
|
|
|
657
657
|
callMetadata.clear()
|
|
658
658
|
currentCallId = null
|
|
659
659
|
|
|
660
|
-
finalCleanup(
|
|
660
|
+
finalCleanup()
|
|
661
661
|
updateLockScreenBypass()
|
|
662
662
|
}
|
|
663
663
|
|
|
@@ -678,7 +678,7 @@ object CallEngine {
|
|
|
678
678
|
|
|
679
679
|
stopRingback()
|
|
680
680
|
stopRingtone()
|
|
681
|
-
|
|
681
|
+
cancelIncomingCallUI()
|
|
682
682
|
|
|
683
683
|
if (currentCallId == callId) {
|
|
684
684
|
currentCallId = activeCalls.filter { it.value.state != CallState.ENDED }.keys.firstOrNull()
|
|
@@ -694,7 +694,7 @@ object CallEngine {
|
|
|
694
694
|
}
|
|
695
695
|
|
|
696
696
|
if (activeCalls.isEmpty()) {
|
|
697
|
-
|
|
697
|
+
finalCleanup()
|
|
698
698
|
} else {
|
|
699
699
|
updateForegroundNotification()
|
|
700
700
|
}
|
|
@@ -717,11 +717,7 @@ object CallEngine {
|
|
|
717
717
|
|
|
718
718
|
// --- Audio Management ---
|
|
719
719
|
fun getAudioDevices(): AudioRoutesInfo {
|
|
720
|
-
val context =
|
|
721
|
-
Log.e(TAG, "getAudioDevices: appContext is not set. Returning default.")
|
|
722
|
-
return AudioRoutesInfo(emptyArray(), "Unknown")
|
|
723
|
-
}
|
|
724
|
-
|
|
720
|
+
val context = requireContext()
|
|
725
721
|
audioManager = audioManager ?: context.getSystemService(Context.AUDIO_SERVICE) as? AudioManager ?: run {
|
|
726
722
|
Log.e(TAG, "getAudioDevices: AudioManager is null. Returning default.")
|
|
727
723
|
return AudioRoutesInfo(emptyArray(), "Unknown")
|
|
@@ -764,7 +760,8 @@ object CallEngine {
|
|
|
764
760
|
return result
|
|
765
761
|
}
|
|
766
762
|
|
|
767
|
-
fun setAudioRoute(
|
|
763
|
+
fun setAudioRoute(route: String) {
|
|
764
|
+
val context = requireContext()
|
|
768
765
|
audioManager = audioManager ?: context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
|
769
766
|
Log.d(TAG, "Attempting to set audio route to: $route. Current mode: ${audioManager?.mode}")
|
|
770
767
|
|
|
@@ -815,7 +812,7 @@ object CallEngine {
|
|
|
815
812
|
}
|
|
816
813
|
}
|
|
817
814
|
|
|
818
|
-
private fun setInitialAudioRoute(
|
|
815
|
+
private fun setInitialAudioRoute(callType: String) {
|
|
819
816
|
val availableDevices = getAudioDevices()
|
|
820
817
|
|
|
821
818
|
val defaultRoute = when {
|
|
@@ -826,10 +823,11 @@ object CallEngine {
|
|
|
826
823
|
}
|
|
827
824
|
|
|
828
825
|
Log.d(TAG, "Setting initial audio route for $callType call: $defaultRoute")
|
|
829
|
-
setAudioRoute(
|
|
826
|
+
setAudioRoute(defaultRoute)
|
|
830
827
|
}
|
|
831
828
|
|
|
832
|
-
fun resetAudioMode(
|
|
829
|
+
private fun resetAudioMode() {
|
|
830
|
+
val context = requireContext()
|
|
833
831
|
audioManager = audioManager ?: context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
|
834
832
|
if (activeCalls.isEmpty()) {
|
|
835
833
|
Log.d(TAG, "Resetting audio mode to NORMAL as no active calls remain.")
|
|
@@ -856,14 +854,15 @@ object CallEngine {
|
|
|
856
854
|
}
|
|
857
855
|
}
|
|
858
856
|
|
|
859
|
-
fun registerAudioDeviceCallback(
|
|
860
|
-
|
|
857
|
+
fun registerAudioDeviceCallback() {
|
|
858
|
+
val context = requireContext()
|
|
861
859
|
audioManager = audioManager ?: context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
|
862
860
|
audioManager?.registerAudioDeviceCallback(audioDeviceCallback, null)
|
|
863
861
|
Log.d(TAG, "Audio device callback registered.")
|
|
864
862
|
}
|
|
865
863
|
|
|
866
|
-
fun unregisterAudioDeviceCallback(
|
|
864
|
+
fun unregisterAudioDeviceCallback() {
|
|
865
|
+
val context = requireContext()
|
|
867
866
|
audioManager = audioManager ?: context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
|
868
867
|
audioManager?.unregisterAudioDeviceCallback(audioDeviceCallback)
|
|
869
868
|
Log.d(TAG, "Audio device callback unregistered.")
|
|
@@ -886,7 +885,8 @@ object CallEngine {
|
|
|
886
885
|
}
|
|
887
886
|
|
|
888
887
|
// --- Screen Management ---
|
|
889
|
-
fun keepScreenAwake(
|
|
888
|
+
fun keepScreenAwake(keepAwake: Boolean) {
|
|
889
|
+
val context = requireContext()
|
|
890
890
|
val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
|
|
891
891
|
if (keepAwake) {
|
|
892
892
|
if (wakeLock == null || !wakeLock!!.isHeld) {
|
|
@@ -943,7 +943,8 @@ object CallEngine {
|
|
|
943
943
|
}
|
|
944
944
|
|
|
945
945
|
// --- Notification Management ---
|
|
946
|
-
private fun createNotificationChannel(
|
|
946
|
+
private fun createNotificationChannel() {
|
|
947
|
+
val context = requireContext()
|
|
947
948
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
948
949
|
val channel = NotificationChannel(
|
|
949
950
|
NOTIF_CHANNEL_ID,
|
|
@@ -974,9 +975,10 @@ object CallEngine {
|
|
|
974
975
|
}
|
|
975
976
|
}
|
|
976
977
|
|
|
977
|
-
fun showIncomingCallUI(
|
|
978
|
+
private fun showIncomingCallUI(callId: String, callerName: String, callType: String) {
|
|
979
|
+
val context = requireContext()
|
|
978
980
|
Log.d(TAG, "Showing incoming call UI for $callId, caller: $callerName, callType: $callType")
|
|
979
|
-
createNotificationChannel(
|
|
981
|
+
createNotificationChannel()
|
|
980
982
|
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
981
983
|
|
|
982
984
|
val answerIntent = Intent(context, CallNotificationActionReceiver::class.java).apply {
|
|
@@ -1035,13 +1037,14 @@ object CallEngine {
|
|
|
1035
1037
|
notificationManager.notify(NOTIF_ID, notification)
|
|
1036
1038
|
|
|
1037
1039
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
|
|
1038
|
-
playRingtone(
|
|
1040
|
+
playRingtone()
|
|
1039
1041
|
}
|
|
1040
1042
|
|
|
1041
|
-
setInitialAudioRoute(
|
|
1043
|
+
setInitialAudioRoute(callType)
|
|
1042
1044
|
}
|
|
1043
1045
|
|
|
1044
|
-
fun cancelIncomingCallUI(
|
|
1046
|
+
private fun cancelIncomingCallUI() {
|
|
1047
|
+
val context = requireContext()
|
|
1045
1048
|
Log.d(TAG, "Cancelling incoming call UI.")
|
|
1046
1049
|
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
1047
1050
|
notificationManager.cancel(NOTIF_ID)
|
|
@@ -1049,7 +1052,8 @@ object CallEngine {
|
|
|
1049
1052
|
}
|
|
1050
1053
|
|
|
1051
1054
|
// --- Service Management ---
|
|
1052
|
-
fun startForegroundService(
|
|
1055
|
+
private fun startForegroundService() {
|
|
1056
|
+
val context = requireContext()
|
|
1053
1057
|
Log.d(TAG, "Starting CallForegroundService.")
|
|
1054
1058
|
|
|
1055
1059
|
val currentCall = activeCalls.values.find {
|
|
@@ -1074,13 +1078,15 @@ object CallEngine {
|
|
|
1074
1078
|
}
|
|
1075
1079
|
}
|
|
1076
1080
|
|
|
1077
|
-
fun stopForegroundService(
|
|
1081
|
+
private fun stopForegroundService() {
|
|
1082
|
+
val context = requireContext()
|
|
1078
1083
|
Log.d(TAG, "Stopping CallForegroundService.")
|
|
1079
1084
|
val intent = Intent(context, CallForegroundService::class.java)
|
|
1080
1085
|
context.stopService(intent)
|
|
1081
1086
|
}
|
|
1082
1087
|
|
|
1083
|
-
fun bringAppToForeground(
|
|
1088
|
+
private fun bringAppToForeground() {
|
|
1089
|
+
val context = requireContext()
|
|
1084
1090
|
val packageName = context.packageName
|
|
1085
1091
|
val launchIntent = context.packageManager.getLaunchIntentForPackage(packageName)
|
|
1086
1092
|
launchIntent?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
|
@@ -1105,9 +1111,10 @@ object CallEngine {
|
|
|
1105
1111
|
}
|
|
1106
1112
|
|
|
1107
1113
|
// --- Phone Account Management ---
|
|
1108
|
-
private fun registerPhoneAccount(
|
|
1114
|
+
private fun registerPhoneAccount() {
|
|
1115
|
+
val context = requireContext()
|
|
1109
1116
|
val telecomManager = context.getSystemService(Context.TELECOM_SERVICE) as TelecomManager
|
|
1110
|
-
val phoneAccountHandle = getPhoneAccountHandle(
|
|
1117
|
+
val phoneAccountHandle = getPhoneAccountHandle()
|
|
1111
1118
|
|
|
1112
1119
|
if (telecomManager.getPhoneAccount(phoneAccountHandle) == null) {
|
|
1113
1120
|
val phoneAccount = PhoneAccount.builder(phoneAccountHandle, "PingMe Call")
|
|
@@ -1127,7 +1134,8 @@ object CallEngine {
|
|
|
1127
1134
|
}
|
|
1128
1135
|
}
|
|
1129
1136
|
|
|
1130
|
-
private fun getPhoneAccountHandle(
|
|
1137
|
+
private fun getPhoneAccountHandle(): PhoneAccountHandle {
|
|
1138
|
+
val context = requireContext()
|
|
1131
1139
|
return PhoneAccountHandle(
|
|
1132
1140
|
ComponentName(context, MyConnectionService::class.java),
|
|
1133
1141
|
PHONE_ACCOUNT_ID
|
|
@@ -1135,7 +1143,8 @@ object CallEngine {
|
|
|
1135
1143
|
}
|
|
1136
1144
|
|
|
1137
1145
|
// --- Media Management ---
|
|
1138
|
-
fun playRingtone(
|
|
1146
|
+
private fun playRingtone() {
|
|
1147
|
+
val context = requireContext()
|
|
1139
1148
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
1140
1149
|
Log.d(TAG, "playRingtone: Android S+ detected, system will handle ringtone via Telecom.")
|
|
1141
1150
|
return
|
|
@@ -1155,7 +1164,7 @@ object CallEngine {
|
|
|
1155
1164
|
}
|
|
1156
1165
|
}
|
|
1157
1166
|
|
|
1158
|
-
fun stopRingtone() {
|
|
1167
|
+
private fun stopRingtone() {
|
|
1159
1168
|
try {
|
|
1160
1169
|
if (ringtone?.isPlaying == true) {
|
|
1161
1170
|
ringtone?.stop()
|
|
@@ -1168,14 +1177,15 @@ object CallEngine {
|
|
|
1168
1177
|
}
|
|
1169
1178
|
|
|
1170
1179
|
private fun startRingback() {
|
|
1180
|
+
val context = requireContext()
|
|
1171
1181
|
if (ringbackPlayer?.isPlaying == true) {
|
|
1172
1182
|
Log.d(TAG, "Ringback tone already playing.")
|
|
1173
1183
|
return
|
|
1174
1184
|
}
|
|
1175
1185
|
|
|
1176
1186
|
try {
|
|
1177
|
-
val ringbackUri = Uri.parse("android.resource://${
|
|
1178
|
-
ringbackPlayer = MediaPlayer.create(
|
|
1187
|
+
val ringbackUri = Uri.parse("android.resource://${context.packageName}/raw/ringback_tone")
|
|
1188
|
+
ringbackPlayer = MediaPlayer.create(context, ringbackUri)
|
|
1179
1189
|
if (ringbackPlayer == null) {
|
|
1180
1190
|
Log.e(TAG, "Failed to create MediaPlayer for ringback. Check raw/ringback_tone.mp3 exists.")
|
|
1181
1191
|
return
|
|
@@ -1212,7 +1222,7 @@ object CallEngine {
|
|
|
1212
1222
|
}
|
|
1213
1223
|
|
|
1214
1224
|
private fun updateForegroundNotification() {
|
|
1215
|
-
val context =
|
|
1225
|
+
val context = requireContext()
|
|
1216
1226
|
val activeCall = activeCalls.values.find { it.state == CallState.ACTIVE }
|
|
1217
1227
|
val heldCall = activeCalls.values.find { it.state == CallState.HELD }
|
|
1218
1228
|
|
|
@@ -1233,11 +1243,41 @@ object CallEngine {
|
|
|
1233
1243
|
}
|
|
1234
1244
|
}
|
|
1235
1245
|
|
|
1236
|
-
private fun finalCleanup(
|
|
1246
|
+
private fun finalCleanup() {
|
|
1237
1247
|
Log.d(TAG, "Performing final cleanup - no active calls remaining")
|
|
1238
|
-
stopForegroundService(
|
|
1239
|
-
keepScreenAwake(
|
|
1240
|
-
resetAudioMode(
|
|
1248
|
+
stopForegroundService()
|
|
1249
|
+
keepScreenAwake(false)
|
|
1250
|
+
resetAudioMode()
|
|
1241
1251
|
isSystemCallActive = false
|
|
1242
1252
|
}
|
|
1253
|
+
|
|
1254
|
+
// --- Lifecycle Management ---
|
|
1255
|
+
fun onApplicationTerminate() {
|
|
1256
|
+
Log.d(TAG, "Application terminating - cleaning up all calls")
|
|
1257
|
+
|
|
1258
|
+
// End all calls properly
|
|
1259
|
+
activeCalls.keys.toList().forEach { callId ->
|
|
1260
|
+
val connection = telecomConnections[callId]
|
|
1261
|
+
connection?.setDisconnected(DisconnectCause(DisconnectCause.LOCAL))
|
|
1262
|
+
connection?.destroy()
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
// Clear all state
|
|
1266
|
+
activeCalls.clear()
|
|
1267
|
+
telecomConnections.clear()
|
|
1268
|
+
callMetadata.clear()
|
|
1269
|
+
currentCallId = null
|
|
1270
|
+
|
|
1271
|
+
// Release resources
|
|
1272
|
+
finalCleanup()
|
|
1273
|
+
|
|
1274
|
+
// Clear callbacks
|
|
1275
|
+
lockScreenBypassCallbacks.clear()
|
|
1276
|
+
eventHandler = null
|
|
1277
|
+
cachedEvents.clear()
|
|
1278
|
+
|
|
1279
|
+
// Reset initialization
|
|
1280
|
+
isInitialized.set(false)
|
|
1281
|
+
appContext = null
|
|
1282
|
+
}
|
|
1243
1283
|
}
|
package/android/src/main/java/com/margelo/nitro/qusaieilouti99/callmanager/CallForegroundService.kt
CHANGED
|
@@ -172,7 +172,7 @@ class CallForegroundService : Service() {
|
|
|
172
172
|
|
|
173
173
|
override fun onTaskRemoved(rootIntent: Intent?) {
|
|
174
174
|
Log.d(TAG, "onTaskRemoved: User swiped app from recents. Ending all calls.")
|
|
175
|
-
CallEngine.
|
|
175
|
+
CallEngine.onApplicationTerminate()
|
|
176
176
|
super.onTaskRemoved(rootIntent)
|
|
177
177
|
stopSelf()
|
|
178
178
|
}
|
|
@@ -181,5 +181,10 @@ class CallForegroundService : Service() {
|
|
|
181
181
|
super.onDestroy()
|
|
182
182
|
Log.d(TAG, "Service onDestroy. Stopping foreground.")
|
|
183
183
|
stopForeground(true)
|
|
184
|
+
|
|
185
|
+
// Additional cleanup when service is destroyed
|
|
186
|
+
if (!CallEngine.isCallActive()) {
|
|
187
|
+
CallEngine.onApplicationTerminate()
|
|
188
|
+
}
|
|
184
189
|
}
|
|
185
190
|
}
|
|
@@ -8,55 +8,72 @@ class CallManager : HybridCallManagerSpec() {
|
|
|
8
8
|
|
|
9
9
|
private val TAG = "CallManager"
|
|
10
10
|
|
|
11
|
+
init {
|
|
12
|
+
// Initialize CallEngine as soon as CallManager is created
|
|
13
|
+
try {
|
|
14
|
+
val context = com.facebook.react.bridge.ReactApplicationContext.getCurrentApplication()
|
|
15
|
+
if (context != null) {
|
|
16
|
+
CallEngine.initialize(context.applicationContext)
|
|
17
|
+
}
|
|
18
|
+
} catch (e: Exception) {
|
|
19
|
+
Log.w(TAG, "Could not initialize CallEngine in init, will try during first call")
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
private fun ensureInitialized() {
|
|
24
|
+
if (!CallEngine.isInitialized()) {
|
|
25
|
+
try {
|
|
26
|
+
val context = com.facebook.react.bridge.ReactApplicationContext.getCurrentApplication()
|
|
27
|
+
?: throw IllegalStateException("No application context available")
|
|
28
|
+
CallEngine.initialize(context.applicationContext)
|
|
29
|
+
} catch (e: Exception) {
|
|
30
|
+
Log.e(TAG, "Failed to initialize CallEngine: ${e.message}", e)
|
|
31
|
+
throw IllegalStateException("CallEngine not initialized and cannot initialize: ${e.message}")
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
11
36
|
override fun endCall(callId: String) {
|
|
12
37
|
Log.d(TAG, "endCall requested for callId: $callId")
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
} ?: Log.e(TAG, "App context not set for endCall.")
|
|
38
|
+
ensureInitialized()
|
|
39
|
+
CallEngine.endCall(callId)
|
|
16
40
|
}
|
|
17
41
|
|
|
18
42
|
override fun endAllCalls() {
|
|
19
43
|
Log.d(TAG, "endAllCalls requested")
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
} ?: Log.e(TAG, "App context not set for endAllCalls.")
|
|
44
|
+
ensureInitialized()
|
|
45
|
+
CallEngine.endAllCalls()
|
|
23
46
|
}
|
|
24
47
|
|
|
25
48
|
override fun silenceRingtone() {
|
|
26
49
|
Log.d(TAG, "silenceRingtone requested.")
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
} ?: Log.e(TAG, "App context not set for silenceRingtone.")
|
|
50
|
+
ensureInitialized()
|
|
51
|
+
CallEngine.stopRingtone()
|
|
30
52
|
}
|
|
31
53
|
|
|
32
54
|
override fun getAudioDevices(): AudioRoutesInfo {
|
|
33
55
|
Log.d(TAG, "getAudioDevices requested.")
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
} ?: run {
|
|
37
|
-
Log.e(TAG, "App context not set for getAudioDevices. Returning empty AudioRoutesInfo.")
|
|
38
|
-
AudioRoutesInfo(emptyArray(), "Unknown")
|
|
39
|
-
}
|
|
56
|
+
ensureInitialized()
|
|
57
|
+
return CallEngine.getAudioDevices()
|
|
40
58
|
}
|
|
41
59
|
|
|
42
60
|
override fun setAudioRoute(route: String) {
|
|
43
61
|
Log.d(TAG, "setAudioRoute requested for route: $route")
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
} ?: Log.e(TAG, "App context not set for setAudioRoute.")
|
|
62
|
+
ensureInitialized()
|
|
63
|
+
CallEngine.setAudioRoute(route)
|
|
47
64
|
}
|
|
48
65
|
|
|
49
66
|
override fun keepScreenAwake(keepAwake: Boolean) {
|
|
50
67
|
Log.d(TAG, "keepScreenAwake requested: $keepAwake")
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
} ?: Log.e(TAG, "App context not set for keepAwake.")
|
|
68
|
+
ensureInitialized()
|
|
69
|
+
CallEngine.keepScreenAwake(keepAwake)
|
|
54
70
|
}
|
|
55
71
|
|
|
56
72
|
override fun addListener(
|
|
57
73
|
listener: (event: CallEventType, payload: String) -> Unit
|
|
58
74
|
): () -> Unit {
|
|
59
75
|
Log.d(TAG, "addListener called with listener: $listener")
|
|
76
|
+
ensureInitialized()
|
|
60
77
|
CallEngine.setEventHandler(listener)
|
|
61
78
|
return {
|
|
62
79
|
CallEngine.setEventHandler(null)
|
|
@@ -66,36 +83,31 @@ class CallManager : HybridCallManagerSpec() {
|
|
|
66
83
|
|
|
67
84
|
override fun startOutgoingCall(callId: String, callType: String, targetName: String, metadata: String?) {
|
|
68
85
|
Log.d(TAG, "startOutgoingCall requested: callId=$callId, callType=$callType, targetName=$targetName")
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
} ?: Log.e(TAG, "App context not set for startOutgoingCall.")
|
|
86
|
+
ensureInitialized()
|
|
87
|
+
CallEngine.startOutgoingCall(callId, callType, targetName, metadata)
|
|
72
88
|
}
|
|
73
89
|
|
|
74
90
|
override fun startCall(callId: String, callType: String, targetName: String, metadata: String?) {
|
|
75
91
|
Log.d(TAG, "startCall requested: callId=$callId, callType=$callType, targetName=$targetName")
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
} ?: Log.e(TAG, "App context not set for startCall.")
|
|
92
|
+
ensureInitialized()
|
|
93
|
+
CallEngine.startCall(callId, callType, targetName, metadata)
|
|
79
94
|
}
|
|
80
95
|
|
|
81
96
|
override fun callAnswered(callId: String) {
|
|
82
97
|
Log.d(TAG, "callAnswered (from JS) requested for callId: $callId")
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
} ?: Log.e(TAG, "App context not set for callAnswered.")
|
|
98
|
+
ensureInitialized()
|
|
99
|
+
CallEngine.callAnsweredFromJS(callId)
|
|
86
100
|
}
|
|
87
101
|
|
|
88
102
|
override fun setOnHold(callId: String, onHold: Boolean) {
|
|
89
103
|
Log.d(TAG, "setOnHold requested for callId: $callId, onHold: $onHold")
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
} ?: Log.e(TAG, "App context not set for setOnHold.")
|
|
104
|
+
ensureInitialized()
|
|
105
|
+
CallEngine.setOnHold(callId, onHold)
|
|
93
106
|
}
|
|
94
107
|
|
|
95
108
|
override fun setMuted(callId: String, muted: Boolean) {
|
|
96
109
|
Log.d(TAG, "setMuted requested for callId: $callId, muted: $muted")
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
} ?: Log.e(TAG, "App context not set for setMuted.")
|
|
110
|
+
ensureInitialized()
|
|
111
|
+
CallEngine.setMuted(callId, muted)
|
|
100
112
|
}
|
|
101
113
|
}
|