@qusaieilouti99/call-manager 0.1.68 → 0.1.70

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.
@@ -30,9 +30,6 @@ import android.telecom.PhoneAccountHandle
30
30
  import android.telecom.TelecomManager
31
31
  import android.telecom.VideoProfile
32
32
  import android.util.Log
33
- import kotlinx.coroutines.CoroutineScope
34
- import kotlinx.coroutines.Dispatchers
35
- import kotlinx.coroutines.launch
36
33
  import org.json.JSONArray
37
34
  import org.json.JSONObject
38
35
  import java.util.concurrent.ConcurrentHashMap
@@ -275,8 +272,11 @@ object CallEngine {
275
272
 
276
273
  val isVideoCall = callType == "Video"
277
274
  if (!canMakeMultipleCalls && activeCalls.isNotEmpty()) {
278
- activeCalls.values.filter { it.state == CallState.ACTIVE }
279
- .forEach { holdCallInternal(it.callId, heldBySystem = false) }
275
+ activeCalls.values.forEach {
276
+ if (it.state == CallState.ACTIVE) {
277
+ holdCallInternal(it.callId, heldBySystem = false)
278
+ }
279
+ }
280
280
  }
281
281
 
282
282
  // Track dialing state
@@ -629,7 +629,7 @@ object CallEngine {
629
629
  })
630
630
  }
631
631
 
632
- // --- Audio Management (SIMPLIFIED - NO MANUAL AUDIO FOCUS) ---
632
+ // --- Enhanced Audio Management ---
633
633
  fun getAudioDevices(): AudioRoutesInfo {
634
634
  val context = requireContext()
635
635
  audioManager = audioManager ?: context.getSystemService(Context.AUDIO_SERVICE) as? AudioManager ?: run {
@@ -637,8 +637,12 @@ object CallEngine {
637
637
  }
638
638
 
639
639
  val devices = mutableSetOf<String>()
640
- var currentRoute = "Earpiece"
641
640
 
641
+ // ALWAYS include Speaker and Earpiece for phone calls
642
+ devices.add("Speaker")
643
+ devices.add("Earpiece")
644
+
645
+ // Check for additional connected devices
642
646
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
643
647
  val audioDeviceInfoList = audioManager?.getDevices(AudioManager.GET_DEVICES_OUTPUTS)
644
648
  audioDeviceInfoList?.forEach { device ->
@@ -649,24 +653,26 @@ object CallEngine {
649
653
  AudioDeviceInfo.TYPE_WIRED_HEADPHONES, AudioDeviceInfo.TYPE_WIRED_HEADSET -> {
650
654
  devices.add("Headset")
651
655
  }
652
- AudioDeviceInfo.TYPE_BUILTIN_SPEAKER -> {
653
- devices.add("Speaker")
654
- }
655
- AudioDeviceInfo.TYPE_BUILTIN_EARPIECE -> {
656
- devices.add("Earpiece")
657
- }
656
+ // Speaker and Earpiece already added above if they are reported, but explicit add handles cases where they are not
658
657
  }
659
658
  }
660
659
  } else {
661
- devices.addAll(listOf("Speaker", "Earpiece"))
660
+ // For older versions, check for Bluetooth and Headset
661
+ @Suppress("DEPRECATION")
662
+ if (audioManager?.isBluetoothA2dpOn == true || audioManager?.isBluetoothScoOn == true) {
663
+ devices.add("Bluetooth")
664
+ }
665
+ @Suppress("DEPRECATION")
666
+ if (audioManager?.isWiredHeadsetOn == true) {
667
+ devices.add("Headset")
668
+ }
662
669
  }
663
670
 
664
- currentRoute = when {
665
- audioManager?.isBluetoothScoOn == true -> "Bluetooth"
666
- audioManager?.isSpeakerphoneOn == true -> "Speaker"
667
- audioManager?.isWiredHeadsetOn == true -> "Headset"
668
- else -> "Earpiece"
669
- }
671
+ val currentRoute = getCurrentAudioRoute()
672
+ Log.d(TAG, "Available audio devices: ${devices.toList()}, current route: $currentRoute")
673
+
674
+ // Update last known audio routes info
675
+ lastAudioRoutesInfo = AudioRoutesInfo(devices.toTypedArray(), currentRoute)
670
676
 
671
677
  return AudioRoutesInfo(devices.toTypedArray(), currentRoute)
672
678
  }
@@ -678,6 +684,7 @@ object CallEngine {
678
684
 
679
685
  val previousRoute = getCurrentAudioRoute()
680
686
 
687
+ // Reset all routes first
681
688
  audioManager?.isSpeakerphoneOn = false
682
689
  audioManager?.stopBluetoothSco()
683
690
  audioManager?.isBluetoothScoOn = false
@@ -689,6 +696,7 @@ object CallEngine {
689
696
  }
690
697
  "Earpiece" -> {
691
698
  audioManager?.mode = AudioManager.MODE_IN_COMMUNICATION
699
+ // Earpiece is the default - just ensure speaker and bluetooth are off
692
700
  }
693
701
  "Bluetooth" -> {
694
702
  audioManager?.startBluetoothSco()
@@ -697,6 +705,7 @@ object CallEngine {
697
705
  }
698
706
  "Headset" -> {
699
707
  audioManager?.mode = AudioManager.MODE_IN_COMMUNICATION
708
+ // Headset routing is automatic when connected
700
709
  }
701
710
  else -> {
702
711
  Log.w(TAG, "Unknown audio route: $route")
@@ -706,7 +715,8 @@ object CallEngine {
706
715
 
707
716
  val newRoute = getCurrentAudioRoute()
708
717
  if (previousRoute != newRoute) {
709
- emitEvent(CallEventType.AUDIO_ROUTE_CHANGED, JSONObject().put("route", newRoute))
718
+ // Emit unified event with full context
719
+ emitAudioRouteChanged()
710
720
  }
711
721
  }
712
722
 
@@ -720,7 +730,7 @@ object CallEngine {
720
730
  }
721
731
 
722
732
  private fun setInitialAudioRoute(callType: String) {
723
- val availableDevices = getAudioDevices()
733
+ val availableDevices = getAudioDevices() // This will update lastAudioRoutesInfo
724
734
 
725
735
  val defaultRoute = when {
726
736
  availableDevices.devices.contains("Bluetooth") -> "Bluetooth"
@@ -748,17 +758,44 @@ object CallEngine {
748
758
  }
749
759
  }
750
760
 
761
+ // UNIFIED event emission - always sends full audio context
762
+ private fun emitAudioRouteChanged() {
763
+ // Re-calculate latest state to ensure accuracy
764
+ val audioInfo = getAudioDevices() // This updates lastAudioRoutesInfo internally
765
+ val jsonPayload = JSONObject().apply {
766
+ put("devices", JSONArray(audioInfo.devices.toList()))
767
+ put("currentRoute", audioInfo.currentRoute)
768
+ }
769
+ emitEvent(CallEventType.AUDIO_ROUTE_CHANGED, jsonPayload)
770
+ Log.d(TAG, "Audio route changed: ${audioInfo.currentRoute}, available: ${audioInfo.devices.toList()}")
771
+ }
772
+
751
773
  // --- Audio Device Callback ---
752
774
  private val audioDeviceCallback = object : AudioDeviceCallback() {
753
775
  override fun onAudioDevicesAdded(addedDevices: Array<out AudioDeviceInfo>?) {
754
- emitAudioDevicesChangedIfNeeded()
776
+ Log.d(TAG, "Audio devices added")
777
+ emitAudioDevicesChanged()
755
778
  }
756
779
 
757
780
  override fun onAudioDevicesRemoved(removedDevices: Array<out AudioDeviceInfo>?) {
758
- emitAudioDevicesChangedIfNeeded()
781
+ Log.d(TAG, "Audio devices removed")
782
+ emitAudioDevicesChanged()
783
+ }
784
+ }
785
+
786
+ // Event for when physical devices are added/removed - includes full audio context
787
+ private fun emitAudioDevicesChanged() {
788
+ // Re-calculate latest state to ensure accuracy
789
+ val audioInfo = getAudioDevices() // This updates lastAudioRoutesInfo internally
790
+ val jsonPayload = JSONObject().apply {
791
+ put("devices", JSONArray(audioInfo.devices.toList()))
792
+ put("currentRoute", audioInfo.currentRoute)
759
793
  }
794
+ emitEvent(CallEventType.AUDIO_DEVICES_CHANGED, jsonPayload)
795
+ Log.d(TAG, "Audio devices changed: available: ${audioInfo.devices.toList()}")
760
796
  }
761
797
 
798
+
762
799
  fun registerAudioDeviceCallback() {
763
800
  val context = requireContext()
764
801
  audioManager = audioManager ?: context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
@@ -771,22 +808,6 @@ object CallEngine {
771
808
  audioManager?.unregisterAudioDeviceCallback(audioDeviceCallback)
772
809
  }
773
810
 
774
- private fun emitAudioDevicesChangedIfNeeded() {
775
- val currentAudioInfo = getAudioDevices()
776
-
777
- if (lastAudioRoutesInfo == null ||
778
- !currentAudioInfo.devices.contentEquals(lastAudioRoutesInfo!!.devices) ||
779
- currentAudioInfo.currentRoute != lastAudioRoutesInfo!!.currentRoute) {
780
-
781
- lastAudioRoutesInfo = currentAudioInfo
782
- val jsonPayload = JSONObject().apply {
783
- put("devices", JSONArray(currentAudioInfo.devices.toList()))
784
- put("currentRoute", currentAudioInfo.currentRoute)
785
- }
786
- emitEvent(CallEventType.AUDIO_DEVICES_CHANGED, jsonPayload)
787
- }
788
- }
789
-
790
811
  // --- Screen Management ---
791
812
  fun keepScreenAwake(keepAwake: Boolean) {
792
813
  val context = requireContext()
@@ -25,8 +25,8 @@ class MyConnection(
25
25
  init {
26
26
  connectionProperties = Connection.PROPERTY_SELF_MANAGED
27
27
  connectionCapabilities = Connection.CAPABILITY_SUPPORT_HOLD or
28
- Connection.CAPABILITY_MUTE or
29
- Connection.CAPABILITY_HOLD
28
+ Connection.CAPABILITY_MUTE or
29
+ Connection.CAPABILITY_HOLD
30
30
 
31
31
  if (callType == "Video") {
32
32
  Log.d(TAG, "MyConnection for callId $callId initialized as VIDEO call.")
@@ -84,19 +84,11 @@ class MyConnection(
84
84
  }
85
85
  }
86
86
 
87
+ // Only react to route change if it's different.
88
+ // DO NOT emit AUDIO_ROUTE_CHANGED from here, let CallEngine's
89
+ // AudioDeviceCallback handle it for consistency and to avoid duplication.
87
90
  if (lastAudioState == null || lastAudioState!!.route != state.route) {
88
- val routeName = when (state.route) {
89
- CallAudioState.ROUTE_SPEAKER -> "Speaker"
90
- CallAudioState.ROUTE_EARPIECE -> "Earpiece"
91
- CallAudioState.ROUTE_BLUETOOTH -> "Bluetooth"
92
- CallAudioState.ROUTE_WIRED_HEADSET -> "Headset"
93
- else -> "Unknown"
94
- }
95
-
96
- CallEngine.emitEvent(
97
- CallEventType.AUDIO_ROUTE_CHANGED,
98
- JSONObject().put("callId", callId).put("route", routeName)
99
- )
91
+ Log.d(TAG, "System audio route changed for callId: $callId. Telecom route: ${state.route}")
100
92
  }
101
93
 
102
94
  lastAudioState = state
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qusaieilouti99/call-manager",
3
- "version": "0.1.68",
3
+ "version": "0.1.70",
4
4
  "description": "Call manager",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",