@qusaieilouti99/call-manager 0.1.37 → 0.1.38
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 +57 -58
- package/android/src/main/java/com/margelo/nitro/qusaieilouti99/callmanager/CallForegroundService.kt +2 -2
- package/android/src/main/java/com/margelo/nitro/qusaieilouti99/callmanager/CallManager.kt +7 -14
- package/package.json +1 -1
|
@@ -12,16 +12,16 @@ import android.media.MediaPlayer
|
|
|
12
12
|
import android.media.RingtoneManager
|
|
13
13
|
import android.net.Uri
|
|
14
14
|
import android.os.Build
|
|
15
|
-
import android.os.Bundle
|
|
15
|
+
import android.os.Bundle
|
|
16
16
|
import android.os.PowerManager
|
|
17
|
-
import android.telecom.CallAudioState
|
|
18
|
-
import android.telecom.Connection
|
|
19
|
-
import android.telecom.DisconnectCause
|
|
20
|
-
import android.telecom.PhoneAccount
|
|
21
|
-
import android.telecom.PhoneAccountHandle
|
|
22
|
-
import android.telecom.TelecomManager
|
|
23
|
-
import android.telecom.VideoProfile
|
|
24
|
-
import android.util.Log
|
|
17
|
+
import android.telecom.CallAudioState
|
|
18
|
+
import android.telecom.Connection
|
|
19
|
+
import android.telecom.DisconnectCause
|
|
20
|
+
import android.telecom.PhoneAccount
|
|
21
|
+
import android.telecom.PhoneAccountHandle
|
|
22
|
+
import android.telecom.TelecomManager
|
|
23
|
+
import android.telecom.VideoProfile
|
|
24
|
+
import android.util.Log
|
|
25
25
|
import android.graphics.Color
|
|
26
26
|
import android.app.Person
|
|
27
27
|
import org.json.JSONArray
|
|
@@ -38,15 +38,15 @@ object CallEngine {
|
|
|
38
38
|
private const val FOREGROUND_NOTIF_ID = 1001
|
|
39
39
|
|
|
40
40
|
private var ringtone: android.media.Ringtone? = null
|
|
41
|
-
private var ringbackPlayer: MediaPlayer? = null
|
|
41
|
+
private var ringbackPlayer: MediaPlayer? = null
|
|
42
42
|
private var audioManager: AudioManager? = null
|
|
43
43
|
private var wakeLock: PowerManager.WakeLock? = null
|
|
44
44
|
private var appContext: Context? = null
|
|
45
45
|
|
|
46
46
|
// --- Multi-call state ---
|
|
47
|
-
private val activeCalls = ConcurrentHashMap<String, CallInfo>()
|
|
48
|
-
private val telecomConnections = ConcurrentHashMap<String, Connection>()
|
|
49
|
-
private var currentCallId: String? = null
|
|
47
|
+
private val activeCalls = ConcurrentHashMap<String, CallInfo>()
|
|
48
|
+
private val telecomConnections = ConcurrentHashMap<String, Connection>()
|
|
49
|
+
private var currentCallId: String? = null
|
|
50
50
|
private var canMakeMultipleCalls: Boolean = true
|
|
51
51
|
|
|
52
52
|
data class CallInfo(
|
|
@@ -62,12 +62,11 @@ object CallEngine {
|
|
|
62
62
|
|
|
63
63
|
// --- Event handler for JS ---
|
|
64
64
|
private var eventHandler: ((CallEventType, String) -> Unit)? = null
|
|
65
|
-
private val cachedEvents = mutableListOf<Pair<CallEventType, String>>()
|
|
65
|
+
private val cachedEvents = mutableListOf<Pair<CallEventType, String>>()
|
|
66
66
|
|
|
67
67
|
fun setEventHandler(handler: ((CallEventType, String) -> Unit)?) {
|
|
68
68
|
Log.d(TAG, "setEventHandler called. Handler present: ${handler != null}")
|
|
69
69
|
eventHandler = handler
|
|
70
|
-
// Emit cached events if a handler is now available
|
|
71
70
|
handler?.let { h ->
|
|
72
71
|
if (cachedEvents.isNotEmpty()) {
|
|
73
72
|
Log.d(TAG, "Emitting ${cachedEvents.size} cached events.")
|
|
@@ -124,6 +123,7 @@ object CallEngine {
|
|
|
124
123
|
return result
|
|
125
124
|
}
|
|
126
125
|
|
|
126
|
+
// This is for INCOMING calls using TelecomManager.addNewIncomingCall
|
|
127
127
|
fun reportIncomingCall(context: Context, callId: String, callData: String) {
|
|
128
128
|
appContext = context.applicationContext
|
|
129
129
|
Log.d(TAG, "reportIncomingCall: $callId, $callData")
|
|
@@ -133,12 +133,12 @@ object CallEngine {
|
|
|
133
133
|
json.optString("name", "Unknown")
|
|
134
134
|
} catch (e: Exception) { "Unknown" }
|
|
135
135
|
|
|
136
|
-
val parsedCallType = try {
|
|
136
|
+
val parsedCallType = try {
|
|
137
137
|
val json = JSONObject(callData)
|
|
138
138
|
json.optString("callType", "Audio")
|
|
139
139
|
} catch (e: Exception) { "Audio" }
|
|
140
140
|
|
|
141
|
-
val isVideoCallBoolean = parsedCallType == "Video"
|
|
141
|
+
val isVideoCallBoolean = parsedCallType == "Video"
|
|
142
142
|
|
|
143
143
|
if (!canMakeMultipleCalls && activeCalls.isNotEmpty()) {
|
|
144
144
|
Log.d(TAG, "Can't make multiple calls, holding existing calls.")
|
|
@@ -150,14 +150,15 @@ object CallEngine {
|
|
|
150
150
|
Log.d(TAG, "Call $callId added to activeCalls. State: INCOMING, callType: $parsedCallType")
|
|
151
151
|
|
|
152
152
|
showIncomingCallUI(context, callId, callerName, parsedCallType)
|
|
153
|
-
registerPhoneAccount(context)
|
|
153
|
+
registerPhoneAccount(context) // Register phone account so Telecom can handle incoming
|
|
154
154
|
val telecomManager = context.getSystemService(Context.TELECOM_SERVICE) as TelecomManager
|
|
155
155
|
val phoneAccountHandle = getPhoneAccountHandle(context)
|
|
156
156
|
val extras = Bundle().apply {
|
|
157
|
-
putString(MyConnectionService.EXTRA_CALL_DATA, callData)
|
|
158
|
-
putBoolean(MyConnectionService.EXTRA_IS_VIDEO_CALL_BOOLEAN, isVideoCallBoolean)
|
|
157
|
+
putString(MyConnectionService.EXTRA_CALL_DATA, callData)
|
|
158
|
+
putBoolean(MyConnectionService.EXTRA_IS_VIDEO_CALL_BOOLEAN, isVideoCallBoolean)
|
|
159
159
|
}
|
|
160
160
|
try {
|
|
161
|
+
// This API is fundamental for INCOMING call integration with Telecom.
|
|
161
162
|
telecomManager.addNewIncomingCall(phoneAccountHandle, extras)
|
|
162
163
|
startForegroundService(context)
|
|
163
164
|
Log.d(TAG, "Successfully reported incoming call to TelecomManager for $callId")
|
|
@@ -171,6 +172,7 @@ object CallEngine {
|
|
|
171
172
|
notifyCallStateChanged(context)
|
|
172
173
|
}
|
|
173
174
|
|
|
175
|
+
// CORRECTED: Uses TelecomManager.placeCall for outgoing calls.
|
|
174
176
|
fun startOutgoingCall(context: Context, callId: String, callData: String) {
|
|
175
177
|
appContext = context.applicationContext
|
|
176
178
|
Log.d(TAG, "startOutgoingCall: $callId, $callData")
|
|
@@ -180,12 +182,12 @@ object CallEngine {
|
|
|
180
182
|
json.optString("name", "Unknown")
|
|
181
183
|
} catch (e: Exception) { "Unknown" }
|
|
182
184
|
|
|
183
|
-
val parsedCallType = try {
|
|
185
|
+
val parsedCallType = try {
|
|
184
186
|
val json = JSONObject(callData)
|
|
185
187
|
json.optString("callType", "Audio")
|
|
186
188
|
} catch (e: Exception) { "Audio" }
|
|
187
189
|
|
|
188
|
-
val isVideoCallBoolean = parsedCallType == "Video"
|
|
190
|
+
val isVideoCallBoolean = parsedCallType == "Video"
|
|
189
191
|
|
|
190
192
|
if (!canMakeMultipleCalls && activeCalls.isNotEmpty()) {
|
|
191
193
|
Log.d(TAG, "Can't make multiple calls, holding existing calls before outgoing.")
|
|
@@ -196,36 +198,41 @@ object CallEngine {
|
|
|
196
198
|
currentCallId = callId
|
|
197
199
|
Log.d(TAG, "Call $callId added to activeCalls. State: DIALING, callType: $parsedCallType")
|
|
198
200
|
|
|
199
|
-
registerPhoneAccount(context)
|
|
201
|
+
registerPhoneAccount(context) // Ensure PhoneAccount is registered for Telecom to recognize it
|
|
200
202
|
val telecomManager = context.getSystemService(Context.TELECOM_SERVICE) as TelecomManager
|
|
201
203
|
val phoneAccountHandle = getPhoneAccountHandle(context)
|
|
204
|
+
|
|
205
|
+
// For `placeCall`, the address URI is usually required. Use a generic "tel:" scheme with callId or number.
|
|
206
|
+
val addressUri = Uri.fromParts(PhoneAccount.SCHEME_TEL, callId, null) // Using callId as a unique identifier for URI
|
|
207
|
+
|
|
202
208
|
val extras = Bundle().apply {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
//
|
|
209
|
+
// Essential for self-managed apps using placeCall
|
|
210
|
+
putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle)
|
|
211
|
+
// Pass additional data for your ConnectionService to retrieve
|
|
212
|
+
putString(MyConnectionService.EXTRA_CALL_DATA, callData)
|
|
213
|
+
putBoolean(MyConnectionService.EXTRA_IS_VIDEO_CALL_BOOLEAN, isVideoCallBoolean)
|
|
214
|
+
// Hint for initial speakerphone state
|
|
206
215
|
putBoolean(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, isVideoCallBoolean)
|
|
207
216
|
}
|
|
208
217
|
|
|
209
218
|
try {
|
|
210
|
-
//
|
|
211
|
-
|
|
212
|
-
// Imports already checked; assuming minSdkVersion 26+.
|
|
213
|
-
telecomManager.addNewOutgoingCall(phoneAccountHandle, extras)
|
|
219
|
+
// CORRECT API for self-managed outgoing calls that interact with Telecom
|
|
220
|
+
telecomManager.placeCall(addressUri, extras)
|
|
214
221
|
startForegroundService(context)
|
|
215
|
-
Log.d(TAG, "Successfully reported outgoing call to TelecomManager for $callId")
|
|
222
|
+
Log.d(TAG, "Successfully reported outgoing call to TelecomManager via placeCall for $callId")
|
|
216
223
|
startRingback()
|
|
217
224
|
bringAppToForeground(context)
|
|
218
225
|
keepScreenAwake(context, true)
|
|
219
|
-
if (parsedCallType == "Video") {
|
|
226
|
+
if (parsedCallType == "Video") {
|
|
220
227
|
setAudioRoute(context, "Speaker")
|
|
221
228
|
} else {
|
|
222
|
-
setAudioRoute(context, "Earpiece")
|
|
229
|
+
setAudioRoute(context, "Earpiece")
|
|
223
230
|
}
|
|
224
231
|
} catch (e: SecurityException) {
|
|
225
|
-
Log.e(TAG, "SecurityException: Failed to start outgoing call. Check MANAGE_OWN_CALLS permission: ${e.message}", e)
|
|
232
|
+
Log.e(TAG, "SecurityException: Failed to start outgoing call via placeCall. Check MANAGE_OWN_CALLS permission: ${e.message}", e)
|
|
226
233
|
endCall(context, callId)
|
|
227
234
|
} catch (e: Exception) {
|
|
228
|
-
Log.e(TAG, "Failed to start outgoing call: ${e.message}", e)
|
|
235
|
+
Log.e(TAG, "Failed to start outgoing call via placeCall: ${e.message}", e)
|
|
229
236
|
endCall(context, callId)
|
|
230
237
|
}
|
|
231
238
|
notifyCallStateChanged(context)
|
|
@@ -262,7 +269,7 @@ object CallEngine {
|
|
|
262
269
|
|
|
263
270
|
fun holdCall(context: Context, callId: String) {
|
|
264
271
|
Log.d(TAG, "holdCall: $callId")
|
|
265
|
-
|
|
272
|
+
// Always attempt to inform Telecom if a Connection object exists for this callId
|
|
266
273
|
val connection = telecomConnections[callId]
|
|
267
274
|
connection?.setOnHold()
|
|
268
275
|
emitEvent(CallEventType.CALL_HELD, JSONObject().put("callId", callId))
|
|
@@ -272,6 +279,7 @@ object CallEngine {
|
|
|
272
279
|
fun unholdCall(context: Context, callId: String) {
|
|
273
280
|
Log.d(TAG, "unholdCall: $callId")
|
|
274
281
|
activeCalls[callId]?.state = CallState.ACTIVE
|
|
282
|
+
// Always attempt to inform Telecom if a Connection object exists for this callId
|
|
275
283
|
val connection = telecomConnections[callId]
|
|
276
284
|
connection?.setActive()
|
|
277
285
|
emitEvent(CallEventType.CALL_UNHELD, JSONObject().put("callId", callId))
|
|
@@ -309,6 +317,7 @@ object CallEngine {
|
|
|
309
317
|
|
|
310
318
|
cancelIncomingCallUI(context)
|
|
311
319
|
|
|
320
|
+
// Only disconnect from Telecom if a Connection object exists for this callId
|
|
312
321
|
val connection = telecomConnections[callId]
|
|
313
322
|
if (connection != null) {
|
|
314
323
|
connection.setDisconnected(DisconnectCause(DisconnectCause.LOCAL))
|
|
@@ -316,7 +325,7 @@ object CallEngine {
|
|
|
316
325
|
removeTelecomConnection(callId)
|
|
317
326
|
Log.d(TAG, "Telecom Connection for $callId disconnected and destroyed.")
|
|
318
327
|
} else {
|
|
319
|
-
Log.
|
|
328
|
+
Log.d(TAG, "No Telecom Connection found for callId=$callId. Likely an outgoing call not initially handled by Telecom.")
|
|
320
329
|
}
|
|
321
330
|
|
|
322
331
|
if (activeCalls.isEmpty()) {
|
|
@@ -445,11 +454,10 @@ object CallEngine {
|
|
|
445
454
|
|
|
446
455
|
// --- Audio Device Management ---
|
|
447
456
|
|
|
448
|
-
// Returns the generated AudioRoutesInfo data class
|
|
449
457
|
fun getAudioDevices(): AudioRoutesInfo {
|
|
450
|
-
audioManager =
|
|
458
|
+
audioManager = appContext?.getSystemService(Context.AUDIO_SERVICE) as? AudioManager ?: run {
|
|
451
459
|
Log.e(TAG, "getAudioDevices: AudioManager is null or appContext is not set. Returning default.")
|
|
452
|
-
return AudioRoutesInfo(emptyArray(), "Unknown")
|
|
460
|
+
return AudioRoutesInfo(emptyArray(), "Unknown")
|
|
453
461
|
}
|
|
454
462
|
val devices = mutableListOf<String>()
|
|
455
463
|
var currentRoute: String = "Unknown"
|
|
@@ -460,7 +468,7 @@ object CallEngine {
|
|
|
460
468
|
when (device.type) {
|
|
461
469
|
AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, AudioDeviceInfo.TYPE_BLUETOOTH_SCO -> {
|
|
462
470
|
devices.add("Bluetooth")
|
|
463
|
-
if (audioManager?.isBluetoothScoOn == true && !device.isSource) currentRoute = "Bluetooth"
|
|
471
|
+
if (audioManager?.isBluetoothScoOn == true && !device.isSource) currentRoute = "Bluetooth"
|
|
464
472
|
}
|
|
465
473
|
AudioDeviceInfo.TYPE_WIRED_HEADPHONES, AudioDeviceInfo.TYPE_WIRED_HEADSET -> {
|
|
466
474
|
devices.add("Headset")
|
|
@@ -472,8 +480,6 @@ object CallEngine {
|
|
|
472
480
|
}
|
|
473
481
|
AudioDeviceInfo.TYPE_BUILTIN_EARPIECE -> {
|
|
474
482
|
devices.add("Earpiece")
|
|
475
|
-
// Earpiece is active if speaker, bluetooth, and headset are off
|
|
476
|
-
// This heuristic is tricky, relies on other routes being off
|
|
477
483
|
if (audioManager?.isSpeakerphoneOn == false && audioManager?.isWiredHeadsetOn == false && audioManager?.isBluetoothScoOn == false && !device.isSource) {
|
|
478
484
|
currentRoute = "Earpiece"
|
|
479
485
|
}
|
|
@@ -482,17 +488,15 @@ object CallEngine {
|
|
|
482
488
|
}
|
|
483
489
|
}
|
|
484
490
|
} else {
|
|
485
|
-
// Fallback for older APIs. Detection is less reliable.
|
|
486
491
|
devices.add("Speaker")
|
|
487
492
|
devices.add("Earpiece")
|
|
488
493
|
if (audioManager?.isSpeakerphoneOn == true) currentRoute = "Speaker"
|
|
489
|
-
else currentRoute = "Earpiece"
|
|
494
|
+
else currentRoute = "Earpiece"
|
|
490
495
|
}
|
|
491
496
|
|
|
492
497
|
val distinctDevices = devices.distinct().toTypedArray()
|
|
493
498
|
|
|
494
|
-
|
|
495
|
-
if (currentRoute == "Unknown" || currentRoute == "Earpiece") { // If still unknown or defaulted to earpiece
|
|
499
|
+
if (currentRoute == "Unknown" || currentRoute == "Earpiece") {
|
|
496
500
|
if (audioManager?.isBluetoothScoOn == true) {
|
|
497
501
|
currentRoute = "Bluetooth"
|
|
498
502
|
} else if (audioManager?.isSpeakerphoneOn == true) {
|
|
@@ -500,7 +504,7 @@ object CallEngine {
|
|
|
500
504
|
} else if (audioManager?.isWiredHeadsetOn == true) {
|
|
501
505
|
currentRoute = "Headset"
|
|
502
506
|
} else {
|
|
503
|
-
currentRoute = "Earpiece"
|
|
507
|
+
currentRoute = "Earpiece"
|
|
504
508
|
}
|
|
505
509
|
}
|
|
506
510
|
|
|
@@ -542,8 +546,6 @@ object CallEngine {
|
|
|
542
546
|
Log.w(TAG, "Unknown audio route: $route. No action taken.")
|
|
543
547
|
}
|
|
544
548
|
}
|
|
545
|
-
// Emit event immediately with the requested route.
|
|
546
|
-
// The actual route change will be confirmed by onCallAudioStateChanged callback.
|
|
547
549
|
emitEvent(CallEventType.AUDIO_ROUTE_CHANGED, JSONObject().put("route", route))
|
|
548
550
|
}
|
|
549
551
|
|
|
@@ -615,12 +617,12 @@ object CallEngine {
|
|
|
615
617
|
Log.w(TAG, "Cannot emit AudioDevicesChanged: appContext is null.")
|
|
616
618
|
return
|
|
617
619
|
}
|
|
618
|
-
val audioInfo = getAudioDevices()
|
|
619
|
-
val jsonPayload = JSONObject().apply {
|
|
620
|
+
val audioInfo = getAudioDevices()
|
|
621
|
+
val jsonPayload = JSONObject().apply {
|
|
620
622
|
put("devices", JSONArray(audioInfo.devices.toList()))
|
|
621
623
|
put("currentRoute", audioInfo.currentRoute)
|
|
622
624
|
}
|
|
623
|
-
emitEvent(CallEventType.AUDIO_DEVICES_CHANGED, jsonPayload)
|
|
625
|
+
emitEvent(CallEventType.AUDIO_DEVICES_CHANGED, jsonPayload)
|
|
624
626
|
}
|
|
625
627
|
|
|
626
628
|
// --- Call State Change Notification ---
|
|
@@ -669,12 +671,13 @@ object CallEngine {
|
|
|
669
671
|
}
|
|
670
672
|
}
|
|
671
673
|
|
|
674
|
+
// PhoneAccount registration is used for both INCOMING and OUTGOING calls when interacting with Telecom
|
|
672
675
|
private fun registerPhoneAccount(context: Context) {
|
|
673
676
|
val telecomManager = context.getSystemService(Context.TELECOM_SERVICE) as TelecomManager
|
|
674
677
|
val phoneAccountHandle = getPhoneAccountHandle(context)
|
|
675
678
|
if (telecomManager.getPhoneAccount(phoneAccountHandle) == null) {
|
|
676
679
|
val phoneAccount = PhoneAccount.builder(phoneAccountHandle, "PingMe Call")
|
|
677
|
-
.setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED or PhoneAccount.CAPABILITY_SUPPORTS_HOLD)
|
|
680
|
+
.setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED or PhoneAccount.CAPABILITY_SUPPORTS_HOLD)
|
|
678
681
|
.build()
|
|
679
682
|
try {
|
|
680
683
|
telecomManager.registerPhoneAccount(phoneAccount)
|
|
@@ -697,7 +700,6 @@ object CallEngine {
|
|
|
697
700
|
}
|
|
698
701
|
|
|
699
702
|
// --- Ringtone Management (for incoming calls, pre-Android S) ---
|
|
700
|
-
// Can rely on default ringtone without adding custom file for incoming calls.
|
|
701
703
|
fun playRingtone(context: Context) {
|
|
702
704
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
703
705
|
Log.d(TAG, "playRingtone: Android S+ detected, system will handle ringtone via Telecom.")
|
|
@@ -730,15 +732,12 @@ object CallEngine {
|
|
|
730
732
|
}
|
|
731
733
|
|
|
732
734
|
// --- Ringback Tone Management (for outgoing calls) ---
|
|
733
|
-
// Requires `res/raw/ringback_tone.mp3` for custom outgoing ringback.
|
|
734
735
|
private fun startRingback() {
|
|
735
736
|
if (ringbackPlayer != null && ringbackPlayer!!.isPlaying) {
|
|
736
737
|
Log.d(TAG, "Ringback tone already playing.")
|
|
737
738
|
return
|
|
738
739
|
}
|
|
739
740
|
try {
|
|
740
|
-
// Ensure you have `res/raw/ringback_tone.mp3` in your project for this to work.
|
|
741
|
-
// If you don't want a custom ringback, you can play a silent audio file or do nothing here.
|
|
742
741
|
val ringbackUri = Uri.parse("android.resource://${appContext?.packageName}/raw/ringback_tone")
|
|
743
742
|
ringbackPlayer = MediaPlayer.create(appContext, ringbackUri)
|
|
744
743
|
if (ringbackPlayer == null) {
|
package/android/src/main/java/com/margelo/nitro/qusaieilouti99/callmanager/CallForegroundService.kt
CHANGED
|
@@ -8,7 +8,7 @@ import android.content.Intent
|
|
|
8
8
|
import android.os.Build
|
|
9
9
|
import android.util.Log
|
|
10
10
|
import android.os.IBinder
|
|
11
|
-
import android.app.NotificationManager
|
|
11
|
+
import android.app.NotificationManager
|
|
12
12
|
|
|
13
13
|
class CallForegroundService : Service() {
|
|
14
14
|
|
|
@@ -61,7 +61,7 @@ class CallForegroundService : Service() {
|
|
|
61
61
|
NotificationManager.IMPORTANCE_LOW
|
|
62
62
|
)
|
|
63
63
|
channel.description = "Notification for ongoing calls"
|
|
64
|
-
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
64
|
+
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
65
65
|
manager.createNotificationChannel(channel)
|
|
66
66
|
Log.d(TAG, "Foreground notification channel '$CHANNEL_ID' created/updated.")
|
|
67
67
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
package com.margelo.nitro.qusaieilouti99.callmanager
|
|
2
2
|
|
|
3
3
|
import com.facebook.proguard.annotations.DoNotStrip
|
|
4
|
-
import android.util.Log
|
|
4
|
+
import android.util.Log
|
|
5
5
|
import org.json.JSONArray
|
|
6
6
|
import org.json.JSONObject
|
|
7
7
|
|
|
@@ -10,7 +10,6 @@ class CallManager : HybridCallManagerSpec() {
|
|
|
10
10
|
|
|
11
11
|
private val TAG = "CallManager"
|
|
12
12
|
|
|
13
|
-
// Listener type now directly matches the generated Func_void_CallEventType_std__string
|
|
14
13
|
private var currentListener: Func_void_CallEventType_std__string? = null
|
|
15
14
|
|
|
16
15
|
override fun endCall(callId: String) {
|
|
@@ -27,14 +26,13 @@ class CallManager : HybridCallManagerSpec() {
|
|
|
27
26
|
} ?: Log.e(TAG, "App context not set for silenceRingtone.")
|
|
28
27
|
}
|
|
29
28
|
|
|
30
|
-
// Return type changed to AudioRoutesInfo from String
|
|
31
29
|
override fun getAudioDevices(): AudioRoutesInfo {
|
|
32
30
|
Log.d(TAG, "getAudioDevices requested.")
|
|
33
31
|
return CallEngine.getAppContext()?.let {
|
|
34
|
-
CallEngine.getAudioDevices()
|
|
32
|
+
CallEngine.getAudioDevices()
|
|
35
33
|
} ?: run {
|
|
36
34
|
Log.e(TAG, "App context not set for getAudioDevices. Returning empty AudioRoutesInfo.")
|
|
37
|
-
AudioRoutesInfo(emptyArray(), "Unknown")
|
|
35
|
+
AudioRoutesInfo(emptyArray(), "Unknown")
|
|
38
36
|
}
|
|
39
37
|
}
|
|
40
38
|
|
|
@@ -52,19 +50,15 @@ class CallManager : HybridCallManagerSpec() {
|
|
|
52
50
|
} ?: Log.e(TAG, "App context not set for keepAwake.")
|
|
53
51
|
}
|
|
54
52
|
|
|
55
|
-
// Corrected addListener signature to match generated HybridCallManagerSpec
|
|
56
53
|
override fun addListener(
|
|
57
|
-
listener: Func_void_CallEventType_std__string
|
|
58
|
-
): Func_void {
|
|
54
|
+
listener: Func_void_CallEventType_std__string
|
|
55
|
+
): Func_void {
|
|
59
56
|
Log.d(TAG, "addListener called with listener: $listener")
|
|
60
|
-
// Store the incoming Nitro-generated listener.
|
|
61
|
-
// It already implements the (CallEventType, String) -> Unit interface.
|
|
62
57
|
currentListener = listener
|
|
63
|
-
CallEngine.setEventHandler(listener)
|
|
58
|
+
CallEngine.setEventHandler(listener)
|
|
64
59
|
|
|
65
|
-
// Return a Nitro-generated Func_void for the remove function
|
|
66
60
|
return Func_void_java {
|
|
67
|
-
if (currentListener === listener) {
|
|
61
|
+
if (currentListener === listener) {
|
|
68
62
|
CallEngine.setEventHandler(null)
|
|
69
63
|
currentListener = null
|
|
70
64
|
Log.d(TAG, "Listener removed.")
|
|
@@ -74,7 +68,6 @@ class CallManager : HybridCallManagerSpec() {
|
|
|
74
68
|
}
|
|
75
69
|
}
|
|
76
70
|
|
|
77
|
-
// Removed callType param, now extracted from callData
|
|
78
71
|
override fun startOutgoingCall(callId: String, callData: String) {
|
|
79
72
|
Log.d(TAG, "startOutgoingCall requested: callId=$callId, callData=$callData")
|
|
80
73
|
CallEngine.getAppContext()?.let {
|