mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-25 19:05:56 +03:00
Missed call notif: make some cleanup and minor changes
This commit is contained in:
parent
88cc7471a8
commit
5dda5a107a
14 changed files with 159 additions and 93 deletions
1
changelog.d/3710.feature
Normal file
1
changelog.d/3710.feature
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Show missed call notification.
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.api.session.call
|
package org.matrix.android.sdk.api.session.call
|
||||||
|
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.call.EndCallReason
|
||||||
|
|
||||||
sealed class CallState {
|
sealed class CallState {
|
||||||
|
|
||||||
/** Idle, setting up objects */
|
/** Idle, setting up objects */
|
||||||
|
@ -42,6 +44,6 @@ sealed class CallState {
|
||||||
* */
|
* */
|
||||||
data class Connected(val iceConnectionState: MxPeerConnectionState) : CallState()
|
data class Connected(val iceConnectionState: MxPeerConnectionState) : CallState()
|
||||||
|
|
||||||
/** Terminated. Incoming/Outgoing call, the call is terminated */
|
/** Ended. Incoming/Outgoing call, the call is terminated */
|
||||||
object Terminated : CallState()
|
data class Ended(val reason: EndCallReason? = null) : CallState()
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ package org.matrix.android.sdk.api.session.call
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.session.room.model.call.CallCandidate
|
import org.matrix.android.sdk.api.session.room.model.call.CallCandidate
|
||||||
import org.matrix.android.sdk.api.session.room.model.call.CallCapabilities
|
import org.matrix.android.sdk.api.session.room.model.call.CallCapabilities
|
||||||
import org.matrix.android.sdk.api.session.room.model.call.CallHangupContent
|
import org.matrix.android.sdk.api.session.room.model.call.EndCallReason
|
||||||
import org.matrix.android.sdk.api.session.room.model.call.SdpType
|
import org.matrix.android.sdk.api.session.room.model.call.SdpType
|
||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ interface MxCall : MxCallDetail {
|
||||||
/**
|
/**
|
||||||
* End the call
|
* End the call
|
||||||
*/
|
*/
|
||||||
fun hangUp(reason: CallHangupContent.Reason? = null)
|
fun hangUp(reason: EndCallReason? = null)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start a call
|
* Start a call
|
||||||
|
|
|
@ -43,29 +43,5 @@ data class CallHangupContent(
|
||||||
* or `invite_timeout` for when the other party did not answer in time.
|
* or `invite_timeout` for when the other party did not answer in time.
|
||||||
* One of: ["ice_failed", "invite_timeout"]
|
* One of: ["ice_failed", "invite_timeout"]
|
||||||
*/
|
*/
|
||||||
@Json(name = "reason") val reason: Reason? = null
|
@Json(name = "reason") val reason: EndCallReason? = null
|
||||||
) : CallSignalingContent {
|
) : CallSignalingContent
|
||||||
@JsonClass(generateAdapter = false)
|
|
||||||
enum class Reason {
|
|
||||||
@Json(name = "ice_failed")
|
|
||||||
ICE_FAILED,
|
|
||||||
|
|
||||||
@Json(name = "ice_timeout")
|
|
||||||
ICE_TIMEOUT,
|
|
||||||
|
|
||||||
@Json(name = "user_hangup")
|
|
||||||
USER_HANGUP,
|
|
||||||
|
|
||||||
@Json(name = "replaced")
|
|
||||||
REPLACED,
|
|
||||||
|
|
||||||
@Json(name = "user_media_failed")
|
|
||||||
USER_MEDIA_FAILED,
|
|
||||||
|
|
||||||
@Json(name = "invite_timeout")
|
|
||||||
INVITE_TIMEOUT,
|
|
||||||
|
|
||||||
@Json(name = "unknown_error")
|
|
||||||
UNKWOWN_ERROR
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -36,5 +36,10 @@ data class CallRejectContent(
|
||||||
/**
|
/**
|
||||||
* Required. The version of the VoIP specification this message adheres to.
|
* Required. The version of the VoIP specification this message adheres to.
|
||||||
*/
|
*/
|
||||||
@Json(name = "version") override val version: String?
|
@Json(name = "version") override val version: String?,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional error reason for the reject.
|
||||||
|
*/
|
||||||
|
@Json(name = "reason") val reason: EndCallReason? = null
|
||||||
) : CallSignalingContent
|
) : CallSignalingContent
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.matrix.android.sdk.api.session.room.model.call
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = false)
|
||||||
|
enum class EndCallReason {
|
||||||
|
@Json(name = "ice_failed")
|
||||||
|
ICE_FAILED,
|
||||||
|
|
||||||
|
@Json(name = "ice_timeout")
|
||||||
|
ICE_TIMEOUT,
|
||||||
|
|
||||||
|
@Json(name = "user_hangup")
|
||||||
|
USER_HANGUP,
|
||||||
|
|
||||||
|
@Json(name = "replaced")
|
||||||
|
REPLACED,
|
||||||
|
|
||||||
|
@Json(name = "user_media_failed")
|
||||||
|
USER_MEDIA_FAILED,
|
||||||
|
|
||||||
|
@Json(name = "invite_timeout")
|
||||||
|
INVITE_TIMEOUT,
|
||||||
|
|
||||||
|
@Json(name = "unknown_error")
|
||||||
|
UNKWOWN_ERROR,
|
||||||
|
|
||||||
|
@Json(name = "user_busy")
|
||||||
|
USER_BUSY,
|
||||||
|
|
||||||
|
@Json(name = "answered_elsewhere")
|
||||||
|
ANSWERED_ELSEWHERE
|
||||||
|
}
|
|
@ -166,7 +166,7 @@ internal class CallSignalingHandler @Inject constructor(private val activeCallHa
|
||||||
Timber.v("Ignoring hangup from party ID ${content.partyId} we have chosen party ID ${call.opponentPartyId}")
|
Timber.v("Ignoring hangup from party ID ${content.partyId} we have chosen party ID ${call.opponentPartyId}")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (call.state != CallState.Terminated) {
|
if (call.state !is CallState.Ended) {
|
||||||
activeCallHandler.removeCall(content.callId)
|
activeCallHandler.removeCall(content.callId)
|
||||||
callListenersDispatcher.onCallHangupReceived(content)
|
callListenersDispatcher.onCallHangupReceived(content)
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ import org.matrix.android.sdk.api.session.room.model.call.CallRejectContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.call.CallReplacesContent
|
import org.matrix.android.sdk.api.session.room.model.call.CallReplacesContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.call.CallSelectAnswerContent
|
import org.matrix.android.sdk.api.session.room.model.call.CallSelectAnswerContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.call.CallSignalingContent
|
import org.matrix.android.sdk.api.session.room.model.call.CallSignalingContent
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.call.EndCallReason
|
||||||
import org.matrix.android.sdk.api.session.room.model.call.SdpType
|
import org.matrix.android.sdk.api.session.room.model.call.SdpType
|
||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
import org.matrix.android.sdk.internal.session.call.DefaultCallSignalingService
|
import org.matrix.android.sdk.internal.session.call.DefaultCallSignalingService
|
||||||
|
@ -153,20 +154,20 @@ internal class MxCallImpl(
|
||||||
)
|
)
|
||||||
.let { createEventAndLocalEcho(type = EventType.CALL_REJECT, roomId = roomId, content = it.toContent()) }
|
.let { createEventAndLocalEcho(type = EventType.CALL_REJECT, roomId = roomId, content = it.toContent()) }
|
||||||
.also { eventSenderProcessor.postEvent(it) }
|
.also { eventSenderProcessor.postEvent(it) }
|
||||||
state = CallState.Terminated
|
state = CallState.Ended(reason = EndCallReason.USER_HANGUP)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hangUp(reason: CallHangupContent.Reason?) {
|
override fun hangUp(reason: EndCallReason?) {
|
||||||
Timber.v("## VOIP hangup $callId")
|
Timber.v("## VOIP hangup $callId")
|
||||||
CallHangupContent(
|
CallHangupContent(
|
||||||
callId = callId,
|
callId = callId,
|
||||||
partyId = ourPartyId,
|
partyId = ourPartyId,
|
||||||
reason = reason ?: CallHangupContent.Reason.USER_HANGUP,
|
reason = reason ?: EndCallReason.USER_HANGUP,
|
||||||
version = MxCall.VOIP_PROTO_VERSION.toString()
|
version = MxCall.VOIP_PROTO_VERSION.toString()
|
||||||
)
|
)
|
||||||
.let { createEventAndLocalEcho(type = EventType.CALL_HANGUP, roomId = roomId, content = it.toContent()) }
|
.let { createEventAndLocalEcho(type = EventType.CALL_HANGUP, roomId = roomId, content = it.toContent()) }
|
||||||
.also { eventSenderProcessor.postEvent(it) }
|
.also { eventSenderProcessor.postEvent(it) }
|
||||||
state = CallState.Terminated
|
state = CallState.Ended(reason)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(sdpString: String) {
|
override fun accept(sdpString: String) {
|
||||||
|
|
|
@ -37,7 +37,7 @@ import im.vector.app.features.home.AvatarRenderer
|
||||||
import im.vector.app.features.notifications.NotificationUtils
|
import im.vector.app.features.notifications.NotificationUtils
|
||||||
import im.vector.app.features.popup.IncomingCallAlert
|
import im.vector.app.features.popup.IncomingCallAlert
|
||||||
import im.vector.app.features.popup.PopupAlertManager
|
import im.vector.app.features.popup.PopupAlertManager
|
||||||
import org.matrix.android.sdk.api.session.content.ContentUrlResolver
|
import org.matrix.android.sdk.api.session.room.model.call.EndCallReason
|
||||||
import org.matrix.android.sdk.api.util.MatrixItem
|
import org.matrix.android.sdk.api.util.MatrixItem
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
|
@ -192,7 +192,8 @@ class CallService : VectorService() {
|
||||||
|
|
||||||
private fun handleCallTerminated(intent: Intent) {
|
private fun handleCallTerminated(intent: Intent) {
|
||||||
val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: ""
|
val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: ""
|
||||||
val isRejected = intent.getBooleanExtra(EXTRA_IS_REJECTED, false)
|
val endCallReason = intent.getSerializableExtra(EXTRA_END_CALL_REASON) as EndCallReason
|
||||||
|
val rejected = intent.getBooleanExtra(EXTRA_END_CALL_REJECTED, false)
|
||||||
alertManager.cancelAlert(callId)
|
alertManager.cancelAlert(callId)
|
||||||
val terminatedCall = knownCalls.firstOrNull { it.callId == callId }
|
val terminatedCall = knownCalls.firstOrNull { it.callId == callId }
|
||||||
if (terminatedCall == null) {
|
if (terminatedCall == null) {
|
||||||
|
@ -206,13 +207,13 @@ class CallService : VectorService() {
|
||||||
myStopSelf()
|
myStopSelf()
|
||||||
}
|
}
|
||||||
val wasConnected = connectedCallIds.remove(callId)
|
val wasConnected = connectedCallIds.remove(callId)
|
||||||
if (wasConnected || terminatedCall.isOutgoing || isRejected) {
|
if (!wasConnected && !terminatedCall.isOutgoing && !rejected && endCallReason != EndCallReason.ANSWERED_ELSEWHERE) {
|
||||||
val notification = notificationUtils.buildCallEndedNotification(terminatedCall.isVideoCall)
|
|
||||||
notificationManager.notify(callId.hashCode(), notification)
|
|
||||||
} else {
|
|
||||||
val notification = notificationUtils.buildCallMissedNotification(terminatedCall)
|
val notification = notificationUtils.buildCallMissedNotification(terminatedCall)
|
||||||
notificationManager.cancel(callId.hashCode())
|
notificationManager.cancel(callId.hashCode())
|
||||||
notificationManager.notify(MISSED_CALL_TAG, terminatedCall.nativeRoomId.hashCode(), notification)
|
notificationManager.notify(MISSED_CALL_TAG, terminatedCall.nativeRoomId.hashCode(), notification)
|
||||||
|
} else {
|
||||||
|
val notification = notificationUtils.buildCallEndedNotification(terminatedCall.isVideoCall)
|
||||||
|
notificationManager.notify(callId.hashCode(), notification)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,7 +288,7 @@ class CallService : VectorService() {
|
||||||
connections[callConnection.callId] = callConnection
|
connections[callConnection.callId] = callConnection
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun WebRtcCall.toCallInformation(): CallInformation{
|
private fun WebRtcCall.toCallInformation(): CallInformation {
|
||||||
return CallInformation(
|
return CallInformation(
|
||||||
callId = this.callId,
|
callId = this.callId,
|
||||||
nativeRoomId = this.nativeRoomId,
|
nativeRoomId = this.nativeRoomId,
|
||||||
|
@ -306,7 +307,7 @@ class CallService : VectorService() {
|
||||||
val opponentUserId: String,
|
val opponentUserId: String,
|
||||||
val matrixItem: MatrixItem?,
|
val matrixItem: MatrixItem?,
|
||||||
val isVideoCall: Boolean,
|
val isVideoCall: Boolean,
|
||||||
val isOutgoing: Boolean,
|
val isOutgoing: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -324,7 +325,8 @@ class CallService : VectorService() {
|
||||||
|
|
||||||
private const val EXTRA_CALL_ID = "EXTRA_CALL_ID"
|
private const val EXTRA_CALL_ID = "EXTRA_CALL_ID"
|
||||||
private const val EXTRA_IS_IN_BG = "EXTRA_IS_IN_BG"
|
private const val EXTRA_IS_IN_BG = "EXTRA_IS_IN_BG"
|
||||||
private const val EXTRA_IS_REJECTED = "EXTRA_IS_REJECTED"
|
private const val EXTRA_END_CALL_REJECTED = "EXTRA_END_CALL_REJECTED"
|
||||||
|
private const val EXTRA_END_CALL_REASON = "EXTRA_END_CALL_REASON"
|
||||||
|
|
||||||
fun onIncomingCallRinging(context: Context,
|
fun onIncomingCallRinging(context: Context,
|
||||||
callId: String,
|
callId: String,
|
||||||
|
@ -360,12 +362,13 @@ class CallService : VectorService() {
|
||||||
ContextCompat.startForegroundService(context, intent)
|
ContextCompat.startForegroundService(context, intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onCallTerminated(context: Context, callId: String, isRejected: Boolean) {
|
fun onCallTerminated(context: Context, callId: String, endCallReason: EndCallReason, rejected: Boolean) {
|
||||||
val intent = Intent(context, CallService::class.java)
|
val intent = Intent(context, CallService::class.java)
|
||||||
.apply {
|
.apply {
|
||||||
action = ACTION_CALL_TERMINATED
|
action = ACTION_CALL_TERMINATED
|
||||||
putExtra(EXTRA_CALL_ID, callId)
|
putExtra(EXTRA_CALL_ID, callId)
|
||||||
putExtra(EXTRA_IS_REJECTED, isRejected)
|
putExtra(EXTRA_END_CALL_REASON, endCallReason)
|
||||||
|
putExtra(EXTRA_END_CALL_REJECTED, rejected)
|
||||||
}
|
}
|
||||||
ContextCompat.startForegroundService(context, intent)
|
ContextCompat.startForegroundService(context, intent)
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,7 +118,7 @@ class CallControlsView @JvmOverloads constructor(
|
||||||
views.connectedControls.isVisible = false
|
views.connectedControls.isVisible = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is CallState.Terminated,
|
is CallState.Ended,
|
||||||
null -> {
|
null -> {
|
||||||
views.ringingControls.isVisible = false
|
views.ringingControls.isVisible = false
|
||||||
views.connectedControls.isVisible = false
|
views.connectedControls.isVisible = false
|
||||||
|
|
|
@ -196,7 +196,7 @@ class VectorCallActivity : VectorBaseActivity<ActivityCallBinding>(), CallContro
|
||||||
views.callConnectingProgress.isVisible = true
|
views.callConnectingProgress.isVisible = true
|
||||||
configureCallInfo(state)
|
configureCallInfo(state)
|
||||||
}
|
}
|
||||||
is CallState.Connected -> {
|
is CallState.Connected -> {
|
||||||
if (callState.iceConnectionState == MxPeerConnectionState.CONNECTED) {
|
if (callState.iceConnectionState == MxPeerConnectionState.CONNECTED) {
|
||||||
if (state.isLocalOnHold || state.isRemoteOnHold) {
|
if (state.isLocalOnHold || state.isRemoteOnHold) {
|
||||||
views.smallIsHeldIcon.isVisible = true
|
views.smallIsHeldIcon.isVisible = true
|
||||||
|
@ -246,10 +246,10 @@ class VectorCallActivity : VectorBaseActivity<ActivityCallBinding>(), CallContro
|
||||||
views.callConnectingProgress.isVisible = true
|
views.callConnectingProgress.isVisible = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is CallState.Terminated -> {
|
is CallState.Ended -> {
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
null -> {
|
null -> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ class CallTransferViewModel @AssistedInject constructor(@Assisted initialState:
|
||||||
private val call = callManager.getCallById(initialState.callId)
|
private val call = callManager.getCallById(initialState.callId)
|
||||||
private val callListener = object : WebRtcCall.Listener {
|
private val callListener = object : WebRtcCall.Listener {
|
||||||
override fun onStateUpdate(call: MxCall) {
|
override fun onStateUpdate(call: MxCall) {
|
||||||
if (call.state == CallState.Terminated) {
|
if (call.state is CallState.Ended) {
|
||||||
_viewEvents.post(CallTransferViewEvents.Dismiss)
|
_viewEvents.post(CallTransferViewEvents.Dismiss)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,9 @@ import org.matrix.android.sdk.api.session.room.model.call.CallCandidatesContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.call.CallHangupContent
|
import org.matrix.android.sdk.api.session.room.model.call.CallHangupContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent
|
import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.call.CallNegotiateContent
|
import org.matrix.android.sdk.api.session.room.model.call.CallNegotiateContent
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.call.CallRejectContent
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.call.CallSelectAnswerContent
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.call.EndCallReason
|
||||||
import org.matrix.android.sdk.api.session.room.model.call.SdpType
|
import org.matrix.android.sdk.api.session.room.model.call.SdpType
|
||||||
import org.threeten.bp.Duration
|
import org.threeten.bp.Duration
|
||||||
import org.webrtc.AudioSource
|
import org.webrtc.AudioSource
|
||||||
|
@ -99,7 +102,7 @@ class WebRtcCall(
|
||||||
private val sessionProvider: Provider<Session?>,
|
private val sessionProvider: Provider<Session?>,
|
||||||
private val peerConnectionFactoryProvider: Provider<PeerConnectionFactory?>,
|
private val peerConnectionFactoryProvider: Provider<PeerConnectionFactory?>,
|
||||||
private val onCallBecomeActive: (WebRtcCall) -> Unit,
|
private val onCallBecomeActive: (WebRtcCall) -> Unit,
|
||||||
private val onCallEnded: (String, Boolean) -> Unit
|
private val onCallEnded: (String, EndCallReason, Boolean) -> Unit
|
||||||
) : MxCall.StateListener {
|
) : MxCall.StateListener {
|
||||||
|
|
||||||
interface Listener : MxCall.StateListener {
|
interface Listener : MxCall.StateListener {
|
||||||
|
@ -227,7 +230,7 @@ class WebRtcCall(
|
||||||
// Allow a short time for initial candidates to be gathered
|
// Allow a short time for initial candidates to be gathered
|
||||||
delay(200)
|
delay(200)
|
||||||
}
|
}
|
||||||
if (mxCall.state == CallState.Terminated) {
|
if (mxCall.state is CallState.Ended) {
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
if (mxCall.state == CallState.CreateOffer) {
|
if (mxCall.state == CallState.CreateOffer) {
|
||||||
|
@ -285,7 +288,7 @@ class WebRtcCall(
|
||||||
createCallId = CallIdGenerator.generate(),
|
createCallId = CallIdGenerator.generate(),
|
||||||
awaitCallId = null
|
awaitCallId = null
|
||||||
)
|
)
|
||||||
endCall(sendEndSignaling = false)
|
terminate(EndCallReason.REPLACED)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,8 +310,8 @@ class WebRtcCall(
|
||||||
createCallId = newCallId,
|
createCallId = newCallId,
|
||||||
awaitCallId = null
|
awaitCallId = null
|
||||||
)
|
)
|
||||||
endCall(sendEndSignaling = false)
|
terminate(EndCallReason.REPLACED)
|
||||||
transferTargetCall.endCall(sendEndSignaling = false)
|
transferTargetCall.terminate(EndCallReason.REPLACED)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -461,7 +464,7 @@ class WebRtcCall(
|
||||||
peerConnection?.awaitSetRemoteDescription(offerSdp)
|
peerConnection?.awaitSetRemoteDescription(offerSdp)
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
Timber.v("Failure putting remote description")
|
Timber.v("Failure putting remote description")
|
||||||
endCall(true, CallHangupContent.Reason.UNKWOWN_ERROR)
|
endCall(reason = EndCallReason.UNKWOWN_ERROR)
|
||||||
return@withContext
|
return@withContext
|
||||||
}
|
}
|
||||||
// 2) Access camera + microphone, create local stream
|
// 2) Access camera + microphone, create local stream
|
||||||
|
@ -767,7 +770,7 @@ class WebRtcCall(
|
||||||
if (stream.audioTracks.size > 1 || stream.videoTracks.size > 1) {
|
if (stream.audioTracks.size > 1 || stream.videoTracks.size > 1) {
|
||||||
Timber.e("## VOIP StreamObserver weird looking stream: $stream")
|
Timber.e("## VOIP StreamObserver weird looking stream: $stream")
|
||||||
// TODO maybe do something more??
|
// TODO maybe do something more??
|
||||||
endCall(true)
|
endCall(EndCallReason.UNKWOWN_ERROR)
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
if (stream.audioTracks.size == 1) {
|
if (stream.audioTracks.size == 1) {
|
||||||
|
@ -795,33 +798,34 @@ class WebRtcCall(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun endCall(sendEndSignaling: Boolean = true, reason: CallHangupContent.Reason? = null) {
|
fun endCall(reason: EndCallReason? = null) {
|
||||||
sessionScope?.launch(dispatcher) {
|
sessionScope?.launch(dispatcher) {
|
||||||
if (mxCall.state == CallState.Terminated) {
|
if (mxCall.state is CallState.Ended) {
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
// Close tracks ASAP
|
val reject = mxCall.state is CallState.LocalRinging
|
||||||
localVideoTrack?.setEnabled(false)
|
terminate(EndCallReason.USER_HANGUP, reject)
|
||||||
localVideoTrack?.setEnabled(false)
|
if (reject) {
|
||||||
cameraAvailabilityCallback?.let { cameraAvailabilityCallback ->
|
mxCall.reject()
|
||||||
val cameraManager = context.getSystemService<CameraManager>()!!
|
} else {
|
||||||
cameraManager.unregisterAvailabilityCallback(cameraAvailabilityCallback)
|
mxCall.hangUp(reason)
|
||||||
}
|
|
||||||
val wasRinging = mxCall.state is CallState.LocalRinging
|
|
||||||
mxCall.state = CallState.Terminated
|
|
||||||
release()
|
|
||||||
val isRejected = wasRinging && sendEndSignaling
|
|
||||||
onCallEnded(callId, isRejected)
|
|
||||||
if (sendEndSignaling) {
|
|
||||||
if (wasRinging) {
|
|
||||||
mxCall.reject()
|
|
||||||
} else {
|
|
||||||
mxCall.hangUp(reason)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun terminate(reason: EndCallReason? = null, rejected: Boolean = false) = withContext(dispatcher) {
|
||||||
|
// Close tracks ASAP
|
||||||
|
localVideoTrack?.setEnabled(false)
|
||||||
|
localVideoTrack?.setEnabled(false)
|
||||||
|
cameraAvailabilityCallback?.let { cameraAvailabilityCallback ->
|
||||||
|
val cameraManager = context.getSystemService<CameraManager>()!!
|
||||||
|
cameraManager.unregisterAvailabilityCallback(cameraAvailabilityCallback)
|
||||||
|
}
|
||||||
|
mxCall.state = CallState.Ended(reason ?: EndCallReason.USER_HANGUP)
|
||||||
|
release()
|
||||||
|
onCallEnded(callId, reason ?: EndCallReason.USER_HANGUP, rejected)
|
||||||
|
}
|
||||||
|
|
||||||
// Call listener
|
// Call listener
|
||||||
|
|
||||||
fun onCallIceCandidateReceived(iceCandidatesContent: CallCandidatesContent) {
|
fun onCallIceCandidateReceived(iceCandidatesContent: CallCandidatesContent) {
|
||||||
|
@ -844,7 +848,7 @@ class WebRtcCall(
|
||||||
try {
|
try {
|
||||||
peerConnection?.awaitSetRemoteDescription(sdp)
|
peerConnection?.awaitSetRemoteDescription(sdp)
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
endCall(true, CallHangupContent.Reason.UNKWOWN_ERROR)
|
endCall(EndCallReason.UNKWOWN_ERROR)
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
if (mxCall.opponentPartyId?.hasValue().orFalse()) {
|
if (mxCall.opponentPartyId?.hasValue().orFalse()) {
|
||||||
|
@ -905,6 +909,29 @@ class WebRtcCall(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onCallHangupReceived(callHangupContent: CallHangupContent) {
|
||||||
|
sessionScope?.launch(dispatcher) {
|
||||||
|
terminate(callHangupContent.reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onCallRejectReceived(callRejectContent: CallRejectContent) {
|
||||||
|
sessionScope?.launch(dispatcher) {
|
||||||
|
terminate(callRejectContent.reason, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onCallSelectedAnswerReceived(callSelectAnswerContent: CallSelectAnswerContent) {
|
||||||
|
sessionScope?.launch(dispatcher) {
|
||||||
|
val selectedPartyId = callSelectAnswerContent.selectedPartyId
|
||||||
|
if (selectedPartyId != mxCall.ourPartyId) {
|
||||||
|
Timber.i("Got select_answer for party ID $selectedPartyId: we are party ID ${mxCall.ourPartyId}.")
|
||||||
|
// The other party has picked somebody else's answer
|
||||||
|
terminate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun onCallAssertedIdentityReceived(callAssertedIdentityContent: CallAssertedIdentityContent) {
|
fun onCallAssertedIdentityReceived(callAssertedIdentityContent: CallAssertedIdentityContent) {
|
||||||
sessionScope?.launch(dispatcher) {
|
sessionScope?.launch(dispatcher) {
|
||||||
val session = sessionProvider.get() ?: return@launch
|
val session = sessionProvider.get() ?: return@launch
|
||||||
|
|
|
@ -29,7 +29,9 @@ import im.vector.app.features.call.lookup.CallProtocolsChecker
|
||||||
import im.vector.app.features.call.lookup.CallUserMapper
|
import im.vector.app.features.call.lookup.CallUserMapper
|
||||||
import im.vector.app.features.call.utils.EglUtils
|
import im.vector.app.features.call.utils.EglUtils
|
||||||
import im.vector.app.features.call.vectorCallService
|
import im.vector.app.features.call.vectorCallService
|
||||||
|
import im.vector.app.features.session.coroutineScope
|
||||||
import im.vector.app.push.fcm.FcmHelper
|
import im.vector.app.push.fcm.FcmHelper
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.asCoroutineDispatcher
|
import kotlinx.coroutines.asCoroutineDispatcher
|
||||||
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
|
||||||
|
@ -45,6 +47,7 @@ import org.matrix.android.sdk.api.session.room.model.call.CallInviteContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.call.CallNegotiateContent
|
import org.matrix.android.sdk.api.session.room.model.call.CallNegotiateContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.call.CallRejectContent
|
import org.matrix.android.sdk.api.session.room.model.call.CallRejectContent
|
||||||
import org.matrix.android.sdk.api.session.room.model.call.CallSelectAnswerContent
|
import org.matrix.android.sdk.api.session.room.model.call.CallSelectAnswerContent
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.call.EndCallReason
|
||||||
import org.webrtc.DefaultVideoDecoderFactory
|
import org.webrtc.DefaultVideoDecoderFactory
|
||||||
import org.webrtc.DefaultVideoEncoderFactory
|
import org.webrtc.DefaultVideoEncoderFactory
|
||||||
import org.webrtc.PeerConnectionFactory
|
import org.webrtc.PeerConnectionFactory
|
||||||
|
@ -75,6 +78,9 @@ class WebRtcCallManager @Inject constructor(
|
||||||
private val callUserMapper: CallUserMapper?
|
private val callUserMapper: CallUserMapper?
|
||||||
get() = currentSession?.vectorCallService?.userMapper
|
get() = currentSession?.vectorCallService?.userMapper
|
||||||
|
|
||||||
|
private val sessionScope: CoroutineScope?
|
||||||
|
get() = currentSession?.coroutineScope
|
||||||
|
|
||||||
interface CurrentCallListener {
|
interface CurrentCallListener {
|
||||||
fun onCurrentCallChange(call: WebRtcCall?) {}
|
fun onCurrentCallChange(call: WebRtcCall?) {}
|
||||||
fun onAudioDevicesChange() {}
|
fun onAudioDevicesChange() {}
|
||||||
|
@ -232,12 +238,12 @@ class WebRtcCallManager @Inject constructor(
|
||||||
this.currentCall.setAndNotify(call)
|
this.currentCall.setAndNotify(call)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onCallEnded(callId: String, isRejected: Boolean) {
|
private fun onCallEnded(callId: String, endCallReason: EndCallReason, rejected: Boolean) {
|
||||||
Timber.v("## VOIP WebRtcPeerConnectionManager onCall ended: $callId")
|
Timber.v("## VOIP WebRtcPeerConnectionManager onCall ended: $callId")
|
||||||
val webRtcCall = callsByCallId.remove(callId) ?: return Unit.also {
|
val webRtcCall = callsByCallId.remove(callId) ?: return Unit.also {
|
||||||
Timber.v("On call ended for unknown call $callId")
|
Timber.v("On call ended for unknown call $callId")
|
||||||
}
|
}
|
||||||
CallService.onCallTerminated(context, callId, isRejected)
|
CallService.onCallTerminated(context, callId, endCallReason, rejected)
|
||||||
callsByRoomId[webRtcCall.signalingRoomId]?.remove(webRtcCall)
|
callsByRoomId[webRtcCall.signalingRoomId]?.remove(webRtcCall)
|
||||||
callsByRoomId[webRtcCall.nativeRoomId]?.remove(webRtcCall)
|
callsByRoomId[webRtcCall.nativeRoomId]?.remove(webRtcCall)
|
||||||
transferees.remove(callId)
|
transferees.remove(callId)
|
||||||
|
@ -329,8 +335,8 @@ class WebRtcCallManager @Inject constructor(
|
||||||
return webRtcCall
|
return webRtcCall
|
||||||
}
|
}
|
||||||
|
|
||||||
fun endCallForRoom(roomId: String, originatedByMe: Boolean = true) {
|
fun endCallForRoom(roomId: String) {
|
||||||
callsByRoomId[roomId]?.firstOrNull()?.endCall(originatedByMe)
|
callsByRoomId[roomId]?.firstOrNull()?.endCall()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCallInviteReceived(mxCall: MxCall, callInviteContent: CallInviteContent) {
|
override fun onCallInviteReceived(mxCall: MxCall, callInviteContent: CallInviteContent) {
|
||||||
|
@ -386,7 +392,7 @@ class WebRtcCallManager @Inject constructor(
|
||||||
?: return Unit.also {
|
?: return Unit.also {
|
||||||
Timber.w("onCallHangupReceived for non active call? ${callHangupContent.callId}")
|
Timber.w("onCallHangupReceived for non active call? ${callHangupContent.callId}")
|
||||||
}
|
}
|
||||||
call.endCall(false)
|
call.onCallHangupReceived(callHangupContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCallRejectReceived(callRejectContent: CallRejectContent) {
|
override fun onCallRejectReceived(callRejectContent: CallRejectContent) {
|
||||||
|
@ -394,7 +400,7 @@ class WebRtcCallManager @Inject constructor(
|
||||||
?: return Unit.also {
|
?: return Unit.also {
|
||||||
Timber.w("onCallRejectReceived for non active call? ${callRejectContent.callId}")
|
Timber.w("onCallRejectReceived for non active call? ${callRejectContent.callId}")
|
||||||
}
|
}
|
||||||
call.endCall(false)
|
call.onCallRejectReceived(callRejectContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCallSelectAnswerReceived(callSelectAnswerContent: CallSelectAnswerContent) {
|
override fun onCallSelectAnswerReceived(callSelectAnswerContent: CallSelectAnswerContent) {
|
||||||
|
@ -402,12 +408,7 @@ class WebRtcCallManager @Inject constructor(
|
||||||
?: 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
|
call.onCallSelectedAnswerReceived(callSelectAnswerContent)
|
||||||
if (selectedPartyId != 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
|
|
||||||
call.endCall(false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCallNegotiateReceived(callNegotiateContent: CallNegotiateContent) {
|
override fun onCallNegotiateReceived(callNegotiateContent: CallNegotiateContent) {
|
||||||
|
@ -420,7 +421,7 @@ class WebRtcCallManager @Inject constructor(
|
||||||
|
|
||||||
override fun onCallManagedByOtherSession(callId: String) {
|
override fun onCallManagedByOtherSession(callId: String) {
|
||||||
Timber.v("## VOIP onCallManagedByOtherSession: $callId")
|
Timber.v("## VOIP onCallManagedByOtherSession: $callId")
|
||||||
onCallEnded(callId, false)
|
onCallEnded(callId, EndCallReason.ANSWERED_ELSEWHERE, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCallAssertedIdentityReceived(callAssertedIdentityContent: CallAssertedIdentityContent) {
|
override fun onCallAssertedIdentityReceived(callAssertedIdentityContent: CallAssertedIdentityContent) {
|
||||||
|
|
Loading…
Reference in a new issue