@qusaieilouti99/call-manager 0.1.184 → 0.1.185
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/CallActivity.kt +3 -1
- package/android/src/main/java/com/margelo/nitro/qusaieilouti99/callmanager/CallEngine.kt +44 -64
- package/android/src/main/java/com/margelo/nitro/qusaieilouti99/callmanager/CallManager.kt +1 -5
- package/android/src/main/java/com/margelo/nitro/qusaieilouti99/callmanager/CallNotificationActionReceiver.kt +1 -8
- package/android/src/main/java/com/margelo/nitro/qusaieilouti99/callmanager/MyConnection.kt +4 -1
- package/package.json +1 -1
|
@@ -320,7 +320,9 @@ class CallActivity : Activity(), CallEngine.CallEndListener {
|
|
|
320
320
|
finishReason = FinishReason.ANSWER
|
|
321
321
|
CallEngine.stopRingtone()
|
|
322
322
|
CallEngine.cancelIncomingCallUI()
|
|
323
|
-
|
|
323
|
+
|
|
324
|
+
// Use the unified answer flow with isLocalAnswer = true
|
|
325
|
+
CallEngine.answerCall(callId, isLocalAnswer = true)
|
|
324
326
|
finishCallActivity()
|
|
325
327
|
}
|
|
326
328
|
|
|
@@ -80,6 +80,7 @@ object CallEngine {
|
|
|
80
80
|
private val activeCalls = ConcurrentHashMap<String, CallInfo>()
|
|
81
81
|
private val telecomConnections = ConcurrentHashMap<String, Connection>()
|
|
82
82
|
private val callMetadata = ConcurrentHashMap<String, String>()
|
|
83
|
+
private val callAnswerStates = ConcurrentHashMap<String, Boolean>()
|
|
83
84
|
|
|
84
85
|
private var currentCallId: String? = null
|
|
85
86
|
private var canMakeMultipleCalls: Boolean = false
|
|
@@ -205,11 +206,14 @@ object CallEngine {
|
|
|
205
206
|
|
|
206
207
|
fun removeTelecomConnection(callId: String) {
|
|
207
208
|
telecomConnections.remove(callId)
|
|
209
|
+
callAnswerStates.remove(callId) // Clean up answer state
|
|
208
210
|
Log.d(TAG, "Removed Telecom Connection for callId: $callId")
|
|
209
211
|
}
|
|
210
212
|
|
|
211
213
|
fun getTelecomConnection(callId: String): Connection? = telecomConnections[callId]
|
|
212
214
|
|
|
215
|
+
fun getCallAnswerState(callId: String): Boolean? = callAnswerStates.remove(callId)
|
|
216
|
+
|
|
213
217
|
fun setCanMakeMultipleCalls(allow: Boolean) {
|
|
214
218
|
canMakeMultipleCalls = allow
|
|
215
219
|
Log.d(TAG, "canMakeMultipleCalls set to: $allow")
|
|
@@ -329,7 +333,6 @@ object CallEngine {
|
|
|
329
333
|
currentCallId = callId
|
|
330
334
|
Log.d(TAG, "Call $callId added to activeCalls. State: DIALING")
|
|
331
335
|
|
|
332
|
-
// Removed setAudioMode() call from here as per user feedback to fix audio routing issues.
|
|
333
336
|
registerPhoneAccount()
|
|
334
337
|
|
|
335
338
|
val telecomManager =
|
|
@@ -380,13 +383,13 @@ object CallEngine {
|
|
|
380
383
|
if (existingCallInfo != null && existingCallInfo.state == CallState.INCOMING) {
|
|
381
384
|
// Scenario 1: Call with this ID is already incoming, answer it.
|
|
382
385
|
Log.d(TAG, "Call $callId is incoming, answering it directly via startCall.")
|
|
383
|
-
|
|
384
|
-
return
|
|
386
|
+
answerCall(callId, isLocalAnswer = false) // Remote party answered
|
|
387
|
+
return
|
|
385
388
|
}
|
|
386
389
|
|
|
387
390
|
// Scenario 2: Call is new or not incoming, treat as new outgoing call that is immediately active.
|
|
388
391
|
Log.d(TAG, "Call $callId is new or not incoming. Initiating as outgoing and immediately active.")
|
|
389
|
-
if (!canMakeMultipleCalls && activeCalls.isNotEmpty()) {
|
|
392
|
+
if (!canMakeMultipleCalls && activeCalls.isNotEmpty()) {
|
|
390
393
|
if (!validateOutgoingCallRequest()) {
|
|
391
394
|
Log.w(TAG, "Rejecting startCall as outgoing - incoming/active call exists")
|
|
392
395
|
emitEvent(CallEventType.CALL_REJECTED, JSONObject().apply {
|
|
@@ -406,11 +409,10 @@ object CallEngine {
|
|
|
406
409
|
}
|
|
407
410
|
}
|
|
408
411
|
|
|
409
|
-
activeCalls[callId] = CallInfo(callId, callType, targetName, null, CallState.DIALING)
|
|
412
|
+
activeCalls[callId] = CallInfo(callId, callType, targetName, null, CallState.DIALING)
|
|
410
413
|
currentCallId = callId
|
|
411
414
|
Log.d(TAG, "Call $callId added to activeCalls. Initial state: DIALING (for Telecom)")
|
|
412
415
|
|
|
413
|
-
// Removed setAudioMode() call from here as per user feedback to fix audio routing issues.
|
|
414
416
|
registerPhoneAccount()
|
|
415
417
|
|
|
416
418
|
val telecomManager = requireContext().getSystemService(Context.TELECOM_SERVICE) as TelecomManager
|
|
@@ -428,7 +430,7 @@ object CallEngine {
|
|
|
428
430
|
val placeCallExtras = Bundle().apply {
|
|
429
431
|
putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle)
|
|
430
432
|
putBundle(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, outgoingExtrasForConnectionService)
|
|
431
|
-
putBoolean(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, isVideoCall)
|
|
433
|
+
putBoolean(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, isVideoCall)
|
|
432
434
|
putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, if (isVideoCall) VideoProfile.STATE_BIDIRECTIONAL else VideoProfile.STATE_AUDIO_ONLY)
|
|
433
435
|
}
|
|
434
436
|
|
|
@@ -440,7 +442,7 @@ object CallEngine {
|
|
|
440
442
|
Log.d(TAG, "Successfully reported outgoing call (to be immediately active) to TelecomManager for $callId")
|
|
441
443
|
|
|
442
444
|
// Immediately mark as answered for "startCall" behavior
|
|
443
|
-
|
|
445
|
+
answerCall(callId, isLocalAnswer = false) // Remote party answered
|
|
444
446
|
} catch (e: Exception) {
|
|
445
447
|
Log.e(TAG, "Failed to start call as active: ${e.message}", e)
|
|
446
448
|
endCallInternal(callId)
|
|
@@ -448,17 +450,29 @@ object CallEngine {
|
|
|
448
450
|
updateLockScreenBypass()
|
|
449
451
|
}
|
|
450
452
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
453
|
+
// NEW UNIFIED ANSWER METHOD
|
|
454
|
+
fun answerCall(callId: String, isLocalAnswer: Boolean = true) {
|
|
455
|
+
Log.d(TAG, "answerCall: $callId, isLocalAnswer: $isLocalAnswer")
|
|
456
|
+
val callInfo = activeCalls[callId]
|
|
457
|
+
if (callInfo == null) {
|
|
458
|
+
Log.w(TAG, "Cannot answer call $callId - not found in active calls")
|
|
459
|
+
return
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// Store the isLocalAnswer state for the connection to use
|
|
463
|
+
callAnswerStates[callId] = isLocalAnswer
|
|
455
464
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
465
|
+
// Always call connection.onAnswer() to let Telecom handle the flow
|
|
466
|
+
telecomConnections[callId]?.let { connection ->
|
|
467
|
+
connection.onAnswer() // This will trigger MyConnection.onAnswer()
|
|
468
|
+
} ?: run {
|
|
469
|
+
Log.w(TAG, "No telecom connection found for $callId, falling back to direct answer")
|
|
470
|
+
coreCallAnswered(callId, isLocalAnswer)
|
|
471
|
+
}
|
|
459
472
|
}
|
|
460
473
|
|
|
461
|
-
|
|
474
|
+
// INTERNAL METHOD called by MyConnection.onAnswer()
|
|
475
|
+
internal fun coreCallAnswered(callId: String, isLocalAnswer: Boolean) {
|
|
462
476
|
Log.d(TAG, "coreCallAnswered: $callId, isLocalAnswer: $isLocalAnswer")
|
|
463
477
|
val callInfo = activeCalls[callId]
|
|
464
478
|
if (callInfo == null) {
|
|
@@ -489,7 +503,7 @@ object CallEngine {
|
|
|
489
503
|
keepScreenAwake(true)
|
|
490
504
|
updateLockScreenBypass()
|
|
491
505
|
|
|
492
|
-
setAudioMode()
|
|
506
|
+
setAudioMode()
|
|
493
507
|
|
|
494
508
|
// Set initial audio route using Telecom's CallEndpoint API
|
|
495
509
|
setInitialCallAudioRoute(callId, callInfo.callType)
|
|
@@ -614,8 +628,6 @@ object CallEngine {
|
|
|
614
628
|
setMutedInternal(callId, muted)
|
|
615
629
|
}
|
|
616
630
|
|
|
617
|
-
// This method is now called by MyConnection.onMuteStateChanged which receives state from Telecom.
|
|
618
|
-
// It applies the mute state to AudioManager and emits the event.
|
|
619
631
|
private fun setMutedInternal(callId: String, muted: Boolean) {
|
|
620
632
|
val callInfo = activeCalls[callId]
|
|
621
633
|
if (callInfo == null) {
|
|
@@ -630,7 +642,6 @@ object CallEngine {
|
|
|
630
642
|
audioManager?.isMicrophoneMute = muted
|
|
631
643
|
Log.d(TAG, "AudioManager microphone mute set to: $muted")
|
|
632
644
|
|
|
633
|
-
|
|
634
645
|
if (wasMuted != muted) {
|
|
635
646
|
val eventType = if (muted) CallEventType.CALL_MUTED else CallEventType.CALL_UNMUTED
|
|
636
647
|
emitEvent(eventType, JSONObject().put("callId", callId))
|
|
@@ -654,6 +665,7 @@ object CallEngine {
|
|
|
654
665
|
activeCalls.clear()
|
|
655
666
|
telecomConnections.clear()
|
|
656
667
|
callMetadata.clear()
|
|
668
|
+
callAnswerStates.clear()
|
|
657
669
|
currentCallId = null
|
|
658
670
|
|
|
659
671
|
cleanup()
|
|
@@ -670,6 +682,7 @@ object CallEngine {
|
|
|
670
682
|
|
|
671
683
|
val metadata = callMetadata.remove(callId)
|
|
672
684
|
activeCalls.remove(callId)
|
|
685
|
+
callAnswerStates.remove(callId) // Clean up answer state
|
|
673
686
|
|
|
674
687
|
stopRingback()
|
|
675
688
|
stopRingtone()
|
|
@@ -693,7 +706,6 @@ object CallEngine {
|
|
|
693
706
|
}
|
|
694
707
|
|
|
695
708
|
telecomConnections[callId]?.let { connection ->
|
|
696
|
-
// Disconnect and destroy the Telecom Connection
|
|
697
709
|
connection.setDisconnected(DisconnectCause(DisconnectCause.LOCAL))
|
|
698
710
|
connection.destroy()
|
|
699
711
|
removeTelecomConnection(callId)
|
|
@@ -728,14 +740,12 @@ object CallEngine {
|
|
|
728
740
|
|
|
729
741
|
// ====== IMPROVED AUDIO ROUTING SYSTEM (using CallEndpoint API) ======
|
|
730
742
|
|
|
731
|
-
// Stores the currently available CallEndpoints as reported by Telecom
|
|
732
743
|
fun onTelecomAvailableEndpointsChanged(endpoints: List<CallEndpoint>) {
|
|
733
744
|
availableCallEndpoints = endpoints
|
|
734
745
|
Log.d(TAG, "Available CallEndpoints updated: ${endpoints.map { "${it.endpointName}(${mapCallEndpointTypeToString(it.endpointType)})" }}")
|
|
735
746
|
emitAudioDevicesChanged()
|
|
736
747
|
}
|
|
737
748
|
|
|
738
|
-
// Stores the active CallEndpoint as reported by Telecom
|
|
739
749
|
fun onTelecomAudioRouteChanged(callId: String, callEndpoint: CallEndpoint) {
|
|
740
750
|
Log.d(TAG, "Telecom audio route changed for $callId: endpoint=${callEndpoint.endpointName} (type=${mapCallEndpointTypeToString(callEndpoint.endpointType)})")
|
|
741
751
|
currentActiveCallEndpoint = callEndpoint
|
|
@@ -745,9 +755,6 @@ object CallEngine {
|
|
|
745
755
|
fun getAudioDevices(): AudioRoutesInfo {
|
|
746
756
|
val devices = availableCallEndpoints.map { StringHolder(mapCallEndpointTypeToString(it.endpointType)) }.toMutableSet()
|
|
747
757
|
|
|
748
|
-
// Add common fallback endpoints if Telecom doesn't explicitly list them,
|
|
749
|
-
// although for active calls, Telecom should provide comprehensive lists.
|
|
750
|
-
// This provides robustness for the JavaScript side's expected string values.
|
|
751
758
|
if (!devices.any { it.value == "Earpiece" }) devices.add(StringHolder("Earpiece"))
|
|
752
759
|
if (!devices.any { it.value == "Speaker" }) devices.add(StringHolder("Speaker"))
|
|
753
760
|
|
|
@@ -763,9 +770,8 @@ object CallEngine {
|
|
|
763
770
|
|
|
764
771
|
val telecomEndpointType = mapStringToCallEndpointType(route)
|
|
765
772
|
|
|
766
|
-
// Find the actual CallEndpoint object from the available list that matches the type
|
|
767
773
|
val targetEndpoint = availableCallEndpoints.find { it.endpointType == telecomEndpointType }
|
|
768
|
-
?: getOrCreateGenericCallEndpoint(telecomEndpointType, route)
|
|
774
|
+
?: getOrCreateGenericCallEndpoint(telecomEndpointType, route)
|
|
769
775
|
|
|
770
776
|
if (targetEndpoint != null) {
|
|
771
777
|
currentCallId?.let { callId ->
|
|
@@ -783,7 +789,6 @@ object CallEngine {
|
|
|
783
789
|
}
|
|
784
790
|
}
|
|
785
791
|
|
|
786
|
-
// This method is called by MyConnection when its state becomes ACTIVE, or after startCall/startOutgoingCall.
|
|
787
792
|
fun setInitialCallAudioRoute(callId: String, callType: String) {
|
|
788
793
|
Log.d(TAG, "Setting initial audio route for callId: $callId, type: $callType")
|
|
789
794
|
|
|
@@ -799,9 +804,6 @@ object CallEngine {
|
|
|
799
804
|
else -> CallEndpoint.TYPE_EARPIECE
|
|
800
805
|
}
|
|
801
806
|
|
|
802
|
-
// We should ideally pick from `availableCallEndpoints`.
|
|
803
|
-
// If none of that type are explicitly listed (e.g. for default Earpiece/Speaker),
|
|
804
|
-
// we can try to create a generic one.
|
|
805
807
|
val targetEndpoint = availableCallEndpoints.find { it.endpointType == targetEndpointType }
|
|
806
808
|
?: getOrCreateGenericCallEndpoint(targetEndpointType, mapCallEndpointTypeToString(targetEndpointType))
|
|
807
809
|
|
|
@@ -813,7 +815,7 @@ object CallEngine {
|
|
|
813
815
|
connection.setTelecomAudioRoute(targetEndpoint)
|
|
814
816
|
}
|
|
815
817
|
} ?: Log.w(TAG, "No telecom connection found for $callId during initial route setting.")
|
|
816
|
-
}, 500L)
|
|
818
|
+
}, 500L)
|
|
817
819
|
} else {
|
|
818
820
|
Log.w(TAG, "Could not find or create a valid CallEndpoint for initial route type: $targetEndpointType")
|
|
819
821
|
}
|
|
@@ -833,8 +835,8 @@ object CallEngine {
|
|
|
833
835
|
}
|
|
834
836
|
am.isSpeakerphoneOn = false
|
|
835
837
|
}
|
|
836
|
-
currentActiveCallEndpoint = null
|
|
837
|
-
availableCallEndpoints = emptyList()
|
|
838
|
+
currentActiveCallEndpoint = null
|
|
839
|
+
availableCallEndpoints = emptyList()
|
|
838
840
|
wasManuallySetAudioRoute = false
|
|
839
841
|
Log.d(TAG, "Audio mode reset to MODE_NORMAL, audio endpoints reset.")
|
|
840
842
|
}
|
|
@@ -863,8 +865,6 @@ object CallEngine {
|
|
|
863
865
|
}
|
|
864
866
|
|
|
865
867
|
private fun getOrCreateGenericCallEndpoint(type: Int, name: String): CallEndpoint? {
|
|
866
|
-
// This is a fallback to create a CallEndpoint if it's not explicitly in availableCallEndpoints.
|
|
867
|
-
// Useful for basic types like Earpiece/Speaker that might implicitly exist.
|
|
868
868
|
return when (type) {
|
|
869
869
|
CallEndpoint.TYPE_EARPIECE -> CallEndpoint(name, type, ParcelUuid(UUID.nameUUIDFromBytes("Earpiece_Default".toByteArray())))
|
|
870
870
|
CallEndpoint.TYPE_SPEAKER -> CallEndpoint(name, type, ParcelUuid(UUID.nameUUIDFromBytes("Speaker_Default".toByteArray())))
|
|
@@ -874,7 +874,6 @@ object CallEngine {
|
|
|
874
874
|
}
|
|
875
875
|
}
|
|
876
876
|
|
|
877
|
-
|
|
878
877
|
private fun isWiredHeadsetConnected(): Boolean {
|
|
879
878
|
val am = audioManager ?: return false
|
|
880
879
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
@@ -906,7 +905,7 @@ object CallEngine {
|
|
|
906
905
|
}
|
|
907
906
|
|
|
908
907
|
private fun emitAudioRouteChanged(currentRoute: String) {
|
|
909
|
-
val info = getAudioDevices()
|
|
908
|
+
val info = getAudioDevices()
|
|
910
909
|
val deviceStrings = info.devices.map { it.value }
|
|
911
910
|
val payload = JSONObject().apply {
|
|
912
911
|
put("devices", JSONArray(deviceStrings))
|
|
@@ -927,10 +926,6 @@ object CallEngine {
|
|
|
927
926
|
Log.d(TAG, "Audio devices changed: available: $deviceStrings")
|
|
928
927
|
}
|
|
929
928
|
|
|
930
|
-
// Audio device callbacks are now handled by Telecom and MyConnection.
|
|
931
|
-
// The CallEngine does not directly register an AudioDeviceCallback.
|
|
932
|
-
// This part of the code is removed.
|
|
933
|
-
|
|
934
929
|
// ====== END IMPROVED AUDIO ROUTING SYSTEM ======
|
|
935
930
|
|
|
936
931
|
fun keepScreenAwake(keepAwake: Boolean) {
|
|
@@ -938,13 +933,10 @@ object CallEngine {
|
|
|
938
933
|
val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
|
|
939
934
|
if (keepAwake) {
|
|
940
935
|
if (wakeLock == null || wakeLock!!.isHeld.not()) {
|
|
941
|
-
// Use PARTIAL_WAKE_LOCK to keep CPU awake, SCREEN_DIM_WAKE_LOCK to keep screen on (dim)
|
|
942
|
-
// For general "keep screen awake", SCREEN_DIM_WAKE_LOCK is appropriate.
|
|
943
936
|
wakeLock = powerManager.newWakeLock(
|
|
944
937
|
PowerManager.SCREEN_DIM_WAKE_LOCK or PowerManager.ACQUIRE_CAUSES_WAKEUP,
|
|
945
938
|
"CallEngine:WakeLock"
|
|
946
939
|
)
|
|
947
|
-
// Set a timeout for the wakelock to prevent indefinite drain, e.g., 10 minutes
|
|
948
940
|
wakeLock?.acquire(10 * 60 * 1000L)
|
|
949
941
|
Log.d(TAG, "Acquired SCREEN_DIM_WAKE_LOCK")
|
|
950
942
|
}
|
|
@@ -969,20 +961,17 @@ object CallEngine {
|
|
|
969
961
|
}
|
|
970
962
|
|
|
971
963
|
private fun validateOutgoingCallRequest(): Boolean {
|
|
972
|
-
// Only allow outgoing call if no incoming or active call exists and multi-call not allowed
|
|
973
964
|
return !activeCalls.any {
|
|
974
965
|
(!canMakeMultipleCalls && (it.value.state == CallState.INCOMING || it.value.state == CallState.ACTIVE))
|
|
975
966
|
}
|
|
976
967
|
}
|
|
977
968
|
|
|
978
|
-
|
|
979
969
|
private fun rejectIncomingCallCollision(callId: String, reason: String) {
|
|
980
970
|
emitEvent(CallEventType.CALL_REJECTED, JSONObject().apply {
|
|
981
971
|
put("callId", callId)
|
|
982
972
|
put("reason", reason)
|
|
983
973
|
})
|
|
984
974
|
|
|
985
|
-
// Only remove metadata if there's NO existing active call with this ID
|
|
986
975
|
val existingCall = activeCalls[callId]
|
|
987
976
|
if (existingCall == null) {
|
|
988
977
|
callMetadata.remove(callId)
|
|
@@ -1006,12 +995,6 @@ object CallEngine {
|
|
|
1006
995
|
channel.enableVibration(true)
|
|
1007
996
|
channel.setBypassDnd(true)
|
|
1008
997
|
channel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
|
|
1009
|
-
|
|
1010
|
-
// For Android S (API 31) and above, Telecom manages ringing for self-managed calls
|
|
1011
|
-
// if it's the default dialer or an InCallService with METADATA_IN_CALL_SERVICE_RINGING.
|
|
1012
|
-
// Setting sound to null here allows Telecom to take over or for us to manage manually
|
|
1013
|
-
// if not relying on Telecom for ringing.
|
|
1014
|
-
// Since we play ringtone manually below, ensure channel sound is null to avoid double ringing.
|
|
1015
998
|
channel.setSound(null, null)
|
|
1016
999
|
channel.importance = NotificationManager.IMPORTANCE_HIGH
|
|
1017
1000
|
|
|
@@ -1027,12 +1010,11 @@ object CallEngine {
|
|
|
1027
1010
|
val useCallStyleNotification = supportsCallStyleNotifications()
|
|
1028
1011
|
Log.d(TAG, "Using CallStyle notification: $useCallStyleNotification")
|
|
1029
1012
|
|
|
1030
|
-
// Determine if device is locked or if CallStyle is not preferred/supported for current scenario
|
|
1031
1013
|
val isDeviceLocked = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
|
|
1032
1014
|
val keyguardManager = context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
|
|
1033
1015
|
keyguardManager.isKeyguardLocked
|
|
1034
1016
|
} else {
|
|
1035
|
-
false
|
|
1017
|
+
false
|
|
1036
1018
|
}
|
|
1037
1019
|
|
|
1038
1020
|
if (isDeviceLocked || !useCallStyleNotification) {
|
|
@@ -1066,7 +1048,7 @@ object CallEngine {
|
|
|
1066
1048
|
PowerManager.SCREEN_BRIGHT_WAKE_LOCK or PowerManager.ACQUIRE_CAUSES_WAKEUP,
|
|
1067
1049
|
"CallEngine:LockScreenWake"
|
|
1068
1050
|
)
|
|
1069
|
-
wakeLock.acquire(5000)
|
|
1051
|
+
wakeLock.acquire(5000)
|
|
1070
1052
|
context.startActivity(overlayIntent)
|
|
1071
1053
|
Log.d(TAG, "Successfully launched CallActivity overlay")
|
|
1072
1054
|
} catch (e: Exception) {
|
|
@@ -1130,7 +1112,7 @@ object CallEngine {
|
|
|
1130
1112
|
.setCategory(Notification.CATEGORY_CALL)
|
|
1131
1113
|
.setPriority(Notification.PRIORITY_MAX)
|
|
1132
1114
|
.setVisibility(Notification.VISIBILITY_PUBLIC)
|
|
1133
|
-
.setSound(null)
|
|
1115
|
+
.setSound(null)
|
|
1134
1116
|
.build()
|
|
1135
1117
|
} else {
|
|
1136
1118
|
Notification.Builder(context, NOTIF_CHANNEL_ID)
|
|
@@ -1145,7 +1127,7 @@ object CallEngine {
|
|
|
1145
1127
|
.setOngoing(true)
|
|
1146
1128
|
.setAutoCancel(false)
|
|
1147
1129
|
.setVisibility(Notification.VISIBILITY_PUBLIC)
|
|
1148
|
-
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE))
|
|
1130
|
+
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE))
|
|
1149
1131
|
.build()
|
|
1150
1132
|
}
|
|
1151
1133
|
|
|
@@ -1199,12 +1181,10 @@ object CallEngine {
|
|
|
1199
1181
|
val activityManager =
|
|
1200
1182
|
context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
|
1201
1183
|
|
|
1202
|
-
|
|
1203
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { // LMR1 is API 22, getAppTasks available from API 21, but getRunningTasks deprecated from API 23.
|
|
1184
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
|
|
1204
1185
|
try {
|
|
1205
1186
|
val tasks = activityManager.appTasks
|
|
1206
1187
|
if (tasks.isNotEmpty()) {
|
|
1207
|
-
// get(0) is the top-most task
|
|
1208
1188
|
val topActivityComponentName = tasks[0].taskInfo.topActivity
|
|
1209
1189
|
return topActivityComponentName?.className?.contains("MainActivity") == true
|
|
1210
1190
|
}
|
|
@@ -1370,6 +1350,7 @@ object CallEngine {
|
|
|
1370
1350
|
activeCalls.clear()
|
|
1371
1351
|
telecomConnections.clear()
|
|
1372
1352
|
callMetadata.clear()
|
|
1353
|
+
callAnswerStates.clear()
|
|
1373
1354
|
currentCallId = null
|
|
1374
1355
|
cleanup()
|
|
1375
1356
|
lockScreenBypassCallbacks.clear()
|
|
@@ -1377,7 +1358,6 @@ object CallEngine {
|
|
|
1377
1358
|
cachedEvents.clear()
|
|
1378
1359
|
isInitialized.set(false)
|
|
1379
1360
|
appContext = null
|
|
1380
|
-
// Reset audio states
|
|
1381
1361
|
currentActiveCallEndpoint = null
|
|
1382
1362
|
availableCallEndpoints = emptyList()
|
|
1383
1363
|
wasManuallySetAudioRoute = false
|
|
@@ -8,8 +8,6 @@ class CallManager : HybridCallManagerSpec() {
|
|
|
8
8
|
|
|
9
9
|
private val TAG = "CallManager"
|
|
10
10
|
|
|
11
|
-
// Simplified approach - rely on proper Application.onCreate() initialization
|
|
12
|
-
// Remove all fallback context access attempts that don't work with Nitro modules
|
|
13
11
|
private fun ensureInitialized() {
|
|
14
12
|
if (!CallEngine.isInitialized()) {
|
|
15
13
|
Log.e(TAG, "CallEngine not initialized! This should not happen if Application.onCreate() was called properly.")
|
|
@@ -20,8 +18,6 @@ class CallManager : HybridCallManagerSpec() {
|
|
|
20
18
|
}
|
|
21
19
|
}
|
|
22
20
|
|
|
23
|
-
// --- All methods must call ensureInitialized() first ---
|
|
24
|
-
|
|
25
21
|
override fun endCall(callId: String): Unit {
|
|
26
22
|
Log.d(TAG, "endCall requested for callId: $callId")
|
|
27
23
|
ensureInitialized()
|
|
@@ -83,7 +79,7 @@ class CallManager : HybridCallManagerSpec() {
|
|
|
83
79
|
override fun callAnswered(callId: String): Unit {
|
|
84
80
|
Log.d(TAG, "callAnswered (from JS) requested for callId: $callId")
|
|
85
81
|
ensureInitialized()
|
|
86
|
-
CallEngine.
|
|
82
|
+
CallEngine.answerCall(callId, isLocalAnswer = false) // Remote party answered
|
|
87
83
|
}
|
|
88
84
|
|
|
89
85
|
override fun setOnHold(callId: String, onHold: Boolean): Unit {
|
|
@@ -16,14 +16,7 @@ class CallNotificationActionReceiver : BroadcastReceiver() {
|
|
|
16
16
|
when (intent.action) {
|
|
17
17
|
"com.qusaieilouti99.callmanager.ANSWER_CALL" -> {
|
|
18
18
|
Log.d(TAG, "Answer action received for callId: $callId")
|
|
19
|
-
|
|
20
|
-
if (connection != null) {
|
|
21
|
-
connection.onAnswer()
|
|
22
|
-
Log.d(TAG, "Call answered via Telecom connection for callId: $callId")
|
|
23
|
-
} else {
|
|
24
|
-
Log.e(TAG, "No Telecom connection found for callId: $callId. Using direct answer.")
|
|
25
|
-
CallEngine.answerCall(callId)
|
|
26
|
-
}
|
|
19
|
+
CallEngine.answerCall(callId, isLocalAnswer = true)
|
|
27
20
|
}
|
|
28
21
|
"com.qusaieilouti99.callmanager.DECLINE_CALL" -> {
|
|
29
22
|
Log.d(TAG, "Decline action received for callId: $callId")
|
|
@@ -49,7 +49,10 @@ class MyConnection(
|
|
|
49
49
|
super.onAnswer(videoState)
|
|
50
50
|
Log.d(TAG, "Call answered via Telecom for callId: $callId. VideoState: $videoState")
|
|
51
51
|
setActive()
|
|
52
|
-
|
|
52
|
+
|
|
53
|
+
// Retrieve the stored isLocalAnswer state and call coreCallAnswered directly
|
|
54
|
+
val isLocalAnswer = CallEngine.getCallAnswerState(callId) ?: true // default to true if not found
|
|
55
|
+
CallEngine.coreCallAnswered(callId, isLocalAnswer)
|
|
53
56
|
}
|
|
54
57
|
|
|
55
58
|
override fun onReject() {
|