@qusaieilouti99/call-manager 0.1.66 → 0.1.68
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.
|
@@ -13,7 +13,6 @@ import android.graphics.Color
|
|
|
13
13
|
import android.media.AudioAttributes
|
|
14
14
|
import android.media.AudioDeviceCallback
|
|
15
15
|
import android.media.AudioDeviceInfo
|
|
16
|
-
import android.media.AudioFocusRequest
|
|
17
16
|
import android.media.AudioManager
|
|
18
17
|
import android.media.MediaPlayer
|
|
19
18
|
import android.media.RingtoneManager
|
|
@@ -51,13 +50,11 @@ object CallEngine {
|
|
|
51
50
|
private val isInitialized = AtomicBoolean(false)
|
|
52
51
|
private val initializationLock = Any()
|
|
53
52
|
|
|
54
|
-
// Audio & Media
|
|
53
|
+
// Simplified Audio & Media Management (NO MANUAL AUDIO FOCUS)
|
|
55
54
|
private var ringtone: android.media.Ringtone? = null
|
|
56
55
|
private var ringbackPlayer: MediaPlayer? = null
|
|
57
56
|
private var audioManager: AudioManager? = null
|
|
58
57
|
private var wakeLock: PowerManager.WakeLock? = null
|
|
59
|
-
private var audioFocusRequest: AudioFocusRequest? = null
|
|
60
|
-
private var hasAudioFocus: Boolean = false
|
|
61
58
|
|
|
62
59
|
// Call State Management
|
|
63
60
|
private val activeCalls = ConcurrentHashMap<String, CallInfo>()
|
|
@@ -67,7 +64,7 @@ object CallEngine {
|
|
|
67
64
|
private var currentCallId: String? = null
|
|
68
65
|
private var canMakeMultipleCalls: Boolean = false
|
|
69
66
|
|
|
70
|
-
// Audio State Tracking
|
|
67
|
+
// Audio State Tracking
|
|
71
68
|
private var lastAudioRoutesInfo: AudioRoutesInfo? = null
|
|
72
69
|
|
|
73
70
|
// Lock Screen Bypass
|
|
@@ -129,92 +126,6 @@ object CallEngine {
|
|
|
129
126
|
}
|
|
130
127
|
}
|
|
131
128
|
|
|
132
|
-
// --- SIMPLIFIED Audio Focus Management ---
|
|
133
|
-
private val audioFocusChangeListener = AudioManager.OnAudioFocusChangeListener { focusChange ->
|
|
134
|
-
Log.d(TAG, "Audio focus changed: $focusChange")
|
|
135
|
-
when (focusChange) {
|
|
136
|
-
AudioManager.AUDIOFOCUS_LOSS -> {
|
|
137
|
-
// Only hold calls on PERMANENT audio focus loss (like system calls)
|
|
138
|
-
Log.d(TAG, "Permanent audio focus loss - holding calls")
|
|
139
|
-
hasAudioFocus = false
|
|
140
|
-
holdAllActiveCalls(heldBySystem = true)
|
|
141
|
-
}
|
|
142
|
-
AudioManager.AUDIOFOCUS_GAIN -> {
|
|
143
|
-
Log.d(TAG, "Audio focus gained - resuming held calls")
|
|
144
|
-
hasAudioFocus = true
|
|
145
|
-
Handler(Looper.getMainLooper()).postDelayed({
|
|
146
|
-
resumeSystemHeldCalls()
|
|
147
|
-
}, 1000)
|
|
148
|
-
}
|
|
149
|
-
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT,
|
|
150
|
-
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
|
|
151
|
-
// Don't hold calls for transient losses - just note we lost focus
|
|
152
|
-
Log.d(TAG, "Transient audio focus loss - keeping calls active")
|
|
153
|
-
hasAudioFocus = false
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
private fun requestAudioFocus(): Boolean {
|
|
159
|
-
val context = requireContext()
|
|
160
|
-
audioManager = audioManager ?: context.getSystemService(Context.AUDIO_SERVICE) as? AudioManager
|
|
161
|
-
|
|
162
|
-
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
163
|
-
if (audioFocusRequest == null) {
|
|
164
|
-
audioFocusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
|
|
165
|
-
.setAudioAttributes(
|
|
166
|
-
AudioAttributes.Builder()
|
|
167
|
-
.setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
|
|
168
|
-
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
|
|
169
|
-
.build()
|
|
170
|
-
)
|
|
171
|
-
.setOnAudioFocusChangeListener(audioFocusChangeListener)
|
|
172
|
-
.setAcceptsDelayedFocusGain(true)
|
|
173
|
-
.build()
|
|
174
|
-
}
|
|
175
|
-
val result = audioManager?.requestAudioFocus(audioFocusRequest!!)
|
|
176
|
-
hasAudioFocus = result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED
|
|
177
|
-
Log.d(TAG, "Audio focus request result: $result (granted: $hasAudioFocus)")
|
|
178
|
-
hasAudioFocus
|
|
179
|
-
} else {
|
|
180
|
-
@Suppress("DEPRECATION")
|
|
181
|
-
val result = audioManager?.requestAudioFocus(
|
|
182
|
-
audioFocusChangeListener,
|
|
183
|
-
AudioManager.STREAM_VOICE_CALL,
|
|
184
|
-
AudioManager.AUDIOFOCUS_GAIN
|
|
185
|
-
)
|
|
186
|
-
hasAudioFocus = result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED
|
|
187
|
-
hasAudioFocus
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
private fun abandonAudioFocus() {
|
|
192
|
-
audioManager?.let { am ->
|
|
193
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
194
|
-
audioFocusRequest?.let { request ->
|
|
195
|
-
am.abandonAudioFocusRequest(request)
|
|
196
|
-
}
|
|
197
|
-
} else {
|
|
198
|
-
@Suppress("DEPRECATION")
|
|
199
|
-
am.abandonAudioFocus(audioFocusChangeListener)
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
hasAudioFocus = false
|
|
203
|
-
Log.d(TAG, "Audio focus abandoned")
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
private fun holdAllActiveCalls(heldBySystem: Boolean) {
|
|
207
|
-
activeCalls.values.filter { it.state == CallState.ACTIVE }.forEach { call ->
|
|
208
|
-
holdCallInternal(call.callId, heldBySystem = heldBySystem)
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
private fun resumeSystemHeldCalls() {
|
|
213
|
-
activeCalls.values.filter { it.state == CallState.HELD && it.wasHeldBySystem }.forEach { call ->
|
|
214
|
-
unholdCallInternal(call.callId, resumedBySystem = true)
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
129
|
// --- Lock Screen Bypass Management ---
|
|
219
130
|
fun registerLockScreenBypassCallback(callback: LockScreenBypassCallback) {
|
|
220
131
|
lockScreenBypassCallbacks.add(callback)
|
|
@@ -373,6 +284,9 @@ object CallEngine {
|
|
|
373
284
|
currentCallId = callId
|
|
374
285
|
Log.d(TAG, "Call $callId added to activeCalls. State: DIALING")
|
|
375
286
|
|
|
287
|
+
// ONLY set audio mode - let system handle audio focus for self-managed calls
|
|
288
|
+
setAudioMode()
|
|
289
|
+
|
|
376
290
|
registerPhoneAccount()
|
|
377
291
|
val telecomManager = context.getSystemService(Context.TELECOM_SERVICE) as TelecomManager
|
|
378
292
|
val phoneAccountHandle = getPhoneAccountHandle()
|
|
@@ -396,8 +310,7 @@ object CallEngine {
|
|
|
396
310
|
telecomManager.placeCall(addressUri, extras)
|
|
397
311
|
startForegroundService()
|
|
398
312
|
|
|
399
|
-
//
|
|
400
|
-
requestAudioFocus()
|
|
313
|
+
// Start ringback (system will handle audio focus)
|
|
401
314
|
startRingback()
|
|
402
315
|
|
|
403
316
|
bringAppToForeground()
|
|
@@ -441,7 +354,7 @@ object CallEngine {
|
|
|
441
354
|
Log.d(TAG, "Call $callId started as ACTIVE")
|
|
442
355
|
|
|
443
356
|
registerPhoneAccount()
|
|
444
|
-
|
|
357
|
+
setAudioMode()
|
|
445
358
|
bringAppToForeground()
|
|
446
359
|
startForegroundService()
|
|
447
360
|
keepScreenAwake(true)
|
|
@@ -452,7 +365,7 @@ object CallEngine {
|
|
|
452
365
|
emitOutgoingCallAnsweredWithMetadata(callId)
|
|
453
366
|
}
|
|
454
367
|
|
|
455
|
-
// --- Call Answer Management -
|
|
368
|
+
// --- Call Answer Management (SIMPLIFIED - NO MANUAL AUDIO FOCUS) ---
|
|
456
369
|
fun callAnsweredFromJS(callId: String) {
|
|
457
370
|
Log.d(TAG, "callAnsweredFromJS: $callId - remote party answered")
|
|
458
371
|
coreCallAnswered(callId, isLocalAnswer = false)
|
|
@@ -463,7 +376,7 @@ object CallEngine {
|
|
|
463
376
|
coreCallAnswered(callId, isLocalAnswer = true)
|
|
464
377
|
}
|
|
465
378
|
|
|
466
|
-
// SIMPLIFIED:
|
|
379
|
+
// SIMPLIFIED: Let system handle audio focus for self-managed calls
|
|
467
380
|
private fun coreCallAnswered(callId: String, isLocalAnswer: Boolean) {
|
|
468
381
|
Log.d(TAG, "coreCallAnswered: $callId, isLocalAnswer: $isLocalAnswer")
|
|
469
382
|
|
|
@@ -473,19 +386,20 @@ object CallEngine {
|
|
|
473
386
|
return
|
|
474
387
|
}
|
|
475
388
|
|
|
476
|
-
//
|
|
389
|
+
// Set audio mode and let system handle audio focus
|
|
390
|
+
setAudioMode()
|
|
391
|
+
|
|
392
|
+
// Set call to ACTIVE
|
|
477
393
|
activeCalls[callId] = callInfo.copy(state = CallState.ACTIVE)
|
|
478
394
|
currentCallId = callId
|
|
479
|
-
Log.d(TAG, "Call $callId set to ACTIVE state")
|
|
395
|
+
Log.d(TAG, "Call $callId set to ACTIVE state (system manages audio focus)")
|
|
480
396
|
|
|
481
397
|
// Clean up media and UI
|
|
482
398
|
stopRingtone()
|
|
483
399
|
stopRingback()
|
|
484
400
|
cancelIncomingCallUI()
|
|
485
401
|
|
|
486
|
-
//
|
|
487
|
-
requestAudioFocus()
|
|
488
|
-
|
|
402
|
+
// Handle multiple calls
|
|
489
403
|
if (!canMakeMultipleCalls) {
|
|
490
404
|
activeCalls.filter { it.key != callId }.values.forEach { otherCall ->
|
|
491
405
|
if (otherCall.state == CallState.ACTIVE) {
|
|
@@ -497,10 +411,9 @@ object CallEngine {
|
|
|
497
411
|
bringAppToForeground()
|
|
498
412
|
startForegroundService()
|
|
499
413
|
keepScreenAwake(true)
|
|
500
|
-
setAudioMode()
|
|
501
414
|
updateLockScreenBypass()
|
|
502
415
|
|
|
503
|
-
//
|
|
416
|
+
// Emit events based on call direction
|
|
504
417
|
if (isLocalAnswer) {
|
|
505
418
|
emitCallAnsweredWithMetadata(callId)
|
|
506
419
|
} else {
|
|
@@ -716,7 +629,7 @@ object CallEngine {
|
|
|
716
629
|
})
|
|
717
630
|
}
|
|
718
631
|
|
|
719
|
-
// --- Audio Management ---
|
|
632
|
+
// --- Audio Management (SIMPLIFIED - NO MANUAL AUDIO FOCUS) ---
|
|
720
633
|
fun getAudioDevices(): AudioRoutesInfo {
|
|
721
634
|
val context = requireContext()
|
|
722
635
|
audioManager = audioManager ?: context.getSystemService(Context.AUDIO_SERVICE) as? AudioManager ?: run {
|
|
@@ -822,6 +735,7 @@ object CallEngine {
|
|
|
822
735
|
|
|
823
736
|
private fun setAudioMode() {
|
|
824
737
|
audioManager?.mode = AudioManager.MODE_IN_COMMUNICATION
|
|
738
|
+
Log.d(TAG, "Audio mode set to MODE_IN_COMMUNICATION (system handles audio focus)")
|
|
825
739
|
}
|
|
826
740
|
|
|
827
741
|
private fun resetAudioMode() {
|
|
@@ -830,7 +744,7 @@ object CallEngine {
|
|
|
830
744
|
audioManager?.stopBluetoothSco()
|
|
831
745
|
audioManager?.isBluetoothScoOn = false
|
|
832
746
|
audioManager?.isSpeakerphoneOn = false
|
|
833
|
-
|
|
747
|
+
Log.d(TAG, "Audio mode reset to MODE_NORMAL")
|
|
834
748
|
}
|
|
835
749
|
}
|
|
836
750
|
|
|
@@ -1028,7 +942,7 @@ object CallEngine {
|
|
|
1028
942
|
stopRingtone()
|
|
1029
943
|
}
|
|
1030
944
|
|
|
1031
|
-
// --- Service Management
|
|
945
|
+
// --- Service Management ---
|
|
1032
946
|
private fun startForegroundService() {
|
|
1033
947
|
val context = requireContext()
|
|
1034
948
|
val currentCall = activeCalls.values.find {
|
|
@@ -1161,7 +1075,7 @@ object CallEngine {
|
|
|
1161
1075
|
}
|
|
1162
1076
|
}
|
|
1163
1077
|
|
|
1164
|
-
// --- Cleanup
|
|
1078
|
+
// --- Cleanup ---
|
|
1165
1079
|
private fun cleanup() {
|
|
1166
1080
|
Log.d(TAG, "Performing cleanup")
|
|
1167
1081
|
stopForegroundService()
|