@qusaieilouti99/call-manager 0.1.52 → 0.1.53

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.
@@ -69,7 +69,6 @@ object CallEngine {
69
69
 
70
70
  // System Call State Tracking
71
71
  private var isSystemCallActive: Boolean = false
72
- private var wasHeldBySystem: Boolean = false
73
72
 
74
73
  // Lock Screen Bypass
75
74
  private var lockScreenBypassActive = false
@@ -92,7 +91,7 @@ object CallEngine {
92
91
  )
93
92
 
94
93
  enum class CallState {
95
- INCOMING, DIALING, ACTIVE, HELD, HELD_BY_SYSTEM, ENDED
94
+ INCOMING, DIALING, ACTIVE, HELD, ENDED
96
95
  }
97
96
 
98
97
  interface LockScreenBypassCallback {
@@ -104,26 +103,23 @@ object CallEngine {
104
103
  Log.d(TAG, "Audio focus changed: $focusChange")
105
104
  when (focusChange) {
106
105
  AudioManager.AUDIOFOCUS_LOSS -> {
107
- // Lost focus permanently - likely due to phone call
108
106
  handleAudioFocusLoss()
109
107
  }
110
108
  AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
111
- // Lost focus temporarily
112
- handleAudioFocusLossTransient()
109
+ handleAudioFocusLoss()
113
110
  }
114
111
  AudioManager.AUDIOFOCUS_GAIN -> {
115
- // Regained focus
116
112
  handleAudioFocusGain()
117
113
  }
118
114
  AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
119
- // Can duck audio
120
- handleAudioFocusLossCanDuck()
115
+ // Can duck audio - don't hold call
116
+ Log.d(TAG, "Audio focus loss - can duck, not holding call")
121
117
  }
122
118
  }
123
119
  }
124
120
 
125
121
  private fun handleAudioFocusLoss() {
126
- Log.d(TAG, "Audio focus lost permanently - likely system call active")
122
+ Log.d(TAG, "Audio focus lost - likely system call active")
127
123
  hasAudioFocus = false
128
124
  isSystemCallActive = true
129
125
 
@@ -131,17 +127,16 @@ object CallEngine {
131
127
  activeCalls.values.filter { it.state == CallState.ACTIVE }.forEach { call ->
132
128
  if (!call.wasHeldBySystem) {
133
129
  call.wasHeldBySystem = true
134
- call.state = CallState.HELD_BY_SYSTEM
130
+ call.state = CallState.HELD
135
131
 
136
132
  val connection = telecomConnections[call.callId]
137
133
  connection?.setOnHold()
138
134
 
139
135
  emitEvent(CallEventType.CALL_HELD, JSONObject().apply {
140
136
  put("callId", call.callId)
141
- put("reason", "system_call_active")
142
137
  })
143
138
 
144
- notifySpecificCallStateChanged(appContext!!, call.callId, CallState.HELD_BY_SYSTEM)
139
+ notifySpecificCallStateChanged(appContext!!, call.callId, CallState.HELD)
145
140
  Log.d(TAG, "Call ${call.callId} held by system due to audio focus loss")
146
141
  }
147
142
  }
@@ -150,39 +145,29 @@ object CallEngine {
150
145
  updateForegroundNotification(appContext!!)
151
146
  }
152
147
 
153
- private fun handleAudioFocusLossTransient() {
154
- // Similar to permanent loss but may be temporary
155
- handleAudioFocusLoss()
156
- }
157
-
158
148
  private fun handleAudioFocusGain() {
159
149
  Log.d(TAG, "Audio focus regained - system call likely ended")
160
150
  hasAudioFocus = true
161
151
  isSystemCallActive = false
162
152
 
163
- // Resume calls that were held by system
164
- activeCalls.values.filter { it.state == CallState.HELD_BY_SYSTEM && it.wasHeldBySystem }.forEach { call ->
165
- call.wasHeldBySystem = false
166
- call.state = CallState.ACTIVE
167
-
168
- val connection = telecomConnections[call.callId]
169
- connection?.setActive()
153
+ // Automatically resume calls that were held by system after a short delay
154
+ Handler(Looper.getMainLooper()).postDelayed({
155
+ activeCalls.values.filter { it.state == CallState.HELD && it.wasHeldBySystem }.forEach { call ->
156
+ Log.d(TAG, "Auto-resuming call ${call.callId} after system call ended")
157
+ call.wasHeldBySystem = false
158
+ call.state = CallState.ACTIVE
170
159
 
171
- emitEvent(CallEventType.CALL_UNHELD, JSONObject().apply {
172
- put("callId", call.callId)
173
- put("reason", "system_call_ended")
174
- })
175
-
176
- notifySpecificCallStateChanged(appContext!!, call.callId, CallState.ACTIVE)
177
- Log.d(TAG, "Call ${call.callId} resumed after system call ended")
178
- }
160
+ val connection = telecomConnections[call.callId]
161
+ connection?.setActive()
179
162
 
180
- updateForegroundNotification(appContext!!)
181
- }
163
+ emitEvent(CallEventType.CALL_UNHELD, JSONObject().apply {
164
+ put("callId", call.callId)
165
+ })
182
166
 
183
- private fun handleAudioFocusLossCanDuck() {
184
- // Lower volume but continue
185
- Log.d(TAG, "Audio focus loss - can duck")
167
+ notifySpecificCallStateChanged(appContext!!, call.callId, CallState.ACTIVE)
168
+ }
169
+ updateForegroundNotification(appContext!!)
170
+ }, 1000) // 1 second delay to ensure system is ready
186
171
  }
187
172
 
188
173
  private fun requestAudioFocus(): Boolean {
@@ -311,7 +296,6 @@ object CallEngine {
311
296
  obj.put("callData", it.callData)
312
297
  obj.put("state", it.state.name)
313
298
  obj.put("callType", it.callType)
314
- obj.put("wasHeldBySystem", it.wasHeldBySystem)
315
299
  jsonArray.put(obj)
316
300
  }
317
301
  val result = jsonArray.toString()
@@ -333,7 +317,7 @@ object CallEngine {
333
317
  }
334
318
 
335
319
  // Check if there's an active call when receiving incoming
336
- val activeCall = activeCalls.values.find { it.state == CallState.ACTIVE || it.state == CallState.HELD_BY_SYSTEM }
320
+ val activeCall = activeCalls.values.find { it.state == CallState.ACTIVE || it.state == CallState.HELD }
337
321
  if (activeCall != null && !canMakeMultipleCalls) {
338
322
  Log.d(TAG, "Active call exists when receiving incoming call. Auto-rejecting: $callId")
339
323
  rejectIncomingCallCollision(callId, "Another call is already active")
@@ -432,14 +416,13 @@ object CallEngine {
432
416
  startForegroundService(context)
433
417
  Log.d(TAG, "Successfully reported outgoing call to TelecomManager via placeCall for $callId")
434
418
 
435
- // Request audio focus for outgoing call
436
- if (requestAudioFocus()) {
437
- startRingback()
438
- setInitialAudioRoute(context, parsedCallType)
439
- }
419
+ // Request audio focus and start ringback
420
+ requestAudioFocus()
421
+ startRingback()
440
422
 
441
423
  bringAppToForeground(context)
442
424
  keepScreenAwake(context, true)
425
+ setInitialAudioRoute(context, parsedCallType)
443
426
  } catch (e: SecurityException) {
444
427
  Log.e(TAG, "SecurityException: Failed to start outgoing call via placeCall. Check MANAGE_OWN_CALLS permission: ${e.message}", e)
445
428
  endCall(context, callId)
@@ -478,9 +461,7 @@ object CallEngine {
478
461
  cancelIncomingCallUI(context)
479
462
 
480
463
  // Request audio focus when answering
481
- if (!hasAudioFocus) {
482
- requestAudioFocus()
483
- }
464
+ requestAudioFocus()
484
465
 
485
466
  // Update call state
486
467
  activeCalls[callId]?.state = CallState.ACTIVE
@@ -538,31 +519,16 @@ object CallEngine {
538
519
  fun unholdCall(context: Context, callId: String) {
539
520
  Log.d(TAG, "unholdCall: $callId")
540
521
  val callInfo = activeCalls[callId]
541
- if (callInfo?.state != CallState.HELD && callInfo?.state != CallState.HELD_BY_SYSTEM) {
522
+ if (callInfo?.state != CallState.HELD) {
542
523
  Log.w(TAG, "Cannot unhold call $callId - not in held state (current: ${callInfo?.state})")
543
524
  return
544
525
  }
545
526
 
546
- // If call was held by system, don't allow manual unhold until system allows it
547
- if (callInfo.state == CallState.HELD_BY_SYSTEM && isSystemCallActive) {
548
- Log.w(TAG, "Cannot unhold call $callId - held by system and system call still active")
549
- emitEvent(CallEventType.CALL_UNHOLD_FAILED, JSONObject().apply {
550
- put("callId", callId)
551
- put("reason", "system_call_active")
552
- })
553
- return
554
- }
555
-
556
- // Request audio focus before unholding
527
+ // Try to request audio focus, but don't fail if we can't get it immediately
528
+ // The system will handle audio routing properly
557
529
  if (!hasAudioFocus) {
558
- if (!requestAudioFocus()) {
559
- Log.w(TAG, "Cannot unhold call $callId - failed to gain audio focus")
560
- emitEvent(CallEventType.CALL_UNHOLD_FAILED, JSONObject().apply {
561
- put("callId", callId)
562
- put("reason", "audio_focus_failed")
563
- })
564
- return
565
- }
530
+ Log.d(TAG, "Attempting to request audio focus for unhold")
531
+ requestAudioFocus()
566
532
  }
567
533
 
568
534
  activeCalls[callId]?.state = CallState.ACTIVE
@@ -574,6 +540,8 @@ object CallEngine {
574
540
  emitEvent(CallEventType.CALL_UNHELD, JSONObject().put("callId", callId))
575
541
  updateLockScreenBypass()
576
542
  notifySpecificCallStateChanged(context, callId, CallState.ACTIVE)
543
+
544
+ Log.d(TAG, "Call $callId successfully unheld")
577
545
  }
578
546
 
579
547
  fun muteCall(context: Context, callId: String) {
@@ -880,8 +848,7 @@ object CallEngine {
880
848
  it.value.state == CallState.ACTIVE ||
881
849
  it.value.state == CallState.INCOMING ||
882
850
  it.value.state == CallState.DIALING ||
883
- it.value.state == CallState.HELD ||
884
- it.value.state == CallState.HELD_BY_SYSTEM
851
+ it.value.state == CallState.HELD
885
852
  }
886
853
 
887
854
  private fun validateOutgoingCallRequest(): Boolean {
@@ -1037,8 +1004,7 @@ object CallEngine {
1037
1004
  // Find the current active call to pass its info
1038
1005
  val currentCall = activeCalls.values.find {
1039
1006
  it.state == CallState.ACTIVE || it.state == CallState.INCOMING ||
1040
- it.state == CallState.DIALING || it.state == CallState.HELD ||
1041
- it.state == CallState.HELD_BY_SYSTEM
1007
+ it.state == CallState.DIALING || it.state == CallState.HELD
1042
1008
  }
1043
1009
 
1044
1010
  val intent = Intent(context, CallForegroundService::class.java)
@@ -1203,7 +1169,6 @@ object CallEngine {
1203
1169
  put("callData", callInfo.callData)
1204
1170
  put("state", newState.name)
1205
1171
  put("callType", callInfo.callType)
1206
- put("wasHeldBySystem", callInfo.wasHeldBySystem)
1207
1172
  }
1208
1173
 
1209
1174
  Log.d(TAG, "Specific call state changed. Emitting CALL_STATE_CHANGED for $callId: $newState")
@@ -1213,9 +1178,8 @@ object CallEngine {
1213
1178
  private fun updateForegroundNotification(context: Context) {
1214
1179
  val activeCall = activeCalls.values.find { it.state == CallState.ACTIVE }
1215
1180
  val heldCall = activeCalls.values.find { it.state == CallState.HELD }
1216
- val heldBySystemCall = activeCalls.values.find { it.state == CallState.HELD_BY_SYSTEM }
1217
1181
 
1218
- val callToShow = activeCall ?: heldCall ?: heldBySystemCall
1182
+ val callToShow = activeCall ?: heldCall
1219
1183
  callToShow?.let {
1220
1184
  val intent = Intent(context, CallForegroundService::class.java)
1221
1185
  intent.putExtra("UPDATE_NOTIFICATION", true)
@@ -1236,8 +1200,6 @@ object CallEngine {
1236
1200
  stopForegroundService(context)
1237
1201
  keepScreenAwake(context, false)
1238
1202
  resetAudioMode(context)
1239
- abandonAudioFocus()
1240
1203
  isSystemCallActive = false
1241
- wasHeldBySystem = false
1242
1204
  }
1243
1205
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qusaieilouti99/call-manager",
3
- "version": "0.1.52",
3
+ "version": "0.1.53",
4
4
  "description": "Call manager",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",