@qusaieilouti99/call-manager 0.1.44 → 0.1.46

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.
@@ -1,22 +1,67 @@
1
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
- package="com.margelo.nitro.qusaieilouti99.callmanager">
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
3
2
 
4
- <!-- Essential permissions that the library requires -->
3
+ <!-- REQUIRED FOR TELECOM API (Self-managed connections) -->
5
4
  <uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
6
- <uses-permission android:name="android.permission.BIND_TELECOM_CONNECTION_SERVICE" />
5
+ <!-- Required for recording audio in calls -->
7
6
  <uses-permission android:name="android.permission.RECORD_AUDIO" />
7
+ <!-- Required for changing audio routes like speakerphone -->
8
8
  <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
9
+ <!-- For keeping screen awake during calls -->
9
10
  <uses-permission android:name="android.permission.WAKE_LOCK" />
11
+ <!-- For vibrating the device (e.g., incoming call) -->
10
12
  <uses-permission android:name="android.permission.VIBRATE" />
11
- <uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
12
- <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
13
- <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
14
- <uses-permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL" />
15
13
 
16
- <!-- Bluetooth permissions -->
14
+ <!-- Bluetooth permissions for audio routing -->
15
+ <!-- For Android 11 (API 30) and lower -->
17
16
  <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
18
17
  <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
18
+ <!-- For Android 12 (API 31) and higher -->
19
19
  <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
20
20
 
21
- <!-- No components needed here - they're declared in app manifest -->
21
+ <!-- Required for foreground service on older devices (< Android P) -->
22
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
23
+ <!-- Required for foreground service with phoneCall type on Android 12 (API 31) and higher -->
24
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL" />
25
+
26
+ <application>
27
+ <!-- Your CallActivity for full-screen incoming call UI -->
28
+ <activity
29
+ android:name=".CallActivity"
30
+ android:exported="true"
31
+ android:showOnLockScreen="true"
32
+ android:turnScreenOn="true"
33
+ android:theme="@android:style/Theme.DeviceDefault.NoActionBar"
34
+ android:screenOrientation="portrait"
35
+ android:launchMode="singleTop"
36
+ android:excludeFromRecents="true"
37
+ android:taskAffinity=""
38
+ android:windowSoftInputMode="adjustResize">
39
+ <intent-filter>
40
+ <action android:name="android.intent.action.VIEW"/>
41
+ <category android:name="android.intent.category.DEFAULT"/>
42
+ </intent-filter>
43
+ </activity>
44
+
45
+ <!-- The ConnectionService, crucial for Telecom API integration -->
46
+ <service
47
+ android:name=".MyConnectionService"
48
+ android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
49
+ android:exported="true">
50
+ <intent-filter>
51
+ <action android:name="android.telecom.ConnectionService" />
52
+ </intent-filter>
53
+ </service>
54
+
55
+ <!-- The foreground service to keep the call alive in the background -->
56
+ <service
57
+ android:name=".CallForegroundService"
58
+ android:enabled="true"
59
+ android:exported="false"
60
+ android:foregroundServiceType="phoneCall" />
61
+
62
+ <!-- Receiver for notification actions (Answer/Decline) -->
63
+ <receiver
64
+ android:name=".CallNotificationActionReceiver"
65
+ android:exported="false" />
66
+ </application>
22
67
  </manifest>
@@ -49,8 +49,8 @@ class CallActivity : Activity() {
49
49
  callType = intent.getStringExtra("callType") ?: "Audio"
50
50
  Log.d(TAG, "CallActivity received callId: $callId, callType: $callType")
51
51
 
52
- // Cancel incoming call UI immediately when CallActivity shows
53
- // This is handled in the core answer function now
52
+ // FIXED: Immediate cleanup of notifications when CallActivity is shown
53
+ CallEngine.cancelIncomingCallUI(this)
54
54
 
55
55
  val callerName = intent.getStringExtra("callerName") ?: "Unknown"
56
56
  val nameView = findViewById<TextView>(R.id.caller_name)
@@ -63,7 +63,7 @@ class CallActivity : Activity() {
63
63
  Log.d(TAG, "CallActivity: Answer button clicked for callId: $callId")
64
64
  finishReason = FinishReason.ANSWER
65
65
 
66
- // Use the single core answer function
66
+ // FIXED: Use single source of truth - this will handle all cleanup
67
67
  CallEngine.answerCall(this, callId)
68
68
  finishCallActivity()
69
69
  }
@@ -82,7 +82,10 @@ class CallActivity : Activity() {
82
82
  super.onDestroy()
83
83
  Log.d(TAG, "CallActivity onDestroy for callId: $callId. Reason: $finishReason")
84
84
  timeoutHandler.removeCallbacks(timeoutRunnable)
85
- // Don't stop ringtone here - it's handled in the core functions
85
+
86
+ // FIXED: Ensure cleanup happens regardless of how activity ends
87
+ CallEngine.stopRingtone()
88
+ CallEngine.cancelIncomingCallUI(this)
86
89
  }
87
90
 
88
91
  override fun onBackPressed() {
@@ -28,7 +28,6 @@ import org.json.JSONArray
28
28
  import org.json.JSONObject
29
29
  import java.util.UUID
30
30
  import java.util.concurrent.ConcurrentHashMap
31
- import android.view.WindowManager
32
31
 
33
32
  object CallEngine {
34
33
  private const val TAG = "CallEngine"
@@ -106,7 +105,6 @@ object CallEngine {
106
105
  }
107
106
 
108
107
  // --- Public API ---
109
-
110
108
  fun setCanMakeMultipleCalls(allow: Boolean) {
111
109
  canMakeMultipleCalls = allow
112
110
  Log.d(TAG, "canMakeMultipleCalls set to: $allow")
@@ -165,6 +163,7 @@ object CallEngine {
165
163
  telecomManager.addNewIncomingCall(phoneAccountHandle, extras)
166
164
  startForegroundService(context)
167
165
  Log.d(TAG, "Successfully reported incoming call to TelecomManager for $callId")
166
+ // REMOVED: Don't bring app to foreground for incoming calls - only when answered
168
167
  } catch (e: SecurityException) {
169
168
  Log.e(TAG, "SecurityException: Failed to report incoming call. Check MANAGE_OWN_CALLS permission: ${e.message}", e)
170
169
  endCall(context, callId)
@@ -218,7 +217,8 @@ object CallEngine {
218
217
  startForegroundService(context)
219
218
  Log.d(TAG, "Successfully reported outgoing call to TelecomManager via placeCall for $callId")
220
219
  startRingback()
221
- // Don't bring app to foreground here - wait for answer
220
+ // CHANGED: Bring app to foreground for outgoing calls
221
+ bringAppToForeground(context)
222
222
  keepScreenAwake(context, true)
223
223
  if (parsedCallType == "Video") {
224
224
  setAudioRoute(context, "Speaker")
@@ -235,11 +235,23 @@ object CallEngine {
235
235
  notifyCallStateChanged(context)
236
236
  }
237
237
 
238
- // *** SINGLE SOURCE OF TRUTH: Core Answer Function ***
239
- private fun coreAnswerCall(context: Context, callId: String, isLocalAnswer: Boolean) {
240
- Log.d(TAG, "coreAnswerCall: $callId, isLocalAnswer: $isLocalAnswer")
238
+ // SINGLE SOURCE OF TRUTH: Core function for handling when remote party answers
239
+ fun callAnsweredFromJS(context: Context, callId: String) {
240
+ Log.d(TAG, "callAnsweredFromJS: $callId - remote party answered")
241
+ coreCallAnswered(context, callId, isLocalAnswer = false)
242
+ }
243
+
244
+ // SINGLE SOURCE OF TRUTH: Core function for handling local answer (user answering)
245
+ fun answerCall(context: Context, callId: String) {
246
+ Log.d(TAG, "answerCall: $callId - local party answering")
247
+ coreCallAnswered(context, callId, isLocalAnswer = true)
248
+ }
241
249
 
242
- // Stop all notification sounds and UI immediately
250
+ // SINGLE SOURCE OF TRUTH: Core function that handles ALL call answering logic
251
+ private fun coreCallAnswered(context: Context, callId: String, isLocalAnswer: Boolean) {
252
+ Log.d(TAG, "coreCallAnswered: $callId, isLocalAnswer: $isLocalAnswer")
253
+
254
+ // Stop all ringtones and notifications immediately
243
255
  stopRingtone()
244
256
  stopRingback()
245
257
  cancelIncomingCallUI(context)
@@ -248,41 +260,21 @@ object CallEngine {
248
260
  activeCalls[callId]?.state = CallState.ACTIVE
249
261
  currentCallId = callId
250
262
 
251
- // Handle multi-call scenario
252
263
  if (!canMakeMultipleCalls) {
253
264
  activeCalls.filter { it.key != callId }.values.forEach { it.state = CallState.HELD }
254
265
  }
255
266
 
256
- // Emit event
257
- emitEvent(CallEventType.CALL_ANSWERED, JSONObject().put("callId", callId))
258
-
259
- // Start foreground service if not already started
267
+ // Bring app to foreground only when call is answered
268
+ bringAppToForeground(context)
260
269
  startForegroundService(context)
261
-
262
- // Wake up screen and clear lock screen if needed
263
270
  keepScreenAwake(context, true)
264
-
265
- // Bring app to foreground NOW (not earlier)
266
- bringAppToForeground(context)
267
-
268
- // Reset audio mode
269
271
  resetAudioMode(context)
270
272
 
271
- // Notify state change
273
+ // Emit event
274
+ emitEvent(CallEventType.CALL_ANSWERED, JSONObject().put("callId", callId))
272
275
  notifyCallStateChanged(context)
273
276
 
274
- Log.d(TAG, "coreAnswerCall completed for $callId")
275
- }
276
-
277
- // Public answer methods that delegate to the core function
278
- fun callAnsweredFromJS(context: Context, callId: String) {
279
- Log.d(TAG, "callAnsweredFromJS: $callId - remote party answered")
280
- coreAnswerCall(context, callId, false)
281
- }
282
-
283
- fun answerCall(context: Context, callId: String) {
284
- Log.d(TAG, "answerCall: $callId - local party answering")
285
- coreAnswerCall(context, callId, true)
277
+ Log.d(TAG, "Call $callId successfully answered and UI cleaned up")
286
278
  }
287
279
 
288
280
  fun holdCall(context: Context, callId: String) {
@@ -316,103 +308,113 @@ object CallEngine {
316
308
  emitEvent(CallEventType.CALL_UNMUTED, JSONObject().put("callId", callId))
317
309
  }
318
310
 
311
+ // SINGLE SOURCE OF TRUTH: Core function that handles ALL call ending logic
319
312
  fun endCall(context: Context, callId: String) {
320
313
  appContext = context.applicationContext
321
314
  Log.d(TAG, "endCall: $callId")
315
+
316
+ // Core cleanup logic
317
+ coreEndCall(context, callId)
318
+ }
319
+
320
+ fun endAllCalls(context: Context) {
321
+ Log.d(TAG, "endAllCalls: Ending all active calls.")
322
+ if (activeCalls.isEmpty()) {
323
+ Log.d(TAG, "No active calls, nothing to do.")
324
+ return
325
+ }
326
+ activeCalls.keys.toList().forEach { callId ->
327
+ coreEndCall(context, callId)
328
+ }
329
+ activeCalls.clear()
330
+ telecomConnections.clear()
331
+ currentCallId = null
332
+
333
+ // Final cleanup
334
+ finalCleanup(context)
335
+ notifyCallStateChanged(context)
336
+ }
337
+
338
+ // SINGLE SOURCE OF TRUTH: Core function that handles ending a single call
339
+ private fun coreEndCall(context: Context, callId: String) {
340
+ Log.d(TAG, "coreEndCall: $callId")
341
+
342
+ // Update call state
322
343
  activeCalls[callId]?.state = CallState.ENDED
323
344
  activeCalls.remove(callId)
324
345
  Log.d(TAG, "Call $callId removed from activeCalls. Remaining: ${activeCalls.size}")
325
346
 
347
+ // Stop ringtones and notifications
326
348
  stopRingback()
327
349
  stopRingtone()
350
+ cancelIncomingCallUI(context)
328
351
 
352
+ // Update current call
329
353
  if (currentCallId == callId) {
330
354
  currentCallId = activeCalls.filter { it.value.state != CallState.ENDED }.keys.firstOrNull()
331
355
  Log.d(TAG, "Current call was $callId. New currentCallId: $currentCallId")
332
356
  }
333
357
 
334
- cancelIncomingCallUI(context)
335
-
358
+ // Handle telecom connection
336
359
  val connection = telecomConnections[callId]
337
360
  if (connection != null) {
338
361
  connection.setDisconnected(DisconnectCause(DisconnectCause.LOCAL))
339
362
  connection.destroy()
340
363
  removeTelecomConnection(callId)
341
364
  Log.d(TAG, "Telecom Connection for $callId disconnected and destroyed.")
342
- } else {
343
- Log.d(TAG, "No Telecom Connection found for callId=$callId. Likely an outgoing call not initially handled by Telecom.")
344
365
  }
345
366
 
367
+ // If no more calls, do final cleanup
346
368
  if (activeCalls.isEmpty()) {
347
- Log.d(TAG, "No active calls remaining. Stopping foreground service and releasing resources.")
348
- stopForegroundService(context)
349
- keepScreenAwake(context, false)
350
- resetAudioMode(context)
351
- clearLockScreenBypass(context) // Clear lock screen bypass when all calls end
352
- } else {
353
- Log.d(TAG, "Other calls still active. Foreground service remains.")
369
+ finalCleanup(context)
354
370
  }
355
371
 
356
372
  emitEvent(CallEventType.CALL_ENDED, JSONObject().put("callId", callId))
357
373
  notifyCallStateChanged(context)
358
374
  }
359
375
 
360
- fun endAllCalls(context: Context) {
361
- Log.d(TAG, "endAllCalls: Ending all active calls.")
362
- if (activeCalls.isEmpty()) {
363
- Log.d(TAG, "No active calls, nothing to do.")
364
- return
365
- }
366
- activeCalls.keys.toList().forEach { callId ->
367
- endCall(context, callId)
368
- }
369
- activeCalls.clear()
370
- telecomConnections.clear()
371
- currentCallId = null
372
- cancelIncomingCallUI(context)
376
+ // SINGLE SOURCE OF TRUTH: Final cleanup when all calls are ended
377
+ private fun finalCleanup(context: Context) {
378
+ Log.d(TAG, "Performing final cleanup - no active calls remaining")
379
+
373
380
  stopForegroundService(context)
374
- stopRingtone()
375
- stopRingback()
376
381
  keepScreenAwake(context, false)
377
382
  resetAudioMode(context)
378
- clearLockScreenBypass(context) // Clear lock screen bypass
379
- notifyCallStateChanged(context)
380
- }
381
383
 
382
- fun getActiveCalls(): List<CallInfo> = activeCalls.values.toList()
383
- fun getCurrentCallId(): String? = currentCallId
384
- fun isCallActive(): Boolean = activeCalls.any { it.value.state == CallState.ACTIVE || it.value.state == CallState.INCOMING || it.value.state == CallState.DIALING }
384
+ // FIXED: Clear lock screen bypass when calls end
385
+ clearLockScreenBypass(context)
386
+ }
385
387
 
386
- // --- Clear Lock Screen Bypass ---
388
+ // NEW: Function to clear lock screen bypass
387
389
  private fun clearLockScreenBypass(context: Context) {
388
- Log.d(TAG, "Clearing lock screen bypass for all activities.")
389
-
390
- // Send broadcast to MainActivity to clear flags if it's active
391
- val intent = Intent("com.pingme2022.CLEAR_LOCK_SCREEN_BYPASS")
392
- context.sendBroadcast(intent)
393
-
394
- // Also try to clear on the app context activities
395
390
  try {
396
391
  if (context is Activity) {
397
392
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
398
- (context as Activity).setShowWhenLocked(false)
399
- (context as Activity).setTurnScreenOn(false)
393
+ context.setShowWhenLocked(false)
394
+ context.setTurnScreenOn(false)
400
395
  } else {
401
396
  context.window.clearFlags(
402
- WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
403
- WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON or
404
- WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
397
+ android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
398
+ android.view.WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON or
399
+ android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
405
400
  )
406
401
  }
407
- Log.d(TAG, "Cleared lock screen bypass flags from activity context.")
402
+ Log.d(TAG, "Lock screen bypass flags cleared for Activity")
408
403
  }
409
404
  } catch (e: Exception) {
410
- Log.w(TAG, "Could not clear lock screen bypass from activity context: ${e.message}")
405
+ Log.w(TAG, "Could not clear lock screen bypass flags: ${e.message}")
411
406
  }
412
407
  }
413
408
 
414
- // --- Notification/UI/Foreground ---
409
+ fun getActiveCalls(): List<CallInfo> = activeCalls.values.toList()
410
+ fun getCurrentCallId(): String? = currentCallId
411
+ fun isCallActive(): Boolean = activeCalls.any {
412
+ it.value.state == CallState.ACTIVE ||
413
+ it.value.state == CallState.INCOMING ||
414
+ it.value.state == CallState.DIALING
415
+ }
415
416
 
417
+ // Enhanced incoming call UI with better cleanup
416
418
  fun showIncomingCallUI(context: Context, callId: String, callerName: String, callType: String) {
417
419
  Log.d(TAG, "Showing incoming call UI for $callId, caller: $callerName, callType: $callType")
418
420
  createNotificationChannel(context)
@@ -494,17 +496,20 @@ object CallEngine {
494
496
  val launchIntent = context.packageManager.getLaunchIntentForPackage(packageName)
495
497
  launchIntent?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
496
498
 
499
+ // Handle lock screen bypass for active calls
497
500
  if (isCallActive()) {
498
- Log.d(TAG, "App brought to foreground due to active call")
501
+ // Only set lock screen bypass when bringing to foreground during active calls
502
+ launchIntent?.putExtra("BYPASS_LOCK_SCREEN", true)
503
+ Log.d(TAG, "App brought to foreground with lock screen bypass for active call")
499
504
  } else {
500
- Log.d(TAG, "App brought to foreground via normal launchIntent")
505
+ Log.d(TAG, "App brought to foreground without lock screen bypass")
501
506
  }
502
507
 
503
508
  context.startActivity(launchIntent)
504
509
  }
505
510
 
506
511
  // Rest of the methods remain the same...
507
- // [Audio Device Management, Screen Awake Management, etc. - keeping existing implementations]
512
+ // (getAudioDevices, setAudioRoute, keepScreenAwake, etc. - keeping them unchanged for brevity)
508
513
 
509
514
  // --- Audio Device Management ---
510
515
  fun getAudioDevices(): AudioRoutesInfo {
@@ -640,9 +645,7 @@ object CallEngine {
640
645
  }
641
646
  }
642
647
 
643
- // Continue with rest of existing methods...
644
- // [Audio Device Change Listener, Call State Change Notification, etc.]
645
-
648
+ // --- Audio Device Change Listener ---
646
649
  private val audioDeviceCallback = object : AudioDeviceCallback() {
647
650
  override fun onAudioDevicesAdded(addedDevices: Array<out AudioDeviceInfo>?) {
648
651
  Log.d(TAG, "Audio devices added. Emitting AUDIO_DEVICES_CHANGED.")
@@ -680,6 +683,7 @@ object CallEngine {
680
683
  emitEvent(CallEventType.AUDIO_DEVICES_CHANGED, jsonPayload)
681
684
  }
682
685
 
686
+ // --- Call State Change Notification ---
683
687
  private fun notifyCallStateChanged(context: Context) {
684
688
  val calls = getActiveCalls()
685
689
  val jsonArray = JSONArray()
@@ -725,6 +729,7 @@ object CallEngine {
725
729
  }
726
730
  }
727
731
 
732
+ // PhoneAccount registration is used for both INCOMING and OUTGOING calls when interacting with Telecom
728
733
  private fun registerPhoneAccount(context: Context) {
729
734
  val telecomManager = context.getSystemService(Context.TELECOM_SERVICE) as TelecomManager
730
735
  val phoneAccountHandle = getPhoneAccountHandle(context)
@@ -752,6 +757,7 @@ object CallEngine {
752
757
  )
753
758
  }
754
759
 
760
+ // --- Ringtone Management (for incoming calls, pre-Android S) ---
755
761
  fun playRingtone(context: Context) {
756
762
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
757
763
  Log.d(TAG, "playRingtone: Android S+ detected, system will handle ringtone via Telecom.")
@@ -783,6 +789,7 @@ object CallEngine {
783
789
  ringtone = null
784
790
  }
785
791
 
792
+ // --- Ringback Tone Management (for outgoing calls) ---
786
793
  private fun startRingback() {
787
794
  if (ringbackPlayer != null && ringbackPlayer!!.isPlaying) {
788
795
  Log.d(TAG, "Ringback tone already playing.")
@@ -15,13 +15,21 @@ class CallNotificationActionReceiver : BroadcastReceiver() {
15
15
 
16
16
  when (intent.action) {
17
17
  "com.qusaieilouti99.callmanager.ANSWER_CALL" -> {
18
- Log.d(TAG, "Answer action received for callId: $callId. Using single core answer function.")
18
+ Log.d(TAG, "Answer action received for callId: $callId")
19
19
 
20
- // Use the single core answer function instead of multiple pathways
21
- CallEngine.answerCall(context, callId)
20
+ // FIXED: Use single source of truth for answering
21
+ val connection = CallEngine.getTelecomConnection(callId)
22
+ if (connection != null) {
23
+ connection.onAnswer() // This will trigger MyConnection.onAnswer()
24
+ Log.d(TAG, "Call answered via Telecom connection for callId: $callId")
25
+ } else {
26
+ Log.e(TAG, "No Telecom connection found for callId: $callId. Using direct answer.")
27
+ CallEngine.answerCall(context, callId)
28
+ }
29
+ // NOTE: Don't call bringAppToForeground here - it's handled in coreCallAnswered
22
30
  }
23
31
  "com.qusaieilouti99.callmanager.DECLINE_CALL" -> {
24
- Log.d(TAG, "Decline action received for callId: $callId. Ending call.")
32
+ Log.d(TAG, "Decline action received for callId: $callId")
25
33
  CallEngine.endCall(context, callId)
26
34
  }
27
35
  else -> {
@@ -32,7 +32,6 @@ class MyConnection(
32
32
  JSONObject(callDataJson).optString("callType", "Audio")
33
33
  } catch (e: Exception) { "Audio" }
34
34
 
35
- // Set connection properties and capabilities
36
35
  connectionProperties = Connection.PROPERTY_SELF_MANAGED
37
36
  connectionCapabilities = Connection.CAPABILITY_SUPPORT_HOLD or Connection.CAPABILITY_MUTE
38
37
 
@@ -52,7 +51,7 @@ class MyConnection(
52
51
  Log.d(TAG, "Call answered via Telecom for callId: $callId")
53
52
  setActive()
54
53
 
55
- // Use the single core answer function
54
+ // FIXED: Use single source of truth for answering
56
55
  CallEngine.answerCall(context, callId)
57
56
  }
58
57
 
@@ -84,7 +83,7 @@ class MyConnection(
84
83
 
85
84
  override fun onCallAudioStateChanged(state: CallAudioState) {
86
85
  super.onCallAudioStateChanged(state)
87
- Log.d(TAG, "Audio state changed for callId: $callId. muted=${state.isMuted}, route=${state.route}, supportedRoutes=${state.supportedRouteMask}")
86
+ Log.d(TAG, "Audio state changed for callId: $callId. muted=${state.isMuted}, route=${state.route}")
88
87
 
89
88
  if (state.isMuted) {
90
89
  CallEngine.muteCall(context, callId)
@@ -99,7 +98,6 @@ class MyConnection(
99
98
  CallAudioState.ROUTE_WIRED_HEADSET -> "Headset"
100
99
  else -> "Unknown"
101
100
  }
102
- Log.d(TAG, "CallAudioState changed, new route detected: $routeName")
103
101
  CallEngine.emitEvent(
104
102
  CallEventType.AUDIO_ROUTE_CHANGED,
105
103
  JSONObject().put("callId", callId).put("route", routeName)
@@ -122,8 +120,8 @@ class MyConnection(
122
120
 
123
121
  override fun onShowIncomingCallUi() {
124
122
  super.onShowIncomingCallUi()
125
- Log.d(TAG, "onShowIncomingCallUi for callId: $callId. Showing CallActivity, not bringing MainActivity to foreground yet.")
126
- // Don't bring MainActivity to foreground here - only show CallActivity
127
- // MainActivity will be brought to foreground when the call is answered
123
+ Log.d(TAG, "onShowIncomingCallUi for callId: $callId")
124
+ // REMOVED: Don't bring app to foreground for incoming calls
125
+ // CallEngine.bringAppToForeground(context)
128
126
  }
129
127
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qusaieilouti99/call-manager",
3
- "version": "0.1.44",
3
+ "version": "0.1.46",
4
4
  "description": "Call manager",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",