@qusaieilouti99/call-manager 0.1.159 → 0.1.161
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.
|
@@ -389,9 +389,7 @@ object CallEngine {
|
|
|
389
389
|
updateLockScreenBypass()
|
|
390
390
|
setAudioMode()
|
|
391
391
|
registerAudioDeviceCallback()
|
|
392
|
-
|
|
393
|
-
setInitialAudioRoute(callInfo.callType)
|
|
394
|
-
}, 500L)
|
|
392
|
+
// The initial audio route will be set automatically by the onStateChanged(STATE_ACTIVE) callback
|
|
395
393
|
if (isLocalAnswer) {
|
|
396
394
|
emitCallAnsweredWithMetadata(callId)
|
|
397
395
|
} else {
|
|
@@ -489,7 +487,6 @@ object CallEngine {
|
|
|
489
487
|
/**
|
|
490
488
|
* FIX: This is the correct way to handle mute requests from the app's UI.
|
|
491
489
|
* It uses the AudioManager, which is the source of truth for microphone state.
|
|
492
|
-
* There is no `setMuted` method on the Connection object.
|
|
493
490
|
*/
|
|
494
491
|
fun setMuted(callId: String, muted: Boolean) {
|
|
495
492
|
if (!activeCalls.containsKey(callId)) {
|
|
@@ -513,7 +510,9 @@ object CallEngine {
|
|
|
513
510
|
return
|
|
514
511
|
}
|
|
515
512
|
// Ensure our audio manager state is consistent with the system's request
|
|
516
|
-
audioManager?.isMicrophoneMute
|
|
513
|
+
if (audioManager?.isMicrophoneMute != isMuted) {
|
|
514
|
+
audioManager?.isMicrophoneMute = isMuted
|
|
515
|
+
}
|
|
517
516
|
Log.d(TAG, "Telecom reported mute state for $callId is now: $isMuted. Emitting event.")
|
|
518
517
|
val eventType = if (isMuted) CallEventType.CALL_MUTED else CallEventType.CALL_UNMUTED
|
|
519
518
|
emitEvent(eventType, JSONObject().put("callId", callId))
|
|
@@ -675,8 +674,11 @@ object CallEngine {
|
|
|
675
674
|
}
|
|
676
675
|
}
|
|
677
676
|
|
|
678
|
-
|
|
679
|
-
|
|
677
|
+
/**
|
|
678
|
+
* FIX: This is now called from MyConnection when the call becomes active.
|
|
679
|
+
*/
|
|
680
|
+
fun setInitialAudioRouteForCall(callId: String, callType: String) {
|
|
681
|
+
Log.d(TAG, "Setting initial audio route for call $callId, type: $callType")
|
|
680
682
|
val defaultRoute = when {
|
|
681
683
|
isBluetoothDeviceConnected() -> "Bluetooth"
|
|
682
684
|
isWiredHeadsetConnected() -> "Headset"
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
package com.margelo.nitro.qusaieilouti99.callmanager
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
|
+
import android.os.Build
|
|
5
|
+
import android.os.OutcomeReceiver
|
|
4
6
|
import android.telecom.CallAudioState
|
|
7
|
+
import android.telecom.CallEndpoint
|
|
8
|
+
import android.telecom.CallEndpointException
|
|
5
9
|
import android.telecom.Connection
|
|
6
10
|
import android.telecom.DisconnectCause
|
|
7
11
|
import android.telecom.VideoProfile
|
|
8
12
|
import android.util.Log
|
|
13
|
+
import androidx.annotation.RequiresApi
|
|
9
14
|
import org.json.JSONObject
|
|
15
|
+
import java.util.concurrent.Executor
|
|
10
16
|
|
|
11
17
|
class MyConnection(
|
|
12
18
|
private val context: Context,
|
|
@@ -21,6 +27,8 @@ class MyConnection(
|
|
|
21
27
|
}
|
|
22
28
|
|
|
23
29
|
private var lastAudioState: CallAudioState? = null
|
|
30
|
+
// FIX: We must store the list of available endpoints when the system gives it to us.
|
|
31
|
+
private var availableCallEndpoints: List<CallEndpoint> = emptyList()
|
|
24
32
|
|
|
25
33
|
init {
|
|
26
34
|
connectionProperties = PROPERTY_SELF_MANAGED
|
|
@@ -41,6 +49,14 @@ class MyConnection(
|
|
|
41
49
|
Log.d(TAG, "MyConnection for callId $callId created and added to CallEngine. Type: $callType")
|
|
42
50
|
}
|
|
43
51
|
|
|
52
|
+
// FIX: Override the callback to receive endpoint updates from the system.
|
|
53
|
+
@RequiresApi(Build.VERSION_CODES.P)
|
|
54
|
+
override fun onAvailableCallEndpointsChanged(endpoints: List<CallEndpoint>) {
|
|
55
|
+
super.onAvailableCallEndpointsChanged(endpoints)
|
|
56
|
+
Log.d(TAG, "Available call endpoints changed. Count: ${endpoints.size}")
|
|
57
|
+
this.availableCallEndpoints = endpoints
|
|
58
|
+
}
|
|
59
|
+
|
|
44
60
|
override fun onSilence() {
|
|
45
61
|
super.onSilence()
|
|
46
62
|
Log.d(TAG, "onSilence called by system for callId: $callId. Silencing ringtone.")
|
|
@@ -103,42 +119,57 @@ class MyConnection(
|
|
|
103
119
|
)
|
|
104
120
|
}
|
|
105
121
|
|
|
106
|
-
override fun
|
|
107
|
-
super.
|
|
108
|
-
|
|
122
|
+
override fun onStateChanged(state: Int) {
|
|
123
|
+
super.onStateChanged(state)
|
|
124
|
+
if (state == Connection.STATE_ACTIVE) {
|
|
125
|
+
CallEngine.setInitialAudioRouteForCall(callId, callType)
|
|
126
|
+
} else if (state == Connection.STATE_DISCONNECTED) {
|
|
127
|
+
CallEngine.removeTelecomConnection(callId)
|
|
128
|
+
}
|
|
109
129
|
}
|
|
110
130
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
131
|
+
fun requestAudioRoute(route: Int) {
|
|
132
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
|
133
|
+
requestCallEndpointChange(route)
|
|
134
|
+
} else {
|
|
135
|
+
Log.d(TAG, "Using deprecated setAudioRoute for API < 34")
|
|
136
|
+
@Suppress("DEPRECATION")
|
|
137
|
+
setAudioRoute(route)
|
|
138
|
+
}
|
|
114
139
|
}
|
|
115
140
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
STATE_NEW -> "NEW"
|
|
121
|
-
STATE_RINGING -> "RINGING"
|
|
122
|
-
STATE_DIALING -> "DIALING"
|
|
123
|
-
STATE_ACTIVE -> "ACTIVE"
|
|
124
|
-
STATE_HOLDING -> "HOLDING"
|
|
125
|
-
STATE_DISCONNECTED -> "DISCONNECTED"
|
|
126
|
-
else -> "UNKNOWN"
|
|
141
|
+
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
|
|
142
|
+
private fun requestCallEndpointChange(route: Int) {
|
|
143
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
|
144
|
+
return // Should not happen if we are on UDC+, but a good safeguard.
|
|
127
145
|
}
|
|
128
|
-
Log.d(TAG, "Connection state changed for callId: $callId. New state: $stateString ($state)")
|
|
129
146
|
|
|
130
|
-
|
|
131
|
-
|
|
147
|
+
val endpointType = when (route) {
|
|
148
|
+
CallAudioState.ROUTE_EARPIECE -> CallEndpoint.TYPE_EARPIECE
|
|
149
|
+
CallAudioState.ROUTE_SPEAKER -> CallEndpoint.TYPE_SPEAKER
|
|
150
|
+
CallAudioState.ROUTE_BLUETOOTH -> CallEndpoint.TYPE_BLUETOOTH
|
|
151
|
+
CallAudioState.ROUTE_WIRED_HEADSET -> CallEndpoint.TYPE_WIRED_HEADSET
|
|
152
|
+
else -> {
|
|
153
|
+
Log.w(TAG, "Unsupported route requested: $route")
|
|
154
|
+
return
|
|
155
|
+
}
|
|
132
156
|
}
|
|
133
|
-
}
|
|
134
157
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
158
|
+
// FIX: Use the locally stored list that the system gave us.
|
|
159
|
+
val targetEndpoint = this.availableCallEndpoints.find { it.type == endpointType }
|
|
160
|
+
|
|
161
|
+
if (targetEndpoint != null) {
|
|
162
|
+
Log.d(TAG, "Requesting CallEndpoint change to: ${targetEndpoint.name} (Type: $endpointType)")
|
|
163
|
+
requestCallEndpointChange(targetEndpoint, context.mainExecutor, object : OutcomeReceiver<Void, CallEndpointException> {
|
|
164
|
+
override fun onResult(result: Void?) {
|
|
165
|
+
Log.d(TAG, "CallEndpoint change request successful.")
|
|
166
|
+
}
|
|
167
|
+
override fun onError(error: CallEndpointException) {
|
|
168
|
+
Log.e(TAG, "CallEndpoint change request failed.", error)
|
|
169
|
+
}
|
|
170
|
+
})
|
|
171
|
+
} else {
|
|
172
|
+
Log.w(TAG, "Could not find an available CallEndpoint for type: $endpointType")
|
|
173
|
+
}
|
|
143
174
|
}
|
|
144
175
|
}
|