mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-27 20:09:27 +03:00
VoIP: continue refactoring
This commit is contained in:
parent
7620aa4264
commit
1a9b0265dc
14 changed files with 219 additions and 249 deletions
|
@ -153,7 +153,7 @@ interface VectorComponent {
|
||||||
|
|
||||||
fun pinLocker(): PinLocker
|
fun pinLocker(): PinLocker
|
||||||
|
|
||||||
fun webRtcPeerConnectionManager(): WebRtcCallManager
|
fun webRtcCallManager(): WebRtcCallManager
|
||||||
|
|
||||||
@Component.Factory
|
@Component.Factory
|
||||||
interface Factory {
|
interface Factory {
|
||||||
|
|
|
@ -63,7 +63,7 @@ class CallService : VectorService(), WiredHeadsetStateReceiver.HeadsetEventListe
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
notificationUtils = vectorComponent().notificationUtils()
|
notificationUtils = vectorComponent().notificationUtils()
|
||||||
callManager = vectorComponent().webRtcPeerConnectionManager()
|
callManager = vectorComponent().webRtcCallManager()
|
||||||
callRingPlayerIncoming = CallRingPlayerIncoming(applicationContext)
|
callRingPlayerIncoming = CallRingPlayerIncoming(applicationContext)
|
||||||
callRingPlayerOutgoing = CallRingPlayerOutgoing(applicationContext)
|
callRingPlayerOutgoing = CallRingPlayerOutgoing(applicationContext)
|
||||||
wiredHeadsetStateReceiver = WiredHeadsetStateReceiver.createAndRegister(this, this)
|
wiredHeadsetStateReceiver = WiredHeadsetStateReceiver.createAndRegister(this, this)
|
||||||
|
|
|
@ -23,7 +23,7 @@ import im.vector.app.core.utils.DebouncedClickListener
|
||||||
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
||||||
import org.matrix.android.sdk.api.session.call.CallState
|
import org.matrix.android.sdk.api.session.call.CallState
|
||||||
import im.vector.app.features.call.utils.EglUtils
|
import im.vector.app.features.call.utils.EglUtils
|
||||||
import org.matrix.android.sdk.api.session.call.MxCall
|
import im.vector.app.features.call.webrtc.WebRtcCall
|
||||||
import org.webrtc.RendererCommon
|
import org.webrtc.RendererCommon
|
||||||
import org.webrtc.SurfaceViewRenderer
|
import org.webrtc.SurfaceViewRenderer
|
||||||
|
|
||||||
|
@ -32,26 +32,28 @@ class ActiveCallViewHolder {
|
||||||
private var activeCallPiP: SurfaceViewRenderer? = null
|
private var activeCallPiP: SurfaceViewRenderer? = null
|
||||||
private var activeCallView: ActiveCallView? = null
|
private var activeCallView: ActiveCallView? = null
|
||||||
private var pipWrapper: CardView? = null
|
private var pipWrapper: CardView? = null
|
||||||
|
private var activeCall: WebRtcCall? = null
|
||||||
|
|
||||||
private var activeCallPipInitialized = false
|
private var activeCallPipInitialized = false
|
||||||
|
|
||||||
fun updateCall(activeCall: MxCall?, callManager: WebRtcCallManager) {
|
fun updateCall(activeCall: WebRtcCall?) {
|
||||||
val hasActiveCall = activeCall?.state is CallState.Connected
|
this.activeCall = activeCall
|
||||||
|
val hasActiveCall = activeCall?.mxCall?.state is CallState.Connected
|
||||||
if (hasActiveCall) {
|
if (hasActiveCall) {
|
||||||
val isVideoCall = activeCall?.isVideoCall == true
|
val isVideoCall = activeCall?.mxCall?.isVideoCall == true
|
||||||
if (isVideoCall) initIfNeeded()
|
if (isVideoCall) initIfNeeded()
|
||||||
activeCallView?.isVisible = !isVideoCall
|
activeCallView?.isVisible = !isVideoCall
|
||||||
pipWrapper?.isVisible = isVideoCall
|
pipWrapper?.isVisible = isVideoCall
|
||||||
activeCallPiP?.isVisible = isVideoCall
|
activeCallPiP?.isVisible = isVideoCall
|
||||||
activeCallPiP?.let {
|
activeCallPiP?.let {
|
||||||
callManager.attachViewRenderers(null, it, null)
|
activeCall?.attachViewRenderers(null, it, null)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
activeCallView?.isVisible = false
|
activeCallView?.isVisible = false
|
||||||
activeCallPiP?.isVisible = false
|
activeCallPiP?.isVisible = false
|
||||||
pipWrapper?.isVisible = false
|
pipWrapper?.isVisible = false
|
||||||
activeCallPiP?.let {
|
activeCallPiP?.let {
|
||||||
callManager.detachRenderers(listOf(it))
|
activeCall?.detachRenderers(listOf(it))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,9 +84,9 @@ class ActiveCallViewHolder {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun unBind(callManager: WebRtcCallManager) {
|
fun unBind() {
|
||||||
activeCallPiP?.let {
|
activeCallPiP?.let {
|
||||||
callManager.detachRenderers(listOf(it))
|
activeCall?.detachRenderers(listOf(it))
|
||||||
}
|
}
|
||||||
if (activeCallPipInitialized) {
|
if (activeCallPipInitialized) {
|
||||||
activeCallPiP?.release()
|
activeCallPiP?.release()
|
||||||
|
|
|
@ -18,6 +18,7 @@ package im.vector.app.features.call
|
||||||
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import im.vector.app.features.call.webrtc.WebRtcCall
|
||||||
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
||||||
import org.matrix.android.sdk.api.session.call.MxCall
|
import org.matrix.android.sdk.api.session.call.MxCall
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
@ -26,27 +27,27 @@ class SharedActiveCallViewModel @Inject constructor(
|
||||||
private val callManager: WebRtcCallManager
|
private val callManager: WebRtcCallManager
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
val activeCall: MutableLiveData<MxCall?> = MutableLiveData()
|
val activeCall: MutableLiveData<WebRtcCall?> = MutableLiveData()
|
||||||
|
|
||||||
val callStateListener = object : MxCall.StateListener {
|
val callStateListener = object : WebRtcCall.Listener {
|
||||||
|
|
||||||
override fun onStateUpdate(call: MxCall) {
|
override fun onStateUpdate(call: MxCall) {
|
||||||
if (activeCall.value?.callId == call.callId) {
|
if (activeCall.value?.callId == call.callId) {
|
||||||
activeCall.postValue(call)
|
activeCall.postValue(callManager.getCallById(call.callId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val listener = object : WebRtcCallManager.CurrentCallListener {
|
private val listener = object : WebRtcCallManager.CurrentCallListener {
|
||||||
override fun onCurrentCallChange(call: MxCall?) {
|
override fun onCurrentCallChange(call: WebRtcCall?) {
|
||||||
activeCall.value?.removeListener(callStateListener)
|
activeCall.value?.mxCall?.removeListener(callStateListener)
|
||||||
activeCall.postValue(call)
|
activeCall.postValue(call)
|
||||||
call?.addListener(callStateListener)
|
call?.addListener(callStateListener)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
activeCall.postValue(callManager.currentCall?.mxCall)
|
activeCall.postValue(callManager.currentCall)
|
||||||
callManager.addCurrentCallListener(listener)
|
callManager.addCurrentCallListener(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,8 @@ import im.vector.app.core.utils.PERMISSIONS_FOR_AUDIO_IP_CALL
|
||||||
import im.vector.app.core.utils.PERMISSIONS_FOR_VIDEO_IP_CALL
|
import im.vector.app.core.utils.PERMISSIONS_FOR_VIDEO_IP_CALL
|
||||||
import im.vector.app.core.utils.allGranted
|
import im.vector.app.core.utils.allGranted
|
||||||
import im.vector.app.core.utils.checkPermissions
|
import im.vector.app.core.utils.checkPermissions
|
||||||
|
import im.vector.app.features.call.utils.EglUtils
|
||||||
|
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
||||||
import im.vector.app.features.home.AvatarRenderer
|
import im.vector.app.features.home.AvatarRenderer
|
||||||
import im.vector.app.features.home.room.detail.RoomDetailActivity
|
import im.vector.app.features.home.room.detail.RoomDetailActivity
|
||||||
import im.vector.app.features.home.room.detail.RoomDetailArgs
|
import im.vector.app.features.home.room.detail.RoomDetailArgs
|
||||||
|
@ -52,8 +54,6 @@ import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
import kotlinx.android.parcel.Parcelize
|
import kotlinx.android.parcel.Parcelize
|
||||||
import kotlinx.android.synthetic.main.activity_call.*
|
import kotlinx.android.synthetic.main.activity_call.*
|
||||||
import org.matrix.android.sdk.api.session.call.CallState
|
import org.matrix.android.sdk.api.session.call.CallState
|
||||||
import im.vector.app.features.call.utils.EglUtils
|
|
||||||
import im.vector.app.features.call.webrtc.WebRtcCallManager
|
|
||||||
import org.matrix.android.sdk.api.session.call.MxCallDetail
|
import org.matrix.android.sdk.api.session.call.MxCallDetail
|
||||||
import org.matrix.android.sdk.api.session.call.MxPeerConnectionState
|
import org.matrix.android.sdk.api.session.call.MxPeerConnectionState
|
||||||
import org.matrix.android.sdk.api.session.call.TurnServerResponse
|
import org.matrix.android.sdk.api.session.call.TurnServerResponse
|
||||||
|
@ -211,7 +211,7 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
callManager.detachRenderers(listOf(pipRenderer, fullscreenRenderer))
|
callManager.getCallById(callArgs.callId)?.detachRenderers(listOf(pipRenderer, fullscreenRenderer))
|
||||||
if (surfaceRenderersAreInitialized) {
|
if (surfaceRenderersAreInitialized) {
|
||||||
pipRenderer.release()
|
pipRenderer.release()
|
||||||
fullscreenRenderer.release()
|
fullscreenRenderer.release()
|
||||||
|
@ -276,7 +276,7 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
|
||||||
callConnectingProgress.isVisible = true
|
callConnectingProgress.isVisible = true
|
||||||
}
|
}
|
||||||
// ensure all attached?
|
// ensure all attached?
|
||||||
callManager.attachViewRenderers(pipRenderer, fullscreenRenderer, null)
|
callManager.getCallById(callArgs.callId)?.attachViewRenderers(pipRenderer, fullscreenRenderer, null)
|
||||||
}
|
}
|
||||||
is CallState.Terminated -> {
|
is CallState.Terminated -> {
|
||||||
finish()
|
finish()
|
||||||
|
@ -326,7 +326,7 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
|
||||||
pipRenderer.setEnableHardwareScaler(true /* enabled */)
|
pipRenderer.setEnableHardwareScaler(true /* enabled */)
|
||||||
fullscreenRenderer.setEnableHardwareScaler(true /* enabled */)
|
fullscreenRenderer.setEnableHardwareScaler(true /* enabled */)
|
||||||
|
|
||||||
callManager.attachViewRenderers(pipRenderer, fullscreenRenderer,
|
callManager.getCallById(callArgs.callId)?.attachViewRenderers(pipRenderer, fullscreenRenderer,
|
||||||
intent.getStringExtra(EXTRA_MODE)?.takeIf { isFirstCreation() })
|
intent.getStringExtra(EXTRA_MODE)?.takeIf { isFirstCreation() })
|
||||||
|
|
||||||
pipRenderer.setOnClickListener {
|
pipRenderer.setOnClickListener {
|
||||||
|
|
|
@ -107,7 +107,7 @@ class VectorCallViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private val currentCallListener = object : WebRtcCallManager.CurrentCallListener {
|
private val currentCallListener = object : WebRtcCallManager.CurrentCallListener {
|
||||||
override fun onCurrentCallChange(call: MxCall?) {
|
override fun onCurrentCallChange(call: WebRtcCall?) {
|
||||||
// we need to check the state
|
// we need to check the state
|
||||||
if (call == null) {
|
if (call == null) {
|
||||||
// we should dismiss, e.g handled by other session?
|
// we should dismiss, e.g handled by other session?
|
||||||
|
@ -155,9 +155,9 @@ class VectorCallViewModel @AssistedInject constructor(
|
||||||
otherUserMatrixItem = item?.let { Success(it) } ?: Uninitialized,
|
otherUserMatrixItem = item?.let { Success(it) } ?: Uninitialized,
|
||||||
soundDevice = currentSoundDevice,
|
soundDevice = currentSoundDevice,
|
||||||
availableSoundDevices = callManager.callAudioManager.getAvailableSoundDevices(),
|
availableSoundDevices = callManager.callAudioManager.getAvailableSoundDevices(),
|
||||||
isFrontCamera = callManager.currentCameraType() == CameraType.FRONT,
|
isFrontCamera = call?.currentCameraType() == CameraType.FRONT,
|
||||||
canSwitchCamera = callManager.canSwitchCamera(),
|
canSwitchCamera = call?.canSwitchCamera() ?: false,
|
||||||
isHD = webRtcCall.mxCall.isVideoCall && callManager.currentCaptureFormat() is CaptureFormat.HD
|
isHD = webRtcCall.mxCall.isVideoCall && webRtcCall.currentCaptureFormat() is CaptureFormat.HD
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,17 +27,22 @@ class CallHeadsUpActionReceiver : BroadcastReceiver() {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val EXTRA_CALL_ACTION_KEY = "EXTRA_CALL_ACTION_KEY"
|
const val EXTRA_CALL_ACTION_KEY = "EXTRA_CALL_ACTION_KEY"
|
||||||
|
const val EXTRA_CALL_ID = "EXTRA_CALL_ID"
|
||||||
const val CALL_ACTION_REJECT = 0
|
const val CALL_ACTION_REJECT = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onReceive(context: Context, intent: Intent?) {
|
override fun onReceive(context: Context, intent: Intent?) {
|
||||||
val peerConnectionManager = (context.applicationContext as? HasVectorInjector)
|
val webRtcCallManager = (context.applicationContext as? HasVectorInjector)
|
||||||
?.injector()
|
?.injector()
|
||||||
?.webRtcPeerConnectionManager()
|
?.webRtcCallManager()
|
||||||
?: return
|
?: return
|
||||||
|
|
||||||
|
|
||||||
when (intent?.getIntExtra(EXTRA_CALL_ACTION_KEY, 0)) {
|
when (intent?.getIntExtra(EXTRA_CALL_ACTION_KEY, 0)) {
|
||||||
CALL_ACTION_REJECT -> onCallRejectClicked(peerConnectionManager)
|
CALL_ACTION_REJECT -> {
|
||||||
|
val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: return
|
||||||
|
onCallRejectClicked(webRtcCallManager, callId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not sure why this should be needed
|
// Not sure why this should be needed
|
||||||
|
@ -48,9 +53,9 @@ class CallHeadsUpActionReceiver : BroadcastReceiver() {
|
||||||
// context.stopService(Intent(context, CallHeadsUpService::class.java))
|
// context.stopService(Intent(context, CallHeadsUpService::class.java))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onCallRejectClicked(callManager: WebRtcCallManager) {
|
private fun onCallRejectClicked(callManager: WebRtcCallManager, callId: String) {
|
||||||
Timber.d("onCallRejectClicked")
|
Timber.d("onCallRejectClicked")
|
||||||
callManager.endCall()
|
callManager.getCallById(callId)?.endCall()
|
||||||
}
|
}
|
||||||
|
|
||||||
// private fun onCallAnswerClicked(context: Context) {
|
// private fun onCallAnswerClicked(context: Context) {
|
||||||
|
|
|
@ -19,6 +19,7 @@ package im.vector.app.features.call.webrtc
|
||||||
import im.vector.app.features.call.CallAudioManager
|
import im.vector.app.features.call.CallAudioManager
|
||||||
import org.matrix.android.sdk.api.session.call.CallState
|
import org.matrix.android.sdk.api.session.call.CallState
|
||||||
import org.matrix.android.sdk.api.session.call.MxPeerConnectionState
|
import org.matrix.android.sdk.api.session.call.MxPeerConnectionState
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.call.CallHangupContent
|
||||||
import org.webrtc.DataChannel
|
import org.webrtc.DataChannel
|
||||||
import org.webrtc.IceCandidate
|
import org.webrtc.IceCandidate
|
||||||
import org.webrtc.MediaStream
|
import org.webrtc.MediaStream
|
||||||
|
@ -132,9 +133,7 @@ class PeerConnectionObserver(private val webRtcCall: WebRtcCall,
|
||||||
* It is, however, possible that the ICE agent did find compatible connections for some components.
|
* It is, however, possible that the ICE agent did find compatible connections for some components.
|
||||||
*/
|
*/
|
||||||
PeerConnection.IceConnectionState.FAILED -> {
|
PeerConnection.IceConnectionState.FAILED -> {
|
||||||
// I should not hangup here..
|
webRtcCall.endCall(true, CallHangupContent.Reason.ICE_FAILED)
|
||||||
// because new candidates could arrive
|
|
||||||
// webRtcCall.mxCall.hangUp()
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* The ICE agent has finished gathering candidates, has checked all pairs against one another, and has found a connection for all components.
|
* The ICE agent has finished gathering candidates, has checked all pairs against one another, and has found a connection for all components.
|
||||||
|
|
|
@ -42,6 +42,7 @@ import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
|
import org.matrix.android.sdk.api.failure.Failure
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.call.CallState
|
import org.matrix.android.sdk.api.session.call.CallState
|
||||||
import org.matrix.android.sdk.api.session.call.MxCall
|
import org.matrix.android.sdk.api.session.call.MxCall
|
||||||
|
@ -88,9 +89,9 @@ class WebRtcCall(val mxCall: MxCall,
|
||||||
private val dispatcher: CoroutineContext,
|
private val dispatcher: CoroutineContext,
|
||||||
private val sessionProvider: Provider<Session?>,
|
private val sessionProvider: Provider<Session?>,
|
||||||
private val peerConnectionFactoryProvider: Provider<PeerConnectionFactory?>,
|
private val peerConnectionFactoryProvider: Provider<PeerConnectionFactory?>,
|
||||||
private val onCallEnded: (WebRtcCall) -> Unit): MxCall.StateListener {
|
private val onCallEnded: (WebRtcCall) -> Unit) : MxCall.StateListener {
|
||||||
|
|
||||||
interface Listener: MxCall.StateListener {
|
interface Listener : MxCall.StateListener {
|
||||||
fun onCaptureStateChanged() {}
|
fun onCaptureStateChanged() {}
|
||||||
fun onCameraChange() {}
|
fun onCameraChange() {}
|
||||||
}
|
}
|
||||||
|
@ -245,7 +246,7 @@ class WebRtcCall(val mxCall: MxCall,
|
||||||
isVideo = mxCall.isVideoCall,
|
isVideo = mxCall.isVideoCall,
|
||||||
roomName = name,
|
roomName = name,
|
||||||
roomId = mxCall.roomId,
|
roomId = mxCall.roomId,
|
||||||
matrixId = session?.myUserId ?:"",
|
matrixId = session?.myUserId ?: "",
|
||||||
callId = mxCall.callId)
|
callId = mxCall.callId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -461,8 +462,13 @@ class WebRtcCall(val mxCall: MxCall,
|
||||||
?: backCamera?.also { cameraInUse = backCamera }
|
?: backCamera?.also { cameraInUse = backCamera }
|
||||||
?: null.also { cameraInUse = null }
|
?: null.also { cameraInUse = null }
|
||||||
|
|
||||||
|
listeners.forEach {
|
||||||
|
tryOrNull { it.onCameraChange() }
|
||||||
|
}
|
||||||
|
|
||||||
if (camera != null) {
|
if (camera != null) {
|
||||||
val videoCapturer = cameraIterator.createCapturer(camera.name, object : CameraEventsHandlerAdapter() {
|
val videoCapturer = cameraIterator.createCapturer(camera.name, object : CameraEventsHandlerAdapter() {
|
||||||
|
|
||||||
override fun onFirstFrameAvailable() {
|
override fun onFirstFrameAvailable() {
|
||||||
super.onFirstFrameAvailable()
|
super.onFirstFrameAvailable()
|
||||||
videoCapturerIsInError = false
|
videoCapturerIsInError = false
|
||||||
|
@ -470,12 +476,25 @@ class WebRtcCall(val mxCall: MxCall,
|
||||||
|
|
||||||
override fun onCameraClosed() {
|
override fun onCameraClosed() {
|
||||||
super.onCameraClosed()
|
super.onCameraClosed()
|
||||||
|
Timber.v("onCameraClosed")
|
||||||
// This could happen if you open the camera app in chat
|
// This could happen if you open the camera app in chat
|
||||||
// We then register in order to restart capture as soon as the camera is available again
|
// We then register in order to restart capture as soon as the camera is available again
|
||||||
videoCapturerIsInError = true
|
videoCapturerIsInError = true
|
||||||
val cameraManager = context.getSystemService<CameraManager>()
|
val cameraManager = context.getSystemService<CameraManager>()
|
||||||
cameraAvailabilityCallback = object : CameraManager.AvailabilityCallback() {
|
cameraAvailabilityCallback = object : CameraManager.AvailabilityCallback() {
|
||||||
|
|
||||||
|
override fun onCameraUnavailable(cameraId: String) {
|
||||||
|
super.onCameraUnavailable(cameraId)
|
||||||
|
Timber.v("On camera unavailable: $cameraId")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCameraAccessPrioritiesChanged() {
|
||||||
|
super.onCameraAccessPrioritiesChanged()
|
||||||
|
Timber.v("onCameraAccessPrioritiesChanged")
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCameraAvailable(cameraId: String) {
|
override fun onCameraAvailable(cameraId: String) {
|
||||||
|
Timber.v("On camera available: $cameraId")
|
||||||
if (cameraId == camera.name) {
|
if (cameraId == camera.name) {
|
||||||
videoCapturer?.startCapture(currentCaptureFormat.width, currentCaptureFormat.height, currentCaptureFormat.fps)
|
videoCapturer?.startCapture(currentCaptureFormat.width, currentCaptureFormat.height, currentCaptureFormat.fps)
|
||||||
cameraManager?.unregisterAvailabilityCallback(this)
|
cameraManager?.unregisterAvailabilityCallback(this)
|
||||||
|
@ -505,12 +524,10 @@ class WebRtcCall(val mxCall: MxCall,
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setCaptureFormat(format: CaptureFormat) {
|
fun setCaptureFormat(format: CaptureFormat) {
|
||||||
GlobalScope.launch(dispatcher) {
|
|
||||||
Timber.v("## VOIP setCaptureFormat $format")
|
Timber.v("## VOIP setCaptureFormat $format")
|
||||||
videoCapturer?.changeCaptureFormat(format.width, format.height, format.fps)
|
videoCapturer?.changeCaptureFormat(format.width, format.height, format.fps)
|
||||||
currentCaptureFormat = format
|
currentCaptureFormat = format
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateMuteStatus() {
|
private fun updateMuteStatus() {
|
||||||
val micShouldBeMuted = micMuted || remoteOnHold
|
val micShouldBeMuted = micMuted || remoteOnHold
|
||||||
|
@ -565,31 +582,41 @@ class WebRtcCall(val mxCall: MxCall,
|
||||||
}
|
}
|
||||||
|
|
||||||
fun canSwitchCamera(): Boolean {
|
fun canSwitchCamera(): Boolean {
|
||||||
return availableCamera.size > 0
|
return availableCamera.size > 1
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getOppositeCameraIfAny(): CameraProxy? {
|
||||||
|
val currentCamera = cameraInUse ?: return null
|
||||||
|
return if (currentCamera.type == CameraType.FRONT) {
|
||||||
|
availableCamera.firstOrNull { it.type == CameraType.BACK }
|
||||||
|
} else {
|
||||||
|
availableCamera.firstOrNull { it.type == CameraType.FRONT }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun switchCamera() {
|
fun switchCamera() {
|
||||||
Timber.v("## VOIP switchCamera")
|
Timber.v("## VOIP switchCamera")
|
||||||
if (!canSwitchCamera()) return
|
|
||||||
if (mxCall.state is CallState.Connected && mxCall.isVideoCall) {
|
if (mxCall.state is CallState.Connected && mxCall.isVideoCall) {
|
||||||
videoCapturer?.switchCamera(object : CameraVideoCapturer.CameraSwitchHandler {
|
val oppositeCamera = getOppositeCameraIfAny() ?: return
|
||||||
|
videoCapturer?.switchCamera(
|
||||||
|
object : CameraVideoCapturer.CameraSwitchHandler {
|
||||||
// Invoked on success. |isFrontCamera| is true if the new camera is front facing.
|
// Invoked on success. |isFrontCamera| is true if the new camera is front facing.
|
||||||
override fun onCameraSwitchDone(isFrontCamera: Boolean) {
|
override fun onCameraSwitchDone(isFrontCamera: Boolean) {
|
||||||
Timber.v("## VOIP onCameraSwitchDone isFront $isFrontCamera")
|
Timber.v("## VOIP onCameraSwitchDone isFront $isFrontCamera")
|
||||||
cameraInUse = availableCamera.first { if (isFrontCamera) it.type == CameraType.FRONT else it.type == CameraType.BACK }
|
cameraInUse = oppositeCamera
|
||||||
localSurfaceRenderers.forEach {
|
localSurfaceRenderers.forEach {
|
||||||
it.get()?.setMirror(isFrontCamera)
|
it.get()?.setMirror(isFrontCamera)
|
||||||
}
|
}
|
||||||
listeners.forEach {
|
listeners.forEach {
|
||||||
tryOrNull { it.onCameraChange() }
|
tryOrNull { it.onCameraChange() }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCameraSwitchError(errorDescription: String?) {
|
override fun onCameraSwitchError(errorDescription: String?) {
|
||||||
Timber.v("## VOIP onCameraSwitchError isFront $errorDescription")
|
Timber.v("## VOIP onCameraSwitchError isFront $errorDescription")
|
||||||
}
|
}
|
||||||
})
|
}, oppositeCamera.name
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -665,6 +692,9 @@ class WebRtcCall(val mxCall: MxCall,
|
||||||
}
|
}
|
||||||
|
|
||||||
fun endCall(originatedByMe: Boolean = true, reason: CallHangupContent.Reason? = null) {
|
fun endCall(originatedByMe: Boolean = true, reason: CallHangupContent.Reason? = null) {
|
||||||
|
if(mxCall.state == CallState.Terminated){
|
||||||
|
return
|
||||||
|
}
|
||||||
mxCall.state = CallState.Terminated
|
mxCall.state = CallState.Terminated
|
||||||
//Close tracks ASAP
|
//Close tracks ASAP
|
||||||
localVideoTrack?.setEnabled(false)
|
localVideoTrack?.setEnabled(false)
|
||||||
|
@ -704,6 +734,7 @@ class WebRtcCall(val mxCall: MxCall,
|
||||||
try {
|
try {
|
||||||
peerConnection?.awaitSetRemoteDescription(sdp)
|
peerConnection?.awaitSetRemoteDescription(sdp)
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
|
endCall(true, CallHangupContent.Reason.UNKWOWN_ERROR)
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
if (mxCall.opponentPartyId?.hasValue().orFalse()) {
|
if (mxCall.opponentPartyId?.hasValue().orFalse()) {
|
||||||
|
|
|
@ -31,7 +31,6 @@ import im.vector.app.features.call.VectorCallActivity
|
||||||
import im.vector.app.features.call.utils.EglUtils
|
import im.vector.app.features.call.utils.EglUtils
|
||||||
import im.vector.app.push.fcm.FcmHelper
|
import im.vector.app.push.fcm.FcmHelper
|
||||||
import kotlinx.coroutines.asCoroutineDispatcher
|
import kotlinx.coroutines.asCoroutineDispatcher
|
||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
|
||||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.call.CallListener
|
import org.matrix.android.sdk.api.session.call.CallListener
|
||||||
|
@ -67,7 +66,7 @@ class WebRtcCallManager @Inject constructor(
|
||||||
get() = activeSessionDataSource.currentValue?.orNull()
|
get() = activeSessionDataSource.currentValue?.orNull()
|
||||||
|
|
||||||
interface CurrentCallListener {
|
interface CurrentCallListener {
|
||||||
fun onCurrentCallChange(call: MxCall?)
|
fun onCurrentCallChange(call: WebRtcCall?)
|
||||||
fun onCaptureStateChanged() {}
|
fun onCaptureStateChanged() {}
|
||||||
fun onAudioDevicesChange() {}
|
fun onAudioDevicesChange() {}
|
||||||
fun onCameraChange() {}
|
fun onCameraChange() {}
|
||||||
|
@ -118,31 +117,32 @@ class WebRtcCallManager @Inject constructor(
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
currentCallsListeners.forEach {
|
currentCallsListeners.forEach {
|
||||||
tryOrNull { it.onCurrentCallChange(value?.mxCall) }
|
tryOrNull { it.onCurrentCallChange(value) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val callsByCallId = HashMap<String, WebRtcCall>()
|
private val callsByCallId = HashMap<String, WebRtcCall>()
|
||||||
|
private val callsByRoomId = HashMap<String, ArrayList<WebRtcCall>>()
|
||||||
|
|
||||||
fun getCallById(callId: String): WebRtcCall? {
|
fun getCallById(callId: String): WebRtcCall? {
|
||||||
return callsByCallId[callId]
|
return callsByCallId[callId]
|
||||||
}
|
}
|
||||||
|
|
||||||
fun headSetButtonTapped() {
|
fun getCallsByRoomId(roomId: String): List<WebRtcCall> {
|
||||||
Timber.v("## VOIP headSetButtonTapped")
|
return callsByRoomId[roomId] ?: emptyList()
|
||||||
val call = currentCall?.mxCall ?: return
|
|
||||||
if (call.state is CallState.LocalRinging) {
|
|
||||||
// accept call
|
|
||||||
acceptIncomingCall()
|
|
||||||
}
|
|
||||||
if (call.state is CallState.Connected) {
|
|
||||||
// end call?
|
|
||||||
endCall()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun attachViewRenderers(localViewRenderer: SurfaceViewRenderer?, remoteViewRenderer: SurfaceViewRenderer, mode: String?) {
|
fun headSetButtonTapped() {
|
||||||
currentCall?.attachViewRenderers(localViewRenderer, remoteViewRenderer, mode)
|
Timber.v("## VOIP headSetButtonTapped")
|
||||||
|
val call = currentCall ?: return
|
||||||
|
if (call.mxCall.state is CallState.LocalRinging) {
|
||||||
|
// accept call
|
||||||
|
call.acceptIncomingCall()
|
||||||
|
}
|
||||||
|
if (call.mxCall.state is CallState.Connected) {
|
||||||
|
// end call?
|
||||||
|
call.endCall()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createPeerConnectionFactoryIfNeeded() {
|
private fun createPeerConnectionFactoryIfNeeded() {
|
||||||
|
@ -174,20 +174,13 @@ class WebRtcCallManager @Inject constructor(
|
||||||
.createPeerConnectionFactory()
|
.createPeerConnectionFactory()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun acceptIncomingCall() {
|
|
||||||
currentCall?.acceptIncomingCall()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun detachRenderers(renderers: List<SurfaceViewRenderer>?) {
|
|
||||||
currentCall?.detachRenderers(renderers)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun onCallEnded(call: WebRtcCall) {
|
private fun onCallEnded(call: WebRtcCall) {
|
||||||
Timber.v("## VOIP WebRtcPeerConnectionManager onCall ended: ${call.mxCall.callId}")
|
Timber.v("## VOIP WebRtcPeerConnectionManager onCall ended: ${call.mxCall.callId}")
|
||||||
CallService.onNoActiveCall(context)
|
CallService.onNoActiveCall(context)
|
||||||
callAudioManager.stop()
|
callAudioManager.stop()
|
||||||
currentCall = null
|
currentCall = null
|
||||||
callsByCallId.remove(call.mxCall.callId)
|
callsByCallId.remove(call.mxCall.callId)
|
||||||
|
callsByRoomId[call.mxCall.roomId]?.remove(call)
|
||||||
// This must be done in this thread
|
// This must be done in this thread
|
||||||
executor.execute {
|
executor.execute {
|
||||||
if (currentCall == null) {
|
if (currentCall == null) {
|
||||||
|
@ -231,9 +224,48 @@ class WebRtcCallManager @Inject constructor(
|
||||||
currentCall?.onCallIceCandidateReceived(iceCandidatesContent)
|
currentCall?.onCallIceCandidateReceived(iceCandidatesContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun createWebRtcCall(mxCall: MxCall): WebRtcCall {
|
||||||
|
val webRtcCall = WebRtcCall(
|
||||||
|
mxCall = mxCall,
|
||||||
|
callAudioManager = callAudioManager,
|
||||||
|
rootEglBase = rootEglBase,
|
||||||
|
context = context,
|
||||||
|
dispatcher = dispatcher,
|
||||||
|
peerConnectionFactoryProvider = {
|
||||||
|
createPeerConnectionFactoryIfNeeded()
|
||||||
|
peerConnectionFactory
|
||||||
|
},
|
||||||
|
sessionProvider = { currentSession },
|
||||||
|
onCallEnded = this::onCallEnded
|
||||||
|
)
|
||||||
|
currentCall = webRtcCall
|
||||||
|
callsByCallId[mxCall.callId] = webRtcCall
|
||||||
|
callsByRoomId.getOrPut(mxCall.roomId, { ArrayList() }).add(webRtcCall)
|
||||||
|
return webRtcCall
|
||||||
|
}
|
||||||
|
|
||||||
|
fun acceptIncomingCall() {
|
||||||
|
currentCall?.acceptIncomingCall()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun endCall(originatedByMe: Boolean = true) {
|
||||||
|
currentCall?.endCall(originatedByMe)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onWiredDeviceEvent(event: WiredHeadsetStateReceiver.HeadsetPlugEvent) {
|
||||||
|
Timber.v("## VOIP onWiredDeviceEvent $event")
|
||||||
|
currentCall ?: return
|
||||||
|
// sometimes we received un-wanted unplugged...
|
||||||
|
callAudioManager.wiredStateChange(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onWirelessDeviceEvent(event: BluetoothHeadsetReceiver.BTHeadsetPlugEvent) {
|
||||||
|
Timber.v("## VOIP onWirelessDeviceEvent $event")
|
||||||
|
callAudioManager.bluetoothStateChange(event.plugged)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCallInviteReceived(mxCall: MxCall, callInviteContent: CallInviteContent) {
|
override fun onCallInviteReceived(mxCall: MxCall, callInviteContent: CallInviteContent) {
|
||||||
Timber.v("## VOIP onCallInviteReceived callId ${mxCall.callId}")
|
Timber.v("## VOIP onCallInviteReceived callId ${mxCall.callId}")
|
||||||
// to simplify we only treat one call at a time, and ignore others
|
|
||||||
if (currentCall != null) {
|
if (currentCall != null) {
|
||||||
Timber.w("## VOIP receiving incoming call while already in call?")
|
Timber.w("## VOIP receiving incoming call while already in call?")
|
||||||
// Just ignore, maybe we could answer from other session?
|
// Just ignore, maybe we could answer from other session?
|
||||||
|
@ -267,72 +299,9 @@ class WebRtcCallManager @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createWebRtcCall(mxCall: MxCall): WebRtcCall {
|
|
||||||
val webRtcCall = WebRtcCall(
|
|
||||||
mxCall = mxCall,
|
|
||||||
callAudioManager = callAudioManager,
|
|
||||||
rootEglBase = rootEglBase,
|
|
||||||
context = context,
|
|
||||||
dispatcher = dispatcher,
|
|
||||||
peerConnectionFactoryProvider = {
|
|
||||||
createPeerConnectionFactoryIfNeeded()
|
|
||||||
peerConnectionFactory
|
|
||||||
},
|
|
||||||
sessionProvider = { currentSession },
|
|
||||||
onCallEnded = this::onCallEnded
|
|
||||||
)
|
|
||||||
currentCall = webRtcCall
|
|
||||||
callsByCallId[mxCall.callId] = webRtcCall
|
|
||||||
return webRtcCall
|
|
||||||
}
|
|
||||||
|
|
||||||
fun muteCall(muted: Boolean) {
|
|
||||||
currentCall?.muteCall(muted)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun enableVideo(enabled: Boolean) {
|
|
||||||
currentCall?.enableVideo(enabled)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun switchCamera() {
|
|
||||||
currentCall?.switchCamera()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun canSwitchCamera(): Boolean {
|
|
||||||
return currentCall?.canSwitchCamera() ?: false
|
|
||||||
}
|
|
||||||
|
|
||||||
fun currentCameraType(): CameraType? {
|
|
||||||
return currentCall?.currentCameraType()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setCaptureFormat(format: CaptureFormat) {
|
|
||||||
currentCall?.setCaptureFormat(format)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun currentCaptureFormat(): CaptureFormat {
|
|
||||||
return currentCall?.currentCaptureFormat() ?: CaptureFormat.HD
|
|
||||||
}
|
|
||||||
|
|
||||||
fun endCall(originatedByMe: Boolean = true) {
|
|
||||||
currentCall?.endCall(originatedByMe)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onWiredDeviceEvent(event: WiredHeadsetStateReceiver.HeadsetPlugEvent) {
|
|
||||||
Timber.v("## VOIP onWiredDeviceEvent $event")
|
|
||||||
currentCall ?: return
|
|
||||||
// sometimes we received un-wanted unplugged...
|
|
||||||
callAudioManager.wiredStateChange(event)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onWirelessDeviceEvent(event: BluetoothHeadsetReceiver.BTHeadsetPlugEvent) {
|
|
||||||
Timber.v("## VOIP onWirelessDeviceEvent $event")
|
|
||||||
callAudioManager.bluetoothStateChange(event.plugged)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCallAnswerReceived(callAnswerContent: CallAnswerContent) {
|
override fun onCallAnswerReceived(callAnswerContent: CallAnswerContent) {
|
||||||
val call = currentCall ?: return
|
val call = callsByCallId[callAnswerContent.callId]
|
||||||
if (call.mxCall.callId != callAnswerContent.callId) return Unit.also {
|
?: return Unit.also {
|
||||||
Timber.w("onCallAnswerReceived for non active call? ${callAnswerContent.callId}")
|
Timber.w("onCallAnswerReceived for non active call? ${callAnswerContent.callId}")
|
||||||
}
|
}
|
||||||
val mxCall = call.mxCall
|
val mxCall = call.mxCall
|
||||||
|
@ -351,39 +320,37 @@ class WebRtcCallManager @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCallHangupReceived(callHangupContent: CallHangupContent) {
|
override fun onCallHangupReceived(callHangupContent: CallHangupContent) {
|
||||||
val call = currentCall ?: return
|
val call = callsByCallId[callHangupContent.callId]
|
||||||
// Remote echos are filtered, so it's only remote hangups that i will get here
|
?: return Unit.also {
|
||||||
if (call.mxCall.callId != callHangupContent.callId) return Unit.also {
|
|
||||||
Timber.w("onCallHangupReceived for non active call? ${callHangupContent.callId}")
|
Timber.w("onCallHangupReceived for non active call? ${callHangupContent.callId}")
|
||||||
}
|
}
|
||||||
endCall(false)
|
call.endCall(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCallRejectReceived(callRejectContent: CallRejectContent) {
|
override fun onCallRejectReceived(callRejectContent: CallRejectContent) {
|
||||||
val call = currentCall ?: return
|
val call = callsByCallId[callRejectContent.callId]
|
||||||
// Remote echos are filtered, so it's only remote hangups that i will get here
|
?: return Unit.also {
|
||||||
if (call.mxCall.callId != callRejectContent.callId) return Unit.also {
|
Timber.w("onCallRejectReceived for non active call? ${callRejectContent.callId}")
|
||||||
Timber.w("onCallRejected for non active call? ${callRejectContent.callId}")
|
|
||||||
}
|
}
|
||||||
endCall(false)
|
call.endCall(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCallSelectAnswerReceived(callSelectAnswerContent: CallSelectAnswerContent) {
|
override fun onCallSelectAnswerReceived(callSelectAnswerContent: CallSelectAnswerContent) {
|
||||||
val call = currentCall ?: return
|
val call = callsByCallId[callSelectAnswerContent.callId]
|
||||||
if (call.mxCall.callId != callSelectAnswerContent.callId) return Unit.also {
|
?: return Unit.also {
|
||||||
Timber.w("onCallSelectAnswerReceived for non active call? ${callSelectAnswerContent.callId}")
|
Timber.w("onCallSelectAnswerReceived for non active call? ${callSelectAnswerContent.callId}")
|
||||||
}
|
}
|
||||||
val selectedPartyId = callSelectAnswerContent.selectedPartyId
|
val selectedPartyId = callSelectAnswerContent.selectedPartyId
|
||||||
if (selectedPartyId != call.mxCall.ourPartyId) {
|
if (selectedPartyId != call.mxCall.ourPartyId) {
|
||||||
Timber.i("Got select_answer for party ID ${selectedPartyId}: we are party ID ${call.mxCall.ourPartyId}.");
|
Timber.i("Got select_answer for party ID ${selectedPartyId}: we are party ID ${call.mxCall.ourPartyId}.");
|
||||||
// The other party has picked somebody else's answer
|
// The other party has picked somebody else's answer
|
||||||
endCall(false)
|
call.endCall(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCallNegotiateReceived(callNegotiateContent: CallNegotiateContent) {
|
override fun onCallNegotiateReceived(callNegotiateContent: CallNegotiateContent) {
|
||||||
val call = currentCall ?: return
|
val call = callsByCallId[callNegotiateContent.callId]
|
||||||
if (call.mxCall.callId != callNegotiateContent.callId) return Unit.also {
|
?: return Unit.also {
|
||||||
Timber.w("onCallNegotiateReceived for non active call? ${callNegotiateContent.callId}")
|
Timber.w("onCallNegotiateReceived for non active call? ${callNegotiateContent.callId}")
|
||||||
}
|
}
|
||||||
call.onCallNegotiateReceived(callNegotiateContent)
|
call.onCallNegotiateReceived(callNegotiateContent)
|
||||||
|
@ -392,7 +359,10 @@ class WebRtcCallManager @Inject constructor(
|
||||||
override fun onCallManagedByOtherSession(callId: String) {
|
override fun onCallManagedByOtherSession(callId: String) {
|
||||||
Timber.v("## VOIP onCallManagedByOtherSession: $callId")
|
Timber.v("## VOIP onCallManagedByOtherSession: $callId")
|
||||||
currentCall = null
|
currentCall = null
|
||||||
callsByCallId.remove(callId)
|
val webRtcCall = callsByCallId.remove(callId)
|
||||||
|
if (webRtcCall != null) {
|
||||||
|
callsByRoomId[webRtcCall.mxCall.roomId]?.remove(webRtcCall)
|
||||||
|
}
|
||||||
CallService.onNoActiveCall(context)
|
CallService.onNoActiveCall(context)
|
||||||
|
|
||||||
// did we start background sync? so we should stop it
|
// did we start background sync? so we should stop it
|
||||||
|
@ -405,24 +375,4 @@ class WebRtcCallManager @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates whether we are 'on hold' to the remote party (ie. if true,
|
|
||||||
* they cannot hear us). Note that this will return true when we put the
|
|
||||||
* remote on hold too due to the way hold is implemented (since we don't
|
|
||||||
* wish to play hold music when we put a call on hold, we use 'inactive'
|
|
||||||
* rather than 'sendonly')
|
|
||||||
* @returns true if the other party has put us on hold
|
|
||||||
*/
|
|
||||||
fun isLocalOnHold(): Boolean {
|
|
||||||
return currentCall?.isLocalOnHold().orFalse()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isRemoteOnHold(): Boolean {
|
|
||||||
return currentCall?.remoteOnHold.orFalse()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setRemoteOnHold(onHold: Boolean) {
|
|
||||||
currentCall?.updateRemoteOnHold(onHold)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,7 +120,7 @@ class HomeDetailFragment @Inject constructor(
|
||||||
sharedCallActionViewModel
|
sharedCallActionViewModel
|
||||||
.activeCall
|
.activeCall
|
||||||
.observe(viewLifecycleOwner, Observer {
|
.observe(viewLifecycleOwner, Observer {
|
||||||
activeCallViewHolder.updateCall(it, callManager)
|
activeCallViewHolder.updateCall(it)
|
||||||
invalidateOptionsMenu()
|
invalidateOptionsMenu()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -331,10 +331,10 @@ class HomeDetailFragment @Inject constructor(
|
||||||
VectorCallActivity.newIntent(
|
VectorCallActivity.newIntent(
|
||||||
context = requireContext(),
|
context = requireContext(),
|
||||||
callId = call.callId,
|
callId = call.callId,
|
||||||
roomId = call.roomId,
|
roomId = call.mxCall.roomId,
|
||||||
otherUserId = call.opponentUserId,
|
otherUserId = call.mxCall.opponentUserId,
|
||||||
isIncomingCall = !call.isOutgoing,
|
isIncomingCall = !call.mxCall.isOutgoing,
|
||||||
isVideoCall = call.isVideoCall,
|
isVideoCall = call.mxCall.isVideoCall,
|
||||||
mode = null
|
mode = null
|
||||||
).let {
|
).let {
|
||||||
startActivity(it)
|
startActivity(it)
|
||||||
|
|
|
@ -315,7 +315,7 @@ class RoomDetailFragment @Inject constructor(
|
||||||
sharedCallActionViewModel
|
sharedCallActionViewModel
|
||||||
.activeCall
|
.activeCall
|
||||||
.observe(viewLifecycleOwner, Observer {
|
.observe(viewLifecycleOwner, Observer {
|
||||||
activeCallViewHolder.updateCall(it, callManager)
|
activeCallViewHolder.updateCall(it)
|
||||||
invalidateOptionsMenu()
|
invalidateOptionsMenu()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -514,7 +514,7 @@ class RoomDetailFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
activeCallViewHolder.unBind(callManager)
|
activeCallViewHolder.unBind()
|
||||||
roomDetailViewModel.handle(RoomDetailAction.ExitTrackingUnreadMessagesState)
|
roomDetailViewModel.handle(RoomDetailAction.ExitTrackingUnreadMessagesState)
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
@ -712,7 +712,7 @@ class RoomDetailFragment @Inject constructor(
|
||||||
val activeCall = sharedCallActionViewModel.activeCall.value
|
val activeCall = sharedCallActionViewModel.activeCall.value
|
||||||
if (activeCall != null) {
|
if (activeCall != null) {
|
||||||
// resume existing if same room, if not prompt to kill and then restart new call?
|
// resume existing if same room, if not prompt to kill and then restart new call?
|
||||||
if (activeCall.roomId == roomDetailArgs.roomId) {
|
if (activeCall.mxCall.roomId == roomDetailArgs.roomId) {
|
||||||
onTapToReturnToCall()
|
onTapToReturnToCall()
|
||||||
}
|
}
|
||||||
// else {
|
// else {
|
||||||
|
@ -1961,10 +1961,10 @@ class RoomDetailFragment @Inject constructor(
|
||||||
VectorCallActivity.newIntent(
|
VectorCallActivity.newIntent(
|
||||||
context = requireContext(),
|
context = requireContext(),
|
||||||
callId = call.callId,
|
callId = call.callId,
|
||||||
roomId = call.roomId,
|
roomId = call.mxCall.roomId,
|
||||||
otherUserId = call.opponentUserId,
|
otherUserId = call.mxCall.opponentUserId,
|
||||||
isIncomingCall = !call.isOutgoing,
|
isIncomingCall = !call.mxCall.isOutgoing,
|
||||||
isVideoCall = call.isVideoCall,
|
isVideoCall = call.mxCall.isVideoCall,
|
||||||
mode = null
|
mode = null
|
||||||
).let {
|
).let {
|
||||||
startActivity(it)
|
startActivity(it)
|
||||||
|
|
|
@ -565,8 +565,8 @@ class RoomDetailViewModel @AssistedInject constructor(
|
||||||
R.id.clear_all -> state.asyncRoomSummary()?.hasFailedSending == true
|
R.id.clear_all -> state.asyncRoomSummary()?.hasFailedSending == true
|
||||||
R.id.open_matrix_apps -> true
|
R.id.open_matrix_apps -> true
|
||||||
R.id.voice_call,
|
R.id.voice_call,
|
||||||
R.id.video_call -> true // always show for discoverability
|
R.id.video_call -> callManager.getCallsByRoomId(state.roomId).isEmpty()
|
||||||
R.id.hangup_call -> callManager.currentCall != null
|
R.id.hangup_call -> callManager.getCallsByRoomId(state.roomId).isNotEmpty()
|
||||||
R.id.search -> true
|
R.id.search -> true
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|
|
@ -296,7 +296,6 @@ class NotificationUtils @Inject constructor(private val context: Context,
|
||||||
builder.priority = NotificationCompat.PRIORITY_HIGH
|
builder.priority = NotificationCompat.PRIORITY_HIGH
|
||||||
|
|
||||||
//
|
//
|
||||||
val requestId = Random.nextInt(1000)
|
|
||||||
// val pendingIntent = stackBuilder.getPendingIntent(requestId, PendingIntent.FLAG_UPDATE_CURRENT)
|
// val pendingIntent = stackBuilder.getPendingIntent(requestId, PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
|
|
||||||
val contentIntent = VectorCallActivity.newIntent(
|
val contentIntent = VectorCallActivity.newIntent(
|
||||||
|
@ -326,16 +325,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
|
||||||
)
|
)
|
||||||
.getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT)
|
.getPendingIntent(System.currentTimeMillis().toInt(), PendingIntent.FLAG_UPDATE_CURRENT)
|
||||||
|
|
||||||
val rejectCallActionReceiver = Intent(context, CallHeadsUpActionReceiver::class.java).apply {
|
val rejectCallPendingIntent = buildRejectCallPendingIntent(callId)
|
||||||
putExtra(CallHeadsUpActionReceiver.EXTRA_CALL_ACTION_KEY, CallHeadsUpActionReceiver.CALL_ACTION_REJECT)
|
|
||||||
}
|
|
||||||
// val answerCallPendingIntent = PendingIntent.getBroadcast(context, requestId, answerCallActionReceiver, PendingIntent.FLAG_UPDATE_CURRENT)
|
|
||||||
val rejectCallPendingIntent = PendingIntent.getBroadcast(
|
|
||||||
context,
|
|
||||||
requestId + 1,
|
|
||||||
rejectCallActionReceiver,
|
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT
|
|
||||||
)
|
|
||||||
|
|
||||||
builder.addAction(
|
builder.addAction(
|
||||||
NotificationCompat.Action(
|
NotificationCompat.Action(
|
||||||
|
@ -375,8 +365,6 @@ class NotificationUtils @Inject constructor(private val context: Context,
|
||||||
.setLights(accentColor, 500, 500)
|
.setLights(accentColor, 500, 500)
|
||||||
.setOngoing(true)
|
.setOngoing(true)
|
||||||
|
|
||||||
val requestId = Random.nextInt(1000)
|
|
||||||
|
|
||||||
val contentIntent = VectorCallActivity.newIntent(
|
val contentIntent = VectorCallActivity.newIntent(
|
||||||
context = context,
|
context = context,
|
||||||
callId = callId,
|
callId = callId,
|
||||||
|
@ -390,16 +378,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
|
||||||
}
|
}
|
||||||
val contentPendingIntent = PendingIntent.getActivity(context, System.currentTimeMillis().toInt(), contentIntent, 0)
|
val contentPendingIntent = PendingIntent.getActivity(context, System.currentTimeMillis().toInt(), contentIntent, 0)
|
||||||
|
|
||||||
val rejectCallActionReceiver = Intent(context, CallHeadsUpActionReceiver::class.java).apply {
|
val rejectCallPendingIntent = buildRejectCallPendingIntent(callId)
|
||||||
putExtra(CallHeadsUpActionReceiver.EXTRA_CALL_ACTION_KEY, CallHeadsUpActionReceiver.CALL_ACTION_REJECT)
|
|
||||||
}
|
|
||||||
|
|
||||||
val rejectCallPendingIntent = PendingIntent.getBroadcast(
|
|
||||||
context,
|
|
||||||
requestId + 1,
|
|
||||||
rejectCallActionReceiver,
|
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT
|
|
||||||
)
|
|
||||||
|
|
||||||
builder.addAction(
|
builder.addAction(
|
||||||
NotificationCompat.Action(
|
NotificationCompat.Action(
|
||||||
|
@ -446,17 +425,7 @@ class NotificationUtils @Inject constructor(private val context: Context,
|
||||||
builder.setOngoing(true)
|
builder.setOngoing(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
val rejectCallActionReceiver = Intent(context, CallHeadsUpActionReceiver::class.java).apply {
|
val rejectCallPendingIntent = buildRejectCallPendingIntent(callId)
|
||||||
data = Uri.parse("mxcall://end?$callId")
|
|
||||||
putExtra(CallHeadsUpActionReceiver.EXTRA_CALL_ACTION_KEY, CallHeadsUpActionReceiver.CALL_ACTION_REJECT)
|
|
||||||
}
|
|
||||||
|
|
||||||
val rejectCallPendingIntent = PendingIntent.getBroadcast(
|
|
||||||
context,
|
|
||||||
System.currentTimeMillis().toInt(),
|
|
||||||
rejectCallActionReceiver,
|
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT
|
|
||||||
)
|
|
||||||
|
|
||||||
builder.addAction(
|
builder.addAction(
|
||||||
NotificationCompat.Action(
|
NotificationCompat.Action(
|
||||||
|
@ -476,6 +445,19 @@ class NotificationUtils @Inject constructor(private val context: Context,
|
||||||
return builder.build()
|
return builder.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun buildRejectCallPendingIntent(callId: String): PendingIntent {
|
||||||
|
val rejectCallActionReceiver = Intent(context, CallHeadsUpActionReceiver::class.java).apply {
|
||||||
|
putExtra(CallHeadsUpActionReceiver.EXTRA_CALL_ID, callId)
|
||||||
|
putExtra(CallHeadsUpActionReceiver.EXTRA_CALL_ACTION_KEY, CallHeadsUpActionReceiver.CALL_ACTION_REJECT)
|
||||||
|
}
|
||||||
|
return PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
System.currentTimeMillis().toInt(),
|
||||||
|
rejectCallActionReceiver,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build a temporary (because service will be stopped just after) notification for the CallService, when a call is ended
|
* Build a temporary (because service will be stopped just after) notification for the CallService, when a call is ended
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in a new issue