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