mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-26 11:26:01 +03:00
Merge pull request #3723 from vector-im/feature/fga/log_tags_voip
Feature/fga/log tags voip
This commit is contained in:
commit
0c211d7b1e
13 changed files with 193 additions and 203 deletions
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* 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.logger
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parent class for custom logger tags. Can be used with Timber :
|
||||||
|
*
|
||||||
|
* val loggerTag = LoggerTag("MyTag", LoggerTag.VOIP)
|
||||||
|
* Timber.tag(loggerTag.value).v("My log message")
|
||||||
|
*/
|
||||||
|
open class LoggerTag(_value: String, parentTag: LoggerTag? = null) {
|
||||||
|
|
||||||
|
object VOIP : LoggerTag("VOIP", null)
|
||||||
|
|
||||||
|
val value: String = if (parentTag == null) {
|
||||||
|
_value
|
||||||
|
} else {
|
||||||
|
"${parentTag.value}/$_value"
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,11 +20,14 @@ import io.realm.Realm
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
import org.matrix.android.sdk.api.session.events.model.EventType
|
||||||
import org.matrix.android.sdk.internal.database.model.EventInsertType
|
import org.matrix.android.sdk.internal.database.model.EventInsertType
|
||||||
|
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||||
import org.matrix.android.sdk.internal.session.EventInsertLiveProcessor
|
import org.matrix.android.sdk.internal.session.EventInsertLiveProcessor
|
||||||
import org.matrix.android.sdk.internal.session.SessionScope
|
import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
private val loggerTag = LoggerTag("CallEventProcessor", LoggerTag.VOIP)
|
||||||
|
|
||||||
@SessionScope
|
@SessionScope
|
||||||
internal class CallEventProcessor @Inject constructor(private val callSignalingHandler: CallSignalingHandler)
|
internal class CallEventProcessor @Inject constructor(private val callSignalingHandler: CallSignalingHandler)
|
||||||
: EventInsertLiveProcessor {
|
: EventInsertLiveProcessor {
|
||||||
|
@ -71,14 +74,8 @@ internal class CallEventProcessor @Inject constructor(private val callSignalingH
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun dispatchToCallSignalingHandlerIfNeeded(event: Event) {
|
private fun dispatchToCallSignalingHandlerIfNeeded(event: Event) {
|
||||||
val now = System.currentTimeMillis()
|
|
||||||
event.roomId ?: return Unit.also {
|
event.roomId ?: return Unit.also {
|
||||||
Timber.w("Event with no room id ${event.eventId}")
|
Timber.tag(loggerTag.value).w("Event with no room id ${event.eventId}")
|
||||||
}
|
|
||||||
val age = now - (event.ageLocalTs ?: now)
|
|
||||||
if (age > 40_000) {
|
|
||||||
// Too old to ring?
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
callSignalingHandler.onCallEvent(event)
|
callSignalingHandler.onCallEvent(event)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.session.call
|
package org.matrix.android.sdk.internal.session.call
|
||||||
|
|
||||||
|
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||||
import org.matrix.android.sdk.api.session.call.CallListener
|
import org.matrix.android.sdk.api.session.call.CallListener
|
||||||
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
|
||||||
|
@ -36,6 +37,9 @@ import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
private val loggerTag = LoggerTag("CallSignalingHandler", LoggerTag.VOIP)
|
||||||
|
private const val MAX_AGE_TO_RING = 40_000
|
||||||
|
|
||||||
@SessionScope
|
@SessionScope
|
||||||
internal class CallSignalingHandler @Inject constructor(private val activeCallHandler: ActiveCallHandler,
|
internal class CallSignalingHandler @Inject constructor(private val activeCallHandler: ActiveCallHandler,
|
||||||
private val mxCallFactory: MxCallFactory,
|
private val mxCallFactory: MxCallFactory,
|
||||||
|
@ -111,12 +115,12 @@ internal class CallSignalingHandler @Inject constructor(private val activeCallHa
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (call.isOutgoing) {
|
if (call.isOutgoing) {
|
||||||
Timber.v("Got selectAnswer for an outbound call: ignoring")
|
Timber.tag(loggerTag.value).v("Got selectAnswer for an outbound call: ignoring")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val selectedPartyId = content.selectedPartyId
|
val selectedPartyId = content.selectedPartyId
|
||||||
if (selectedPartyId == null) {
|
if (selectedPartyId == null) {
|
||||||
Timber.w("Got nonsensical select_answer with null selected_party_id: ignoring")
|
Timber.tag(loggerTag.value).w("Got nonsensical select_answer with null selected_party_id: ignoring")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
callListenersDispatcher.onCallSelectAnswerReceived(content)
|
callListenersDispatcher.onCallSelectAnswerReceived(content)
|
||||||
|
@ -130,7 +134,7 @@ internal class CallSignalingHandler @Inject constructor(private val activeCallHa
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (call.opponentPartyId != null && !call.partyIdsMatches(content)) {
|
if (call.opponentPartyId != null && !call.partyIdsMatches(content)) {
|
||||||
Timber.v("Ignoring candidates from party ID ${content.partyId} we have chosen party ID ${call.opponentPartyId}")
|
Timber.tag(loggerTag.value).v("Ignoring candidates from party ID ${content.partyId} we have chosen party ID ${call.opponentPartyId}")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
callListenersDispatcher.onCallIceCandidateReceived(call, content)
|
callListenersDispatcher.onCallIceCandidateReceived(call, content)
|
||||||
|
@ -163,7 +167,7 @@ internal class CallSignalingHandler @Inject constructor(private val activeCallHa
|
||||||
// party ID must match (our chosen partner hanging up the call) or be undefined (we haven't chosen
|
// party ID must match (our chosen partner hanging up the call) or be undefined (we haven't chosen
|
||||||
// a partner yet but we're treating the hangup as a reject as per VoIP v0)
|
// a partner yet but we're treating the hangup as a reject as per VoIP v0)
|
||||||
if (call.opponentPartyId != null && !call.partyIdsMatches(content)) {
|
if (call.opponentPartyId != null && !call.partyIdsMatches(content)) {
|
||||||
Timber.v("Ignoring hangup from party ID ${content.partyId} we have chosen party ID ${call.opponentPartyId}")
|
Timber.tag(loggerTag.value).v("Ignoring hangup from party ID ${content.partyId} we have chosen party ID ${call.opponentPartyId}")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (call.state !is CallState.Ended) {
|
if (call.state !is CallState.Ended) {
|
||||||
|
@ -180,12 +184,18 @@ internal class CallSignalingHandler @Inject constructor(private val activeCallHa
|
||||||
if (event.roomId == null || event.senderId == null) {
|
if (event.roomId == null || event.senderId == null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
val age = now - (event.ageLocalTs ?: now)
|
||||||
|
if (age > MAX_AGE_TO_RING) {
|
||||||
|
Timber.tag(loggerTag.value).w("Call invite is too old to ring.")
|
||||||
|
return
|
||||||
|
}
|
||||||
val content = event.getClearContent().toModel<CallInviteContent>() ?: return
|
val content = event.getClearContent().toModel<CallInviteContent>() ?: return
|
||||||
|
|
||||||
content.callId ?: return
|
content.callId ?: return
|
||||||
if (invitedCallIds.contains(content.callId)) {
|
if (invitedCallIds.contains(content.callId)) {
|
||||||
// Call is already known, maybe due to fast lane. Ignore
|
// Call is already known, maybe due to fast lane. Ignore
|
||||||
Timber.d("Ignoring already known call invite")
|
Timber.tag(loggerTag.value).d("Ignoring already known call invite")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val incomingCall = mxCallFactory.createIncomingCall(
|
val incomingCall = mxCallFactory.createIncomingCall(
|
||||||
|
@ -214,7 +224,8 @@ internal class CallSignalingHandler @Inject constructor(private val activeCallHa
|
||||||
callListenersDispatcher.onCallManagedByOtherSession(content.callId)
|
callListenersDispatcher.onCallManagedByOtherSession(content.callId)
|
||||||
} else {
|
} else {
|
||||||
if (call.opponentPartyId != null) {
|
if (call.opponentPartyId != null) {
|
||||||
Timber.v("Ignoring answer from party ID ${content.partyId} we already have an answer from ${call.opponentPartyId}")
|
Timber.tag(loggerTag.value)
|
||||||
|
.v("Ignoring answer from party ID ${content.partyId} we already have an answer from ${call.opponentPartyId}")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
mxCallFactory.updateOutgoingCallWithOpponentData(call, event.senderId, content, content.capabilities)
|
mxCallFactory.updateOutgoingCallWithOpponentData(call, event.senderId, content, content.capabilities)
|
||||||
|
@ -231,7 +242,7 @@ internal class CallSignalingHandler @Inject constructor(private val activeCallHa
|
||||||
activeCallHandler.getCallWithId(it)
|
activeCallHandler.getCallWithId(it)
|
||||||
}
|
}
|
||||||
if (currentCall == null) {
|
if (currentCall == null) {
|
||||||
Timber.v("Call with id $callId is null")
|
Timber.tag(loggerTag.value).v("Call with id $callId is null")
|
||||||
}
|
}
|
||||||
return currentCall
|
return currentCall
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,6 @@ import org.matrix.android.sdk.api.session.call.CallSignalingService
|
||||||
import org.matrix.android.sdk.api.session.call.MxCall
|
import org.matrix.android.sdk.api.session.call.MxCall
|
||||||
import org.matrix.android.sdk.api.session.call.TurnServerResponse
|
import org.matrix.android.sdk.api.session.call.TurnServerResponse
|
||||||
import org.matrix.android.sdk.internal.session.SessionScope
|
import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
import timber.log.Timber
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@SessionScope
|
@SessionScope
|
||||||
|
@ -51,7 +50,6 @@ internal class DefaultCallSignalingService @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getCallWithId(callId: String): MxCall? {
|
override fun getCallWithId(callId: String): MxCall? {
|
||||||
Timber.v("## VOIP getCallWithId $callId all calls ${activeCallHandler.getActiveCallsLiveData().value?.map { it.callId }}")
|
|
||||||
return activeCallHandler.getCallWithId(callId)
|
return activeCallHandler.getCallWithId(callId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.matrix.android.sdk.internal.session.call.model
|
package org.matrix.android.sdk.internal.session.call.model
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.MatrixConfiguration
|
import org.matrix.android.sdk.api.MatrixConfiguration
|
||||||
|
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||||
import org.matrix.android.sdk.api.session.call.CallIdGenerator
|
import org.matrix.android.sdk.api.session.call.CallIdGenerator
|
||||||
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
|
||||||
|
@ -48,6 +49,8 @@ import org.matrix.android.sdk.internal.session.room.send.queue.EventSenderProces
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
|
|
||||||
|
private val loggerTag = LoggerTag("MxCallImpl", LoggerTag.VOIP)
|
||||||
|
|
||||||
internal class MxCallImpl(
|
internal class MxCallImpl(
|
||||||
override val callId: String,
|
override val callId: String,
|
||||||
override val isOutgoing: Boolean,
|
override val isOutgoing: Boolean,
|
||||||
|
@ -94,7 +97,7 @@ internal class MxCallImpl(
|
||||||
try {
|
try {
|
||||||
it.onStateUpdate(this)
|
it.onStateUpdate(this)
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
Timber.d("dispatchStateChange failed for call $callId : ${failure.localizedMessage}")
|
Timber.tag(loggerTag.value).d("dispatchStateChange failed for call $callId : ${failure.localizedMessage}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,7 +113,7 @@ internal class MxCallImpl(
|
||||||
|
|
||||||
override fun offerSdp(sdpString: String) {
|
override fun offerSdp(sdpString: String) {
|
||||||
if (!isOutgoing) return
|
if (!isOutgoing) return
|
||||||
Timber.v("## VOIP offerSdp $callId")
|
Timber.tag(loggerTag.value).v("offerSdp $callId")
|
||||||
state = CallState.Dialing
|
state = CallState.Dialing
|
||||||
CallInviteContent(
|
CallInviteContent(
|
||||||
callId = callId,
|
callId = callId,
|
||||||
|
@ -125,7 +128,7 @@ internal class MxCallImpl(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun sendLocalCallCandidates(candidates: List<CallCandidate>) {
|
override fun sendLocalCallCandidates(candidates: List<CallCandidate>) {
|
||||||
Timber.v("Send local call canditates $callId: $candidates")
|
Timber.tag(loggerTag.value).v("Send local call canditates $callId: $candidates")
|
||||||
CallCandidatesContent(
|
CallCandidatesContent(
|
||||||
callId = callId,
|
callId = callId,
|
||||||
partyId = ourPartyId,
|
partyId = ourPartyId,
|
||||||
|
@ -142,11 +145,11 @@ internal class MxCallImpl(
|
||||||
|
|
||||||
override fun reject() {
|
override fun reject() {
|
||||||
if (opponentVersion < 1) {
|
if (opponentVersion < 1) {
|
||||||
Timber.v("Opponent version is less than 1 ($opponentVersion): sending hangup instead of reject")
|
Timber.tag(loggerTag.value).v("Opponent version is less than 1 ($opponentVersion): sending hangup instead of reject")
|
||||||
hangUp(EndCallReason.USER_HANGUP)
|
hangUp(EndCallReason.USER_HANGUP)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
Timber.v("## VOIP reject $callId")
|
Timber.tag(loggerTag.value).v("reject $callId")
|
||||||
CallRejectContent(
|
CallRejectContent(
|
||||||
callId = callId,
|
callId = callId,
|
||||||
partyId = ourPartyId,
|
partyId = ourPartyId,
|
||||||
|
@ -158,7 +161,7 @@ internal class MxCallImpl(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hangUp(reason: EndCallReason?) {
|
override fun hangUp(reason: EndCallReason?) {
|
||||||
Timber.v("## VOIP hangup $callId")
|
Timber.tag(loggerTag.value).v("hangup $callId")
|
||||||
CallHangupContent(
|
CallHangupContent(
|
||||||
callId = callId,
|
callId = callId,
|
||||||
partyId = ourPartyId,
|
partyId = ourPartyId,
|
||||||
|
@ -171,7 +174,7 @@ internal class MxCallImpl(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(sdpString: String) {
|
override fun accept(sdpString: String) {
|
||||||
Timber.v("## VOIP accept $callId")
|
Timber.tag(loggerTag.value).v("accept $callId")
|
||||||
if (isOutgoing) return
|
if (isOutgoing) return
|
||||||
state = CallState.Answering
|
state = CallState.Answering
|
||||||
CallAnswerContent(
|
CallAnswerContent(
|
||||||
|
@ -186,7 +189,7 @@ internal class MxCallImpl(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun negotiate(sdpString: String, type: SdpType) {
|
override fun negotiate(sdpString: String, type: SdpType) {
|
||||||
Timber.v("## VOIP negotiate $callId")
|
Timber.tag(loggerTag.value).v("negotiate $callId")
|
||||||
CallNegotiateContent(
|
CallNegotiateContent(
|
||||||
callId = callId,
|
callId = callId,
|
||||||
partyId = ourPartyId,
|
partyId = ourPartyId,
|
||||||
|
@ -199,7 +202,7 @@ internal class MxCallImpl(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun selectAnswer() {
|
override fun selectAnswer() {
|
||||||
Timber.v("## VOIP select answer $callId")
|
Timber.tag(loggerTag.value).v("select answer $callId")
|
||||||
if (isOutgoing) return
|
if (isOutgoing) return
|
||||||
state = CallState.Answering
|
state = CallState.Answering
|
||||||
CallSelectAnswerContent(
|
CallSelectAnswerContent(
|
||||||
|
@ -220,7 +223,7 @@ internal class MxCallImpl(
|
||||||
val profileInfo = try {
|
val profileInfo = try {
|
||||||
getProfileInfoTask.execute(profileInfoParams)
|
getProfileInfoTask.execute(profileInfoParams)
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
Timber.v("Fail fetching profile info of $targetUserId while transferring call")
|
Timber.tag(loggerTag.value).v("Fail fetching profile info of $targetUserId while transferring call")
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
CallReplacesContent(
|
CallReplacesContent(
|
||||||
|
|
|
@ -37,10 +37,13 @@ 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.logger.LoggerTag
|
||||||
import org.matrix.android.sdk.api.session.room.model.call.EndCallReason
|
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
|
||||||
|
|
||||||
|
private val loggerTag = LoggerTag("CallService", LoggerTag.VOIP)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Foreground service to manage calls
|
* Foreground service to manage calls
|
||||||
*/
|
*/
|
||||||
|
@ -93,7 +96,7 @@ class CallService : VectorService() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
Timber.v("## VOIP onStartCommand $intent")
|
Timber.tag(loggerTag.value).v("onStartCommand $intent")
|
||||||
if (mediaSession == null) {
|
if (mediaSession == null) {
|
||||||
mediaSession = MediaSessionCompat(applicationContext, CallService::class.java.name).apply {
|
mediaSession = MediaSessionCompat(applicationContext, CallService::class.java.name).apply {
|
||||||
setCallback(mediaSessionButtonCallback)
|
setCallback(mediaSessionButtonCallback)
|
||||||
|
@ -150,7 +153,7 @@ class CallService : VectorService() {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
private fun displayIncomingCallNotification(intent: Intent) {
|
private fun displayIncomingCallNotification(intent: Intent) {
|
||||||
Timber.v("## VOIP displayIncomingCallNotification $intent")
|
Timber.tag(loggerTag.value).v("displayIncomingCallNotification $intent")
|
||||||
val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: ""
|
val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: ""
|
||||||
val call = callManager.getCallById(callId) ?: return Unit.also {
|
val call = callManager.getCallById(callId) ?: return Unit.also {
|
||||||
handleUnexpectedState(callId)
|
handleUnexpectedState(callId)
|
||||||
|
@ -158,7 +161,7 @@ class CallService : VectorService() {
|
||||||
val callInformation = call.toCallInformation()
|
val callInformation = call.toCallInformation()
|
||||||
val isVideoCall = call.mxCall.isVideoCall
|
val isVideoCall = call.mxCall.isVideoCall
|
||||||
val fromBg = intent.getBooleanExtra(EXTRA_IS_IN_BG, false)
|
val fromBg = intent.getBooleanExtra(EXTRA_IS_IN_BG, false)
|
||||||
Timber.v("displayIncomingCallNotification : display the dedicated notification")
|
Timber.tag(loggerTag.value).v("displayIncomingCallNotification : display the dedicated notification")
|
||||||
val incomingCallAlert = IncomingCallAlert(callId,
|
val incomingCallAlert = IncomingCallAlert(callId,
|
||||||
shouldBeDisplayedIn = { activity ->
|
shouldBeDisplayedIn = { activity ->
|
||||||
if (activity is VectorCallActivity) {
|
if (activity is VectorCallActivity) {
|
||||||
|
@ -197,7 +200,7 @@ class CallService : VectorService() {
|
||||||
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) {
|
||||||
Timber.v("Call terminated for unknown call $callId$")
|
Timber.tag(loggerTag.value).v("Call terminated for unknown call $callId$")
|
||||||
handleUnexpectedState(callId)
|
handleUnexpectedState(callId)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -207,13 +210,11 @@ class CallService : VectorService() {
|
||||||
myStopSelf()
|
myStopSelf()
|
||||||
}
|
}
|
||||||
val wasConnected = connectedCallIds.remove(callId)
|
val wasConnected = connectedCallIds.remove(callId)
|
||||||
|
val notification = notificationUtils.buildCallEndedNotification(terminatedCall.isVideoCall)
|
||||||
|
notificationManager.notify(callId.hashCode(), notification)
|
||||||
if (!wasConnected && !terminatedCall.isOutgoing && !rejected && endCallReason != EndCallReason.ANSWERED_ELSEWHERE) {
|
if (!wasConnected && !terminatedCall.isOutgoing && !rejected && endCallReason != EndCallReason.ANSWERED_ELSEWHERE) {
|
||||||
val notification = notificationUtils.buildCallMissedNotification(terminatedCall)
|
val missedCallNotification = notificationUtils.buildCallMissedNotification(terminatedCall)
|
||||||
notificationManager.cancel(callId.hashCode())
|
notificationManager.notify(MISSED_CALL_TAG, terminatedCall.nativeRoomId.hashCode(), missedCallNotification)
|
||||||
notificationManager.notify(MISSED_CALL_TAG, terminatedCall.nativeRoomId.hashCode(), notification)
|
|
||||||
} else {
|
|
||||||
val notification = notificationUtils.buildCallEndedNotification(terminatedCall.isVideoCall)
|
|
||||||
notificationManager.notify(callId.hashCode(), notification)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,7 +233,7 @@ class CallService : VectorService() {
|
||||||
handleUnexpectedState(callId)
|
handleUnexpectedState(callId)
|
||||||
}
|
}
|
||||||
val callInformation = call.toCallInformation()
|
val callInformation = call.toCallInformation()
|
||||||
Timber.v("displayOutgoingCallNotification : display the dedicated notification")
|
Timber.tag(loggerTag.value).v("displayOutgoingCallNotification : display the dedicated notification")
|
||||||
val notification = notificationUtils.buildOutgoingRingingCallNotification(
|
val notification = notificationUtils.buildOutgoingRingingCallNotification(
|
||||||
call = call,
|
call = call,
|
||||||
title = callInformation.opponentMatrixItem?.getBestName() ?: callInformation.opponentUserId
|
title = callInformation.opponentMatrixItem?.getBestName() ?: callInformation.opponentUserId
|
||||||
|
@ -249,7 +250,7 @@ class CallService : VectorService() {
|
||||||
* Display a call in progress notification.
|
* Display a call in progress notification.
|
||||||
*/
|
*/
|
||||||
private fun displayCallInProgressNotification(intent: Intent) {
|
private fun displayCallInProgressNotification(intent: Intent) {
|
||||||
Timber.v("## VOIP displayCallInProgressNotification")
|
Timber.tag(loggerTag.value).v("displayCallInProgressNotification")
|
||||||
val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: ""
|
val callId = intent.getStringExtra(EXTRA_CALL_ID) ?: ""
|
||||||
connectedCallIds.add(callId)
|
connectedCallIds.add(callId)
|
||||||
val call = callManager.getCallById(callId) ?: return Unit.also {
|
val call = callManager.getCallById(callId) ?: return Unit.also {
|
||||||
|
@ -270,7 +271,7 @@ class CallService : VectorService() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleUnexpectedState(callId: String?) {
|
private fun handleUnexpectedState(callId: String?) {
|
||||||
Timber.v("Fallback to clear everything")
|
Timber.tag(loggerTag.value).v("Fallback to clear everything")
|
||||||
callRingPlayerIncoming?.stop()
|
callRingPlayerIncoming?.stop()
|
||||||
callRingPlayerOutgoing?.stop()
|
callRingPlayerOutgoing?.stop()
|
||||||
if (callId != null) {
|
if (callId != null) {
|
||||||
|
|
|
@ -54,6 +54,7 @@ import im.vector.app.features.home.room.detail.RoomDetailArgs
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
|
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||||
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.call.TurnServerResponse
|
import org.matrix.android.sdk.api.session.call.TurnServerResponse
|
||||||
|
@ -71,6 +72,8 @@ data class CallArgs(
|
||||||
val isVideoCall: Boolean
|
val isVideoCall: Boolean
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|
||||||
|
private val loggerTag = LoggerTag("VectorCallActivity", LoggerTag.VOIP)
|
||||||
|
|
||||||
class VectorCallActivity : VectorBaseActivity<ActivityCallBinding>(), CallControlsView.InteractionListener {
|
class VectorCallActivity : VectorBaseActivity<ActivityCallBinding>(), CallControlsView.InteractionListener {
|
||||||
|
|
||||||
override fun getBinding() = ActivityCallBinding.inflate(layoutInflater)
|
override fun getBinding() = ActivityCallBinding.inflate(layoutInflater)
|
||||||
|
@ -113,11 +116,11 @@ class VectorCallActivity : VectorBaseActivity<ActivityCallBinding>(), CallContro
|
||||||
if (intent.hasExtra(MvRx.KEY_ARG)) {
|
if (intent.hasExtra(MvRx.KEY_ARG)) {
|
||||||
callArgs = intent.getParcelableExtra(MvRx.KEY_ARG)!!
|
callArgs = intent.getParcelableExtra(MvRx.KEY_ARG)!!
|
||||||
} else {
|
} else {
|
||||||
Timber.e("## VOIP missing callArgs for VectorCall Activity")
|
Timber.tag(loggerTag.value).e("missing callArgs for VectorCall Activity")
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
Timber.v("## VOIP EXTRA_MODE is ${intent.getStringExtra(EXTRA_MODE)}")
|
Timber.tag(loggerTag.value).v("EXTRA_MODE is ${intent.getStringExtra(EXTRA_MODE)}")
|
||||||
if (intent.getStringExtra(EXTRA_MODE) == INCOMING_RINGING) {
|
if (intent.getStringExtra(EXTRA_MODE) == INCOMING_RINGING) {
|
||||||
turnScreenOnAndKeyguardOff()
|
turnScreenOnAndKeyguardOff()
|
||||||
}
|
}
|
||||||
|
@ -160,7 +163,7 @@ class VectorCallActivity : VectorBaseActivity<ActivityCallBinding>(), CallContro
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderState(state: VectorCallViewState) {
|
private fun renderState(state: VectorCallViewState) {
|
||||||
Timber.v("## VOIP renderState call $state")
|
Timber.tag(loggerTag.value).v("renderState call $state")
|
||||||
if (state.callState is Fail) {
|
if (state.callState is Fail) {
|
||||||
finish()
|
finish()
|
||||||
return
|
return
|
||||||
|
@ -309,7 +312,7 @@ class VectorCallActivity : VectorBaseActivity<ActivityCallBinding>(), CallContro
|
||||||
|
|
||||||
private fun start() {
|
private fun start() {
|
||||||
rootEglBase = EglUtils.rootEglBase ?: return Unit.also {
|
rootEglBase = EglUtils.rootEglBase ?: return Unit.also {
|
||||||
Timber.v("## VOIP rootEglBase is null")
|
Timber.tag(loggerTag.value).v("rootEglBase is null")
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,7 +338,7 @@ class VectorCallActivity : VectorBaseActivity<ActivityCallBinding>(), CallContro
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleViewEvents(event: VectorCallViewEvents?) {
|
private fun handleViewEvents(event: VectorCallViewEvents?) {
|
||||||
Timber.v("## VOIP handleViewEvents $event")
|
Timber.tag(loggerTag.value).v("handleViewEvents $event")
|
||||||
when (event) {
|
when (event) {
|
||||||
VectorCallViewEvents.DismissNoCall -> {
|
VectorCallViewEvents.DismissNoCall -> {
|
||||||
finish()
|
finish()
|
||||||
|
@ -357,7 +360,7 @@ class VectorCallActivity : VectorBaseActivity<ActivityCallBinding>(), CallContro
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onErrorTimoutConnect(turn: TurnServerResponse?) {
|
private fun onErrorTimoutConnect(turn: TurnServerResponse?) {
|
||||||
Timber.d("## VOIP onErrorTimoutConnect $turn")
|
Timber.tag(loggerTag.value).d("onErrorTimoutConnect $turn")
|
||||||
// TODO ask to use default stun, etc...
|
// TODO ask to use default stun, etc...
|
||||||
MaterialAlertDialogBuilder(this)
|
MaterialAlertDialogBuilder(this)
|
||||||
.setTitle(R.string.call_failed_no_connection)
|
.setTitle(R.string.call_failed_no_connection)
|
||||||
|
@ -437,7 +440,7 @@ class VectorCallActivity : VectorBaseActivity<ActivityCallBinding>(), CallContro
|
||||||
|
|
||||||
// Needed to let you answer call when phone is locked
|
// Needed to let you answer call when phone is locked
|
||||||
private fun turnScreenOnAndKeyguardOff() {
|
private fun turnScreenOnAndKeyguardOff() {
|
||||||
Timber.v("## VOIP turnScreenOnAndKeyguardOff")
|
Timber.tag(loggerTag.value).v("turnScreenOnAndKeyguardOff")
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||||
setShowWhenLocked(true)
|
setShowWhenLocked(true)
|
||||||
setTurnScreenOn(true)
|
setTurnScreenOn(true)
|
||||||
|
@ -458,7 +461,7 @@ class VectorCallActivity : VectorBaseActivity<ActivityCallBinding>(), CallContro
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun turnScreenOffAndKeyguardOn() {
|
private fun turnScreenOffAndKeyguardOn() {
|
||||||
Timber.v("## VOIP turnScreenOnAndKeyguardOn")
|
Timber.tag(loggerTag.value).v("turnScreenOnAndKeyguardOn")
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||||
setShowWhenLocked(false)
|
setShowWhenLocked(false)
|
||||||
setTurnScreenOn(false)
|
setTurnScreenOn(false)
|
||||||
|
|
|
@ -25,9 +25,12 @@ import android.media.AudioManager
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import im.vector.app.core.services.BluetoothHeadsetReceiver
|
import im.vector.app.core.services.BluetoothHeadsetReceiver
|
||||||
import im.vector.app.core.services.WiredHeadsetStateReceiver
|
import im.vector.app.core.services.WiredHeadsetStateReceiver
|
||||||
|
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.HashSet
|
import java.util.HashSet
|
||||||
|
|
||||||
|
private val loggerTag = LoggerTag("API21AudioDeviceDetector", LoggerTag.VOIP)
|
||||||
|
|
||||||
internal class API21AudioDeviceDetector(private val context: Context,
|
internal class API21AudioDeviceDetector(private val context: Context,
|
||||||
private val audioManager: AudioManager,
|
private val audioManager: AudioManager,
|
||||||
private val callAudioManager: CallAudioManager
|
private val callAudioManager: CallAudioManager
|
||||||
|
@ -62,17 +65,17 @@ internal class API21AudioDeviceDetector(private val context: Context,
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isBluetoothHeadsetOn(): Boolean {
|
private fun isBluetoothHeadsetOn(): Boolean {
|
||||||
Timber.v("## VOIP: AudioManager isBluetoothHeadsetOn")
|
Timber.tag(loggerTag.value).v("AudioManager isBluetoothHeadsetOn")
|
||||||
try {
|
try {
|
||||||
if (connectedBlueToothHeadset == null) return false.also {
|
if (connectedBlueToothHeadset == null) return false.also {
|
||||||
Timber.v("## VOIP: AudioManager no connected bluetooth headset")
|
Timber.tag(loggerTag.value).v("AudioManager no connected bluetooth headset")
|
||||||
}
|
}
|
||||||
if (!audioManager.isBluetoothScoAvailableOffCall) return false.also {
|
if (!audioManager.isBluetoothScoAvailableOffCall) return false.also {
|
||||||
Timber.v("## VOIP: AudioManager isBluetoothScoAvailableOffCall false")
|
Timber.tag(loggerTag.value).v("AudioManager isBluetoothScoAvailableOffCall false")
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
Timber.e("## VOIP: AudioManager isBluetoothHeadsetOn failure ${failure.localizedMessage}")
|
Timber.e("AudioManager isBluetoothHeadsetOn failure ${failure.localizedMessage}")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,11 +94,11 @@ internal class API21AudioDeviceDetector(private val context: Context,
|
||||||
bluetoothHeadsetStateReceiver = BluetoothHeadsetReceiver.createAndRegister(context, this)
|
bluetoothHeadsetStateReceiver = BluetoothHeadsetReceiver.createAndRegister(context, this)
|
||||||
val bm: BluetoothManager? = context.getSystemService()
|
val bm: BluetoothManager? = context.getSystemService()
|
||||||
val adapter = bm?.adapter
|
val adapter = bm?.adapter
|
||||||
Timber.d("## VOIP Bluetooth adapter $adapter")
|
Timber.tag(loggerTag.value).d("Bluetooth adapter $adapter")
|
||||||
bluetoothAdapter = adapter
|
bluetoothAdapter = adapter
|
||||||
adapter?.getProfileProxy(context, object : BluetoothProfile.ServiceListener {
|
adapter?.getProfileProxy(context, object : BluetoothProfile.ServiceListener {
|
||||||
override fun onServiceDisconnected(profile: Int) {
|
override fun onServiceDisconnected(profile: Int) {
|
||||||
Timber.d("## VOIP onServiceDisconnected $profile")
|
Timber.tag(loggerTag.value).d("onServiceDisconnected $profile")
|
||||||
if (profile == BluetoothProfile.HEADSET) {
|
if (profile == BluetoothProfile.HEADSET) {
|
||||||
connectedBlueToothHeadset = null
|
connectedBlueToothHeadset = null
|
||||||
onAudioDeviceChange()
|
onAudioDeviceChange()
|
||||||
|
@ -103,7 +106,7 @@ internal class API21AudioDeviceDetector(private val context: Context,
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onServiceConnected(profile: Int, proxy: BluetoothProfile?) {
|
override fun onServiceConnected(profile: Int, proxy: BluetoothProfile?) {
|
||||||
Timber.d("## VOIP onServiceConnected $profile , proxy:$proxy")
|
Timber.tag(loggerTag.value).d("onServiceConnected $profile , proxy:$proxy")
|
||||||
if (profile == BluetoothProfile.HEADSET) {
|
if (profile == BluetoothProfile.HEADSET) {
|
||||||
connectedBlueToothHeadset = proxy
|
connectedBlueToothHeadset = proxy
|
||||||
onAudioDeviceChange()
|
onAudioDeviceChange()
|
||||||
|
@ -122,12 +125,12 @@ internal class API21AudioDeviceDetector(private val context: Context,
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onHeadsetEvent(event: WiredHeadsetStateReceiver.HeadsetPlugEvent) {
|
override fun onHeadsetEvent(event: WiredHeadsetStateReceiver.HeadsetPlugEvent) {
|
||||||
Timber.v("onHeadsetEvent $event")
|
Timber.tag(loggerTag.value).v("onHeadsetEvent $event")
|
||||||
onAudioDeviceChange()
|
onAudioDeviceChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBTHeadsetEvent(event: BluetoothHeadsetReceiver.BTHeadsetPlugEvent) {
|
override fun onBTHeadsetEvent(event: BluetoothHeadsetReceiver.BTHeadsetPlugEvent) {
|
||||||
Timber.v("onBTHeadsetEvent $event")
|
Timber.tag(loggerTag.value).v("onBTHeadsetEvent $event")
|
||||||
onAudioDeviceChange()
|
onAudioDeviceChange()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package im.vector.app.features.call.webrtc
|
package im.vector.app.features.call.webrtc
|
||||||
|
|
||||||
|
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||||
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.webrtc.DataChannel
|
import org.webrtc.DataChannel
|
||||||
|
@ -25,10 +26,12 @@ import org.webrtc.PeerConnection
|
||||||
import org.webrtc.RtpReceiver
|
import org.webrtc.RtpReceiver
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
|
private val loggerTag = LoggerTag("PeerConnectionObserver", LoggerTag.VOIP)
|
||||||
|
|
||||||
class PeerConnectionObserver(private val webRtcCall: WebRtcCall) : PeerConnection.Observer {
|
class PeerConnectionObserver(private val webRtcCall: WebRtcCall) : PeerConnection.Observer {
|
||||||
|
|
||||||
override fun onConnectionChange(newState: PeerConnection.PeerConnectionState?) {
|
override fun onConnectionChange(newState: PeerConnection.PeerConnectionState?) {
|
||||||
Timber.v("## VOIP StreamObserver onConnectionChange: $newState")
|
Timber.tag(loggerTag.value).v("StreamObserver onConnectionChange: $newState")
|
||||||
when (newState) {
|
when (newState) {
|
||||||
/**
|
/**
|
||||||
* Every ICE transport used by the connection is either in use (state "connected" or "completed")
|
* Every ICE transport used by the connection is either in use (state "connected" or "completed")
|
||||||
|
@ -79,20 +82,20 @@ class PeerConnectionObserver(private val webRtcCall: WebRtcCall) : PeerConnectio
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onIceCandidate(iceCandidate: IceCandidate) {
|
override fun onIceCandidate(iceCandidate: IceCandidate) {
|
||||||
Timber.v("## VOIP StreamObserver onIceCandidate: $iceCandidate")
|
Timber.tag(loggerTag.value).v("StreamObserver onIceCandidate: $iceCandidate")
|
||||||
webRtcCall.onIceCandidate(iceCandidate)
|
webRtcCall.onIceCandidate(iceCandidate)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDataChannel(dc: DataChannel) {
|
override fun onDataChannel(dc: DataChannel) {
|
||||||
Timber.v("## VOIP StreamObserver onDataChannel: ${dc.state()}")
|
Timber.tag(loggerTag.value).v("StreamObserver onDataChannel: ${dc.state()}")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onIceConnectionReceivingChange(receiving: Boolean) {
|
override fun onIceConnectionReceivingChange(receiving: Boolean) {
|
||||||
Timber.v("## VOIP StreamObserver onIceConnectionReceivingChange: $receiving")
|
Timber.tag(loggerTag.value).v("StreamObserver onIceConnectionReceivingChange: $receiving")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onIceConnectionChange(newState: PeerConnection.IceConnectionState) {
|
override fun onIceConnectionChange(newState: PeerConnection.IceConnectionState) {
|
||||||
Timber.v("## VOIP StreamObserver onIceConnectionChange IceConnectionState:$newState")
|
Timber.tag(loggerTag.value).v("StreamObserver onIceConnectionChange IceConnectionState:$newState")
|
||||||
when (newState) {
|
when (newState) {
|
||||||
/**
|
/**
|
||||||
* the ICE agent is gathering addresses or is waiting to be given remote candidates through
|
* the ICE agent is gathering addresses or is waiting to be given remote candidates through
|
||||||
|
@ -145,29 +148,29 @@ class PeerConnectionObserver(private val webRtcCall: WebRtcCall) : PeerConnectio
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAddStream(stream: MediaStream) {
|
override fun onAddStream(stream: MediaStream) {
|
||||||
Timber.v("## VOIP StreamObserver onAddStream: $stream")
|
Timber.tag(loggerTag.value).v("StreamObserver onAddStream: $stream")
|
||||||
webRtcCall.onAddStream(stream)
|
webRtcCall.onAddStream(stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRemoveStream(stream: MediaStream) {
|
override fun onRemoveStream(stream: MediaStream) {
|
||||||
Timber.v("## VOIP StreamObserver onRemoveStream")
|
Timber.tag(loggerTag.value).v("StreamObserver onRemoveStream")
|
||||||
webRtcCall.onRemoveStream()
|
webRtcCall.onRemoveStream()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onIceGatheringChange(newState: PeerConnection.IceGatheringState) {
|
override fun onIceGatheringChange(newState: PeerConnection.IceGatheringState) {
|
||||||
Timber.v("## VOIP StreamObserver onIceGatheringChange: $newState")
|
Timber.tag(loggerTag.value).v("StreamObserver onIceGatheringChange: $newState")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSignalingChange(newState: PeerConnection.SignalingState) {
|
override fun onSignalingChange(newState: PeerConnection.SignalingState) {
|
||||||
Timber.v("## VOIP StreamObserver onSignalingChange: $newState")
|
Timber.tag(loggerTag.value).v("StreamObserver onSignalingChange: $newState")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onIceCandidatesRemoved(candidates: Array<out IceCandidate>) {
|
override fun onIceCandidatesRemoved(candidates: Array<out IceCandidate>) {
|
||||||
Timber.v("## VOIP StreamObserver onIceCandidatesRemoved: ${candidates.contentToString()}")
|
Timber.tag(loggerTag.value).v("StreamObserver onIceCandidatesRemoved: ${candidates.contentToString()}")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRenegotiationNeeded() {
|
override fun onRenegotiationNeeded() {
|
||||||
Timber.v("## VOIP StreamObserver onRenegotiationNeeded")
|
Timber.tag(loggerTag.value).v("StreamObserver onRenegotiationNeeded")
|
||||||
webRtcCall.onRenegotiationNeeded(restartIce = false)
|
webRtcCall.onRenegotiationNeeded(restartIce = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,6 +181,6 @@ class PeerConnectionObserver(private val webRtcCall: WebRtcCall) : PeerConnectio
|
||||||
* gets a new set of tracks because the media element being captured loaded a new source.
|
* gets a new set of tracks because the media element being captured loaded a new source.
|
||||||
*/
|
*/
|
||||||
override fun onAddTrack(p0: RtpReceiver?, p1: Array<out MediaStream>?) {
|
override fun onAddTrack(p0: RtpReceiver?, p1: Array<out MediaStream>?) {
|
||||||
Timber.v("## VOIP StreamObserver onAddTrack")
|
Timber.tag(loggerTag.value).v("StreamObserver onAddTrack")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,70 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2020 New Vector Ltd
|
|
||||||
*
|
|
||||||
* 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 im.vector.app.features.call.webrtc
|
|
||||||
|
|
||||||
import org.webrtc.DataChannel
|
|
||||||
import org.webrtc.IceCandidate
|
|
||||||
import org.webrtc.MediaStream
|
|
||||||
import org.webrtc.PeerConnection
|
|
||||||
import org.webrtc.RtpReceiver
|
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
abstract class PeerConnectionObserverAdapter : PeerConnection.Observer {
|
|
||||||
override fun onIceCandidate(p0: IceCandidate?) {
|
|
||||||
Timber.v("## VOIP onIceCandidate $p0")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDataChannel(p0: DataChannel?) {
|
|
||||||
Timber.v("## VOIP onDataChannel $p0")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onIceConnectionReceivingChange(p0: Boolean) {
|
|
||||||
Timber.v("## VOIP onIceConnectionReceivingChange $p0")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onIceConnectionChange(p0: PeerConnection.IceConnectionState?) {
|
|
||||||
Timber.v("## VOIP onIceConnectionChange $p0")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onIceGatheringChange(p0: PeerConnection.IceGatheringState?) {
|
|
||||||
Timber.v("## VOIP onIceConnectionChange $p0")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onAddStream(mediaStream: MediaStream?) {
|
|
||||||
Timber.v("## VOIP onAddStream $mediaStream")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSignalingChange(p0: PeerConnection.SignalingState?) {
|
|
||||||
Timber.v("## VOIP onSignalingChange $p0")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onIceCandidatesRemoved(p0: Array<out IceCandidate>?) {
|
|
||||||
Timber.v("## VOIP onIceCandidatesRemoved $p0")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onRemoveStream(mediaStream: MediaStream?) {
|
|
||||||
Timber.v("## VOIP onRemoveStream $mediaStream")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onRenegotiationNeeded() {
|
|
||||||
Timber.v("## VOIP onRenegotiationNeeded")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onAddTrack(p0: RtpReceiver?, p1: Array<out MediaStream>?) {
|
|
||||||
Timber.v("## VOIP onAddTrack $p0 / out: $p1")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -45,6 +45,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.logger.LoggerTag
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.call.CallIdGenerator
|
import org.matrix.android.sdk.api.session.call.CallIdGenerator
|
||||||
import org.matrix.android.sdk.api.session.call.CallState
|
import org.matrix.android.sdk.api.session.call.CallState
|
||||||
|
@ -91,6 +92,8 @@ private const val AUDIO_TRACK_ID = "${STREAM_ID}a0"
|
||||||
private const val VIDEO_TRACK_ID = "${STREAM_ID}v0"
|
private const val VIDEO_TRACK_ID = "${STREAM_ID}v0"
|
||||||
private val DEFAULT_AUDIO_CONSTRAINTS = MediaConstraints()
|
private val DEFAULT_AUDIO_CONSTRAINTS = MediaConstraints()
|
||||||
|
|
||||||
|
private val loggerTag = LoggerTag("WebRtcCall", LoggerTag.VOIP)
|
||||||
|
|
||||||
class WebRtcCall(
|
class WebRtcCall(
|
||||||
val mxCall: MxCall,
|
val mxCall: MxCall,
|
||||||
// This is where the call is placed from an ui perspective.
|
// This is where the call is placed from an ui perspective.
|
||||||
|
@ -195,7 +198,7 @@ class WebRtcCall(
|
||||||
.subscribe {
|
.subscribe {
|
||||||
// omit empty :/
|
// omit empty :/
|
||||||
if (it.isNotEmpty()) {
|
if (it.isNotEmpty()) {
|
||||||
Timber.v("## Sending local ice candidates to call")
|
Timber.tag(loggerTag.value).v("Sending local ice candidates to call")
|
||||||
// it.forEach { peerConnection?.addIceCandidate(it) }
|
// it.forEach { peerConnection?.addIceCandidate(it) }
|
||||||
mxCall.sendLocalCallCandidates(it.mapToCallCandidate())
|
mxCall.sendLocalCallCandidates(it.mapToCallCandidate())
|
||||||
}
|
}
|
||||||
|
@ -213,7 +216,7 @@ class WebRtcCall(
|
||||||
fun onRenegotiationNeeded(restartIce: Boolean) {
|
fun onRenegotiationNeeded(restartIce: Boolean) {
|
||||||
sessionScope?.launch(dispatcher) {
|
sessionScope?.launch(dispatcher) {
|
||||||
if (mxCall.state != CallState.CreateOffer && mxCall.opponentVersion == 0) {
|
if (mxCall.state != CallState.CreateOffer && mxCall.opponentVersion == 0) {
|
||||||
Timber.v("Opponent does not support renegotiation: ignoring onRenegotiationNeeded event")
|
Timber.tag(loggerTag.value).v("Opponent does not support renegotiation: ignoring onRenegotiationNeeded event")
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
val constraints = MediaConstraints()
|
val constraints = MediaConstraints()
|
||||||
|
@ -221,7 +224,7 @@ class WebRtcCall(
|
||||||
constraints.mandatory.add(MediaConstraints.KeyValuePair("IceRestart", "true"))
|
constraints.mandatory.add(MediaConstraints.KeyValuePair("IceRestart", "true"))
|
||||||
}
|
}
|
||||||
val peerConnection = peerConnection ?: return@launch
|
val peerConnection = peerConnection ?: return@launch
|
||||||
Timber.v("## VOIP creating offer...")
|
Timber.tag(loggerTag.value).v("creating offer...")
|
||||||
makingOffer = true
|
makingOffer = true
|
||||||
try {
|
try {
|
||||||
val sessionDescription = peerConnection.awaitCreateOffer(constraints) ?: return@launch
|
val sessionDescription = peerConnection.awaitCreateOffer(constraints) ?: return@launch
|
||||||
|
@ -241,7 +244,7 @@ class WebRtcCall(
|
||||||
}
|
}
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
// Need to handle error properly.
|
// Need to handle error properly.
|
||||||
Timber.v("Failure while creating offer")
|
Timber.tag(loggerTag.value).v("Failure while creating offer")
|
||||||
} finally {
|
} finally {
|
||||||
makingOffer = false
|
makingOffer = false
|
||||||
}
|
}
|
||||||
|
@ -270,7 +273,7 @@ class WebRtcCall(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Timber.v("## VOIP creating peer connection...with iceServers $iceServers ")
|
Timber.tag(loggerTag.value).v("creating peer connection...with iceServers $iceServers ")
|
||||||
val rtcConfig = PeerConnection.RTCConfiguration(iceServers).apply {
|
val rtcConfig = PeerConnection.RTCConfiguration(iceServers).apply {
|
||||||
sdpSemantics = PeerConnection.SdpSemantics.UNIFIED_PLAN
|
sdpSemantics = PeerConnection.SdpSemantics.UNIFIED_PLAN
|
||||||
}
|
}
|
||||||
|
@ -317,7 +320,7 @@ class WebRtcCall(
|
||||||
|
|
||||||
fun acceptIncomingCall() {
|
fun acceptIncomingCall() {
|
||||||
sessionScope?.launch {
|
sessionScope?.launch {
|
||||||
Timber.v("## VOIP acceptIncomingCall from state ${mxCall.state}")
|
Timber.tag(loggerTag.value).v("acceptIncomingCall from state ${mxCall.state}")
|
||||||
if (mxCall.state == CallState.LocalRinging) {
|
if (mxCall.state == CallState.LocalRinging) {
|
||||||
internalAcceptIncomingCall()
|
internalAcceptIncomingCall()
|
||||||
}
|
}
|
||||||
|
@ -336,7 +339,7 @@ class WebRtcCall(
|
||||||
sender.dtmf()?.insertDtmf(digit, 100, 70)
|
sender.dtmf()?.insertDtmf(digit, 100, 70)
|
||||||
return@launch
|
return@launch
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
Timber.v("Fail to send Dtmf digit")
|
Timber.tag(loggerTag.value).v("Fail to send Dtmf digit")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -345,7 +348,7 @@ class WebRtcCall(
|
||||||
|
|
||||||
fun attachViewRenderers(localViewRenderer: SurfaceViewRenderer?, remoteViewRenderer: SurfaceViewRenderer, mode: String?) {
|
fun attachViewRenderers(localViewRenderer: SurfaceViewRenderer?, remoteViewRenderer: SurfaceViewRenderer, mode: String?) {
|
||||||
sessionScope?.launch(dispatcher) {
|
sessionScope?.launch(dispatcher) {
|
||||||
Timber.v("## VOIP attachViewRenderers localRendeder $localViewRenderer / $remoteViewRenderer")
|
Timber.tag(loggerTag.value).v("attachViewRenderers localRendeder $localViewRenderer / $remoteViewRenderer")
|
||||||
localSurfaceRenderers.addIfNeeded(localViewRenderer)
|
localSurfaceRenderers.addIfNeeded(localViewRenderer)
|
||||||
remoteSurfaceRenderers.addIfNeeded(remoteViewRenderer)
|
remoteSurfaceRenderers.addIfNeeded(remoteViewRenderer)
|
||||||
when (mode) {
|
when (mode) {
|
||||||
|
@ -392,7 +395,7 @@ class WebRtcCall(
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun detachRenderersInternal(renderers: List<SurfaceViewRenderer>?) = withContext(dispatcher) {
|
private suspend fun detachRenderersInternal(renderers: List<SurfaceViewRenderer>?) = withContext(dispatcher) {
|
||||||
Timber.v("## VOIP detachRenderers")
|
Timber.tag(loggerTag.value).v("detachRenderers")
|
||||||
if (renderers.isNullOrEmpty()) {
|
if (renderers.isNullOrEmpty()) {
|
||||||
// remove all sinks
|
// remove all sinks
|
||||||
localSurfaceRenderers.forEach {
|
localSurfaceRenderers.forEach {
|
||||||
|
@ -425,12 +428,12 @@ class WebRtcCall(
|
||||||
// 2. Access camera (if video call) + microphone, create local stream
|
// 2. Access camera (if video call) + microphone, create local stream
|
||||||
createLocalStream()
|
createLocalStream()
|
||||||
attachViewRenderersInternal()
|
attachViewRenderersInternal()
|
||||||
Timber.v("## VOIP remoteCandidateSource $remoteCandidateSource")
|
Timber.tag(loggerTag.value).v("remoteCandidateSource $remoteCandidateSource")
|
||||||
remoteIceCandidateDisposable = remoteCandidateSource.subscribe({
|
remoteIceCandidateDisposable = remoteCandidateSource.subscribe({
|
||||||
Timber.v("## VOIP adding remote ice candidate $it")
|
Timber.tag(loggerTag.value).v("adding remote ice candidate $it")
|
||||||
peerConnection?.addIceCandidate(it)
|
peerConnection?.addIceCandidate(it)
|
||||||
}, {
|
}, {
|
||||||
Timber.v("## VOIP failed to add remote ice candidate $it")
|
Timber.tag(loggerTag.value).v("failed to add remote ice candidate $it")
|
||||||
})
|
})
|
||||||
// Now we wait for negotiation callback
|
// Now we wait for negotiation callback
|
||||||
}
|
}
|
||||||
|
@ -456,14 +459,14 @@ class WebRtcCall(
|
||||||
SessionDescription(SessionDescription.Type.OFFER, it)
|
SessionDescription(SessionDescription.Type.OFFER, it)
|
||||||
}
|
}
|
||||||
if (offerSdp == null) {
|
if (offerSdp == null) {
|
||||||
Timber.v("We don't have any offer to process")
|
Timber.tag(loggerTag.value).v("We don't have any offer to process")
|
||||||
return@withContext
|
return@withContext
|
||||||
}
|
}
|
||||||
Timber.v("Offer sdp for invite: ${offerSdp.description}")
|
Timber.tag(loggerTag.value).v("Offer sdp for invite: ${offerSdp.description}")
|
||||||
try {
|
try {
|
||||||
peerConnection?.awaitSetRemoteDescription(offerSdp)
|
peerConnection?.awaitSetRemoteDescription(offerSdp)
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
Timber.v("Failure putting remote description")
|
Timber.tag(loggerTag.value).v("Failure putting remote description")
|
||||||
endCall(reason = EndCallReason.UNKWOWN_ERROR)
|
endCall(reason = EndCallReason.UNKWOWN_ERROR)
|
||||||
return@withContext
|
return@withContext
|
||||||
}
|
}
|
||||||
|
@ -475,12 +478,12 @@ class WebRtcCall(
|
||||||
createAnswer()?.also {
|
createAnswer()?.also {
|
||||||
mxCall.accept(it.description)
|
mxCall.accept(it.description)
|
||||||
}
|
}
|
||||||
Timber.v("## VOIP remoteCandidateSource $remoteCandidateSource")
|
Timber.tag(loggerTag.value).v("remoteCandidateSource $remoteCandidateSource")
|
||||||
remoteIceCandidateDisposable = remoteCandidateSource.subscribe({
|
remoteIceCandidateDisposable = remoteCandidateSource.subscribe({
|
||||||
Timber.v("## VOIP adding remote ice candidate $it")
|
Timber.tag(loggerTag.value).v("adding remote ice candidate $it")
|
||||||
peerConnection?.addIceCandidate(it)
|
peerConnection?.addIceCandidate(it)
|
||||||
}, {
|
}, {
|
||||||
Timber.v("## VOIP failed to add remote ice candidate $it")
|
Timber.tag(loggerTag.value).v("failed to add remote ice candidate $it")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -492,7 +495,7 @@ class WebRtcCall(
|
||||||
|
|
||||||
private fun createLocalStream() {
|
private fun createLocalStream() {
|
||||||
val peerConnectionFactory = peerConnectionFactoryProvider.get() ?: return
|
val peerConnectionFactory = peerConnectionFactoryProvider.get() ?: return
|
||||||
Timber.v("Create local stream for call ${mxCall.callId}")
|
Timber.tag(loggerTag.value).v("Create local stream for call ${mxCall.callId}")
|
||||||
configureAudioTrack(peerConnectionFactory)
|
configureAudioTrack(peerConnectionFactory)
|
||||||
// add video track if needed
|
// add video track if needed
|
||||||
if (mxCall.isVideoCall) {
|
if (mxCall.isVideoCall) {
|
||||||
|
@ -505,7 +508,7 @@ class WebRtcCall(
|
||||||
val audioSource = peerConnectionFactory.createAudioSource(DEFAULT_AUDIO_CONSTRAINTS)
|
val audioSource = peerConnectionFactory.createAudioSource(DEFAULT_AUDIO_CONSTRAINTS)
|
||||||
val audioTrack = peerConnectionFactory.createAudioTrack(AUDIO_TRACK_ID, audioSource)
|
val audioTrack = peerConnectionFactory.createAudioTrack(AUDIO_TRACK_ID, audioSource)
|
||||||
audioTrack.setEnabled(true)
|
audioTrack.setEnabled(true)
|
||||||
Timber.v("Add audio track $AUDIO_TRACK_ID to call ${mxCall.callId}")
|
Timber.tag(loggerTag.value).v("Add audio track $AUDIO_TRACK_ID to call ${mxCall.callId}")
|
||||||
peerConnection?.addTrack(audioTrack, listOf(STREAM_ID))
|
peerConnection?.addTrack(audioTrack, listOf(STREAM_ID))
|
||||||
localAudioSource = audioSource
|
localAudioSource = audioSource
|
||||||
localAudioTrack = audioTrack
|
localAudioTrack = audioTrack
|
||||||
|
@ -547,7 +550,7 @@ class WebRtcCall(
|
||||||
|
|
||||||
override fun onCameraClosed() {
|
override fun onCameraClosed() {
|
||||||
super.onCameraClosed()
|
super.onCameraClosed()
|
||||||
Timber.v("onCameraClosed")
|
Timber.tag(loggerTag.value).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
|
||||||
|
@ -555,16 +558,16 @@ class WebRtcCall(
|
||||||
cameraAvailabilityCallback = object : CameraManager.AvailabilityCallback() {
|
cameraAvailabilityCallback = object : CameraManager.AvailabilityCallback() {
|
||||||
override fun onCameraUnavailable(cameraId: String) {
|
override fun onCameraUnavailable(cameraId: String) {
|
||||||
super.onCameraUnavailable(cameraId)
|
super.onCameraUnavailable(cameraId)
|
||||||
Timber.v("On camera unavailable: $cameraId")
|
Timber.tag(loggerTag.value).v("On camera unavailable: $cameraId")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCameraAccessPrioritiesChanged() {
|
override fun onCameraAccessPrioritiesChanged() {
|
||||||
super.onCameraAccessPrioritiesChanged()
|
super.onCameraAccessPrioritiesChanged()
|
||||||
Timber.v("onCameraAccessPrioritiesChanged")
|
Timber.tag(loggerTag.value).v("onCameraAccessPrioritiesChanged")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCameraAvailable(cameraId: String) {
|
override fun onCameraAvailable(cameraId: String) {
|
||||||
Timber.v("On camera available: $cameraId")
|
Timber.tag(loggerTag.value).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)
|
||||||
|
@ -577,7 +580,7 @@ class WebRtcCall(
|
||||||
|
|
||||||
val videoSource = peerConnectionFactory.createVideoSource(videoCapturer.isScreencast)
|
val videoSource = peerConnectionFactory.createVideoSource(videoCapturer.isScreencast)
|
||||||
val surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", rootEglBase!!.eglBaseContext)
|
val surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", rootEglBase!!.eglBaseContext)
|
||||||
Timber.v("## VOIP Local video source created")
|
Timber.tag(loggerTag.value).v("Local video source created")
|
||||||
|
|
||||||
videoCapturer.initialize(surfaceTextureHelper, context, videoSource.capturerObserver)
|
videoCapturer.initialize(surfaceTextureHelper, context, videoSource.capturerObserver)
|
||||||
// HD
|
// HD
|
||||||
|
@ -585,7 +588,7 @@ class WebRtcCall(
|
||||||
this.videoCapturer = videoCapturer
|
this.videoCapturer = videoCapturer
|
||||||
|
|
||||||
val videoTrack = peerConnectionFactory.createVideoTrack(VIDEO_TRACK_ID, videoSource)
|
val videoTrack = peerConnectionFactory.createVideoTrack(VIDEO_TRACK_ID, videoSource)
|
||||||
Timber.v("Add video track $VIDEO_TRACK_ID to call ${mxCall.callId}")
|
Timber.tag(loggerTag.value).v("Add video track $VIDEO_TRACK_ID to call ${mxCall.callId}")
|
||||||
videoTrack.setEnabled(true)
|
videoTrack.setEnabled(true)
|
||||||
peerConnection?.addTrack(videoTrack, listOf(STREAM_ID))
|
peerConnection?.addTrack(videoTrack, listOf(STREAM_ID))
|
||||||
localVideoSource = videoSource
|
localVideoSource = videoSource
|
||||||
|
@ -595,7 +598,7 @@ class WebRtcCall(
|
||||||
|
|
||||||
fun setCaptureFormat(format: CaptureFormat) {
|
fun setCaptureFormat(format: CaptureFormat) {
|
||||||
sessionScope?.launch(dispatcher) {
|
sessionScope?.launch(dispatcher) {
|
||||||
Timber.v("## VOIP setCaptureFormat $format")
|
Timber.tag(loggerTag.value).v("setCaptureFormat $format")
|
||||||
videoCapturer?.changeCaptureFormat(format.width, format.height, format.fps)
|
videoCapturer?.changeCaptureFormat(format.width, format.height, format.fps)
|
||||||
currentCaptureFormat = format
|
currentCaptureFormat = format
|
||||||
}
|
}
|
||||||
|
@ -689,14 +692,14 @@ class WebRtcCall(
|
||||||
|
|
||||||
fun switchCamera() {
|
fun switchCamera() {
|
||||||
sessionScope?.launch(dispatcher) {
|
sessionScope?.launch(dispatcher) {
|
||||||
Timber.v("## VOIP switchCamera")
|
Timber.tag(loggerTag.value).v("switchCamera")
|
||||||
if (mxCall.state is CallState.Connected && mxCall.isVideoCall) {
|
if (mxCall.state is CallState.Connected && mxCall.isVideoCall) {
|
||||||
val oppositeCamera = getOppositeCameraIfAny() ?: return@launch
|
val oppositeCamera = getOppositeCameraIfAny() ?: return@launch
|
||||||
videoCapturer?.switchCamera(
|
videoCapturer?.switchCamera(
|
||||||
object : CameraVideoCapturer.CameraSwitchHandler {
|
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.tag(loggerTag.value).v("onCameraSwitchDone isFront $isFrontCamera")
|
||||||
cameraInUse = oppositeCamera
|
cameraInUse = oppositeCamera
|
||||||
localSurfaceRenderers.forEach {
|
localSurfaceRenderers.forEach {
|
||||||
it.get()?.setMirror(isFrontCamera)
|
it.get()?.setMirror(isFrontCamera)
|
||||||
|
@ -707,7 +710,7 @@ class WebRtcCall(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCameraSwitchError(errorDescription: String?) {
|
override fun onCameraSwitchError(errorDescription: String?) {
|
||||||
Timber.v("## VOIP onCameraSwitchError isFront $errorDescription")
|
Timber.tag(loggerTag.value).v("onCameraSwitchError isFront $errorDescription")
|
||||||
}
|
}
|
||||||
}, oppositeCamera.name
|
}, oppositeCamera.name
|
||||||
)
|
)
|
||||||
|
@ -716,7 +719,7 @@ class WebRtcCall(
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun createAnswer(): SessionDescription? {
|
private suspend fun createAnswer(): SessionDescription? {
|
||||||
Timber.w("## VOIP createAnswer")
|
Timber.tag(loggerTag.value).w("createAnswer")
|
||||||
val peerConnection = peerConnection ?: return null
|
val peerConnection = peerConnection ?: return null
|
||||||
val constraints = MediaConstraints().apply {
|
val constraints = MediaConstraints().apply {
|
||||||
mandatory.add(MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"))
|
mandatory.add(MediaConstraints.KeyValuePair("OfferToReceiveAudio", "true"))
|
||||||
|
@ -727,7 +730,7 @@ class WebRtcCall(
|
||||||
peerConnection.awaitSetLocalDescription(localDescription)
|
peerConnection.awaitSetLocalDescription(localDescription)
|
||||||
localDescription
|
localDescription
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
Timber.v("Fail to create answer")
|
Timber.tag(loggerTag.value).v("Fail to create answer")
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -768,7 +771,7 @@ class WebRtcCall(
|
||||||
sessionScope?.launch(dispatcher) {
|
sessionScope?.launch(dispatcher) {
|
||||||
// reportError("Weird-looking stream: " + stream);
|
// reportError("Weird-looking stream: " + stream);
|
||||||
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.tag(loggerTag.value).e("StreamObserver weird looking stream: $stream")
|
||||||
// TODO maybe do something more??
|
// TODO maybe do something more??
|
||||||
endCall(EndCallReason.UNKWOWN_ERROR)
|
endCall(EndCallReason.UNKWOWN_ERROR)
|
||||||
return@launch
|
return@launch
|
||||||
|
@ -834,7 +837,7 @@ class WebRtcCall(
|
||||||
if (it.sdpMid.isNullOrEmpty() || it.candidate.isNullOrEmpty()) {
|
if (it.sdpMid.isNullOrEmpty() || it.candidate.isNullOrEmpty()) {
|
||||||
return@forEach
|
return@forEach
|
||||||
}
|
}
|
||||||
Timber.v("## VOIP onCallIceCandidateReceived for call ${mxCall.callId} sdp: ${it.candidate}")
|
Timber.tag(loggerTag.value).v("onCallIceCandidateReceived for call ${mxCall.callId} sdp: ${it.candidate}")
|
||||||
val iceCandidate = IceCandidate(it.sdpMid, it.sdpMLineIndex, it.candidate)
|
val iceCandidate = IceCandidate(it.sdpMid, it.sdpMLineIndex, it.candidate)
|
||||||
remoteCandidateSource.onNext(iceCandidate)
|
remoteCandidateSource.onNext(iceCandidate)
|
||||||
}
|
}
|
||||||
|
@ -843,7 +846,7 @@ class WebRtcCall(
|
||||||
|
|
||||||
fun onCallAnswerReceived(callAnswerContent: CallAnswerContent) {
|
fun onCallAnswerReceived(callAnswerContent: CallAnswerContent) {
|
||||||
sessionScope?.launch(dispatcher) {
|
sessionScope?.launch(dispatcher) {
|
||||||
Timber.v("## VOIP onCallAnswerReceived ${callAnswerContent.callId}")
|
Timber.tag(loggerTag.value).v("onCallAnswerReceived ${callAnswerContent.callId}")
|
||||||
val sdp = SessionDescription(SessionDescription.Type.ANSWER, callAnswerContent.answer.sdp)
|
val sdp = SessionDescription(SessionDescription.Type.ANSWER, callAnswerContent.answer.sdp)
|
||||||
try {
|
try {
|
||||||
peerConnection?.awaitSetRemoteDescription(sdp)
|
peerConnection?.awaitSetRemoteDescription(sdp)
|
||||||
|
@ -863,7 +866,7 @@ class WebRtcCall(
|
||||||
val type = description?.type
|
val type = description?.type
|
||||||
val sdpText = description?.sdp
|
val sdpText = description?.sdp
|
||||||
if (type == null || sdpText == null) {
|
if (type == null || sdpText == null) {
|
||||||
Timber.i("Ignoring invalid m.call.negotiate event")
|
Timber.tag(loggerTag.value).i("Ignoring invalid m.call.negotiate event")
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
val peerConnection = peerConnection ?: return@launch
|
val peerConnection = peerConnection ?: return@launch
|
||||||
|
@ -878,7 +881,7 @@ class WebRtcCall(
|
||||||
|
|
||||||
ignoreOffer = !polite && offerCollision
|
ignoreOffer = !polite && offerCollision
|
||||||
if (ignoreOffer) {
|
if (ignoreOffer) {
|
||||||
Timber.i("Ignoring colliding negotiate event because we're impolite")
|
Timber.tag(loggerTag.value).i("Ignoring colliding negotiate event because we're impolite")
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
val prevOnHold = computeIsLocalOnHold()
|
val prevOnHold = computeIsLocalOnHold()
|
||||||
|
@ -891,7 +894,7 @@ class WebRtcCall(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
Timber.e(failure, "Failed to complete negotiation")
|
Timber.tag(loggerTag.value).e(failure, "Failed to complete negotiation")
|
||||||
}
|
}
|
||||||
val nowOnHold = computeIsLocalOnHold()
|
val nowOnHold = computeIsLocalOnHold()
|
||||||
wasLocalOnHold = nowOnHold
|
wasLocalOnHold = nowOnHold
|
||||||
|
@ -937,7 +940,7 @@ class WebRtcCall(
|
||||||
val session = sessionProvider.get() ?: return@launch
|
val session = sessionProvider.get() ?: return@launch
|
||||||
val newAssertedIdentity = callAssertedIdentityContent.assertedIdentity ?: return@launch
|
val newAssertedIdentity = callAssertedIdentityContent.assertedIdentity ?: return@launch
|
||||||
if (newAssertedIdentity.id == null && newAssertedIdentity.displayName == null) {
|
if (newAssertedIdentity.id == null && newAssertedIdentity.displayName == null) {
|
||||||
Timber.v("Asserted identity received with no relevant information, skip")
|
Timber.tag(loggerTag.value).v("Asserted identity received with no relevant information, skip")
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
remoteAssertedIdentity = newAssertedIdentity
|
remoteAssertedIdentity = newAssertedIdentity
|
||||||
|
|
|
@ -35,6 +35,7 @@ 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
|
||||||
|
import org.matrix.android.sdk.api.logger.LoggerTag
|
||||||
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
|
||||||
import org.matrix.android.sdk.api.session.call.CallState
|
import org.matrix.android.sdk.api.session.call.CallState
|
||||||
|
@ -63,6 +64,8 @@ import javax.inject.Singleton
|
||||||
* Manage peerConnectionFactory & Peer connections outside of activity lifecycle to resist configuration changes
|
* Manage peerConnectionFactory & Peer connections outside of activity lifecycle to resist configuration changes
|
||||||
* Use app context
|
* Use app context
|
||||||
*/
|
*/
|
||||||
|
private val loggerTag = LoggerTag("WebRtcCallManager", LoggerTag.VOIP)
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class WebRtcCallManager @Inject constructor(
|
class WebRtcCallManager @Inject constructor(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
|
@ -190,7 +193,7 @@ class WebRtcCallManager @Inject constructor(
|
||||||
fun getAdvertisedCalls() = advertisedCalls
|
fun getAdvertisedCalls() = advertisedCalls
|
||||||
|
|
||||||
fun headSetButtonTapped() {
|
fun headSetButtonTapped() {
|
||||||
Timber.v("## VOIP headSetButtonTapped")
|
Timber.tag(loggerTag.value).v("headSetButtonTapped")
|
||||||
val call = getCurrentCall() ?: return
|
val call = getCurrentCall() ?: return
|
||||||
if (call.mxCall.state is CallState.LocalRinging) {
|
if (call.mxCall.state is CallState.LocalRinging) {
|
||||||
call.acceptIncomingCall()
|
call.acceptIncomingCall()
|
||||||
|
@ -203,12 +206,12 @@ class WebRtcCallManager @Inject constructor(
|
||||||
|
|
||||||
private fun createPeerConnectionFactoryIfNeeded() {
|
private fun createPeerConnectionFactoryIfNeeded() {
|
||||||
if (peerConnectionFactory != null) return
|
if (peerConnectionFactory != null) return
|
||||||
Timber.v("## VOIP createPeerConnectionFactory")
|
Timber.tag(loggerTag.value).v("createPeerConnectionFactory")
|
||||||
val eglBaseContext = rootEglBase?.eglBaseContext ?: return Unit.also {
|
val eglBaseContext = rootEglBase?.eglBaseContext ?: return Unit.also {
|
||||||
Timber.e("## VOIP No EGL BASE")
|
Timber.tag(loggerTag.value).e("No EGL BASE")
|
||||||
}
|
}
|
||||||
|
|
||||||
Timber.v("## VOIP PeerConnectionFactory.initialize")
|
Timber.tag(loggerTag.value).v("PeerConnectionFactory.initialize")
|
||||||
PeerConnectionFactory.initialize(PeerConnectionFactory
|
PeerConnectionFactory.initialize(PeerConnectionFactory
|
||||||
.InitializationOptions.builder(context.applicationContext)
|
.InitializationOptions.builder(context.applicationContext)
|
||||||
.createInitializationOptions()
|
.createInitializationOptions()
|
||||||
|
@ -222,7 +225,7 @@ class WebRtcCallManager @Inject constructor(
|
||||||
/* enableH264HighProfile */
|
/* enableH264HighProfile */
|
||||||
true)
|
true)
|
||||||
val defaultVideoDecoderFactory = DefaultVideoDecoderFactory(eglBaseContext)
|
val defaultVideoDecoderFactory = DefaultVideoDecoderFactory(eglBaseContext)
|
||||||
Timber.v("## VOIP PeerConnectionFactory.createPeerConnectionFactory ...")
|
Timber.tag(loggerTag.value).v("PeerConnectionFactory.createPeerConnectionFactory ...")
|
||||||
peerConnectionFactory = PeerConnectionFactory.builder()
|
peerConnectionFactory = PeerConnectionFactory.builder()
|
||||||
.setOptions(options)
|
.setOptions(options)
|
||||||
.setVideoEncoderFactory(defaultVideoEncoderFactory)
|
.setVideoEncoderFactory(defaultVideoEncoderFactory)
|
||||||
|
@ -231,7 +234,7 @@ class WebRtcCallManager @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onCallActive(call: WebRtcCall) {
|
private fun onCallActive(call: WebRtcCall) {
|
||||||
Timber.v("## VOIP WebRtcPeerConnectionManager onCall active: ${call.mxCall.callId}")
|
Timber.tag(loggerTag.value).v("WebRtcPeerConnectionManager onCall active: ${call.mxCall.callId}")
|
||||||
val currentCall = getCurrentCall().takeIf { it != call }
|
val currentCall = getCurrentCall().takeIf { it != call }
|
||||||
currentCall?.updateRemoteOnHold(onHold = true)
|
currentCall?.updateRemoteOnHold(onHold = true)
|
||||||
audioManager.setMode(if (call.mxCall.isVideoCall) CallAudioManager.Mode.VIDEO_CALL else CallAudioManager.Mode.AUDIO_CALL)
|
audioManager.setMode(if (call.mxCall.isVideoCall) CallAudioManager.Mode.VIDEO_CALL else CallAudioManager.Mode.AUDIO_CALL)
|
||||||
|
@ -239,9 +242,9 @@ class WebRtcCallManager @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onCallEnded(callId: String, endCallReason: EndCallReason, rejected: Boolean) {
|
private fun onCallEnded(callId: String, endCallReason: EndCallReason, rejected: Boolean) {
|
||||||
Timber.v("## VOIP WebRtcPeerConnectionManager onCall ended: $callId")
|
Timber.tag(loggerTag.value).v("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.tag(loggerTag.value).v("On call ended for unknown call $callId")
|
||||||
}
|
}
|
||||||
CallService.onCallTerminated(context, callId, endCallReason, rejected)
|
CallService.onCallTerminated(context, callId, endCallReason, rejected)
|
||||||
callsByRoomId[webRtcCall.signalingRoomId]?.remove(webRtcCall)
|
callsByRoomId[webRtcCall.signalingRoomId]?.remove(webRtcCall)
|
||||||
|
@ -253,7 +256,7 @@ class WebRtcCallManager @Inject constructor(
|
||||||
}
|
}
|
||||||
// There is no active calls
|
// There is no active calls
|
||||||
if (getCurrentCall() == null) {
|
if (getCurrentCall() == null) {
|
||||||
Timber.v("## VOIP Dispose peerConnectionFactory as there is no need to keep one")
|
Timber.tag(loggerTag.value).v("Dispose peerConnectionFactory as there is no need to keep one")
|
||||||
peerConnectionFactory?.dispose()
|
peerConnectionFactory?.dispose()
|
||||||
peerConnectionFactory = null
|
peerConnectionFactory = null
|
||||||
audioManager.setMode(CallAudioManager.Mode.DEFAULT)
|
audioManager.setMode(CallAudioManager.Mode.DEFAULT)
|
||||||
|
@ -271,13 +274,13 @@ class WebRtcCallManager @Inject constructor(
|
||||||
|
|
||||||
suspend fun startOutgoingCall(nativeRoomId: String, otherUserId: String, isVideoCall: Boolean, transferee: WebRtcCall? = null) {
|
suspend fun startOutgoingCall(nativeRoomId: String, otherUserId: String, isVideoCall: Boolean, transferee: WebRtcCall? = null) {
|
||||||
val signalingRoomId = callUserMapper?.getOrCreateVirtualRoomForRoom(nativeRoomId, otherUserId) ?: nativeRoomId
|
val signalingRoomId = callUserMapper?.getOrCreateVirtualRoomForRoom(nativeRoomId, otherUserId) ?: nativeRoomId
|
||||||
Timber.v("## VOIP startOutgoingCall in room $signalingRoomId to $otherUserId isVideo $isVideoCall")
|
Timber.tag(loggerTag.value).v("startOutgoingCall in room $signalingRoomId to $otherUserId isVideo $isVideoCall")
|
||||||
if (getCallsByRoomId(nativeRoomId).isNotEmpty()) {
|
if (getCallsByRoomId(nativeRoomId).isNotEmpty()) {
|
||||||
Timber.w("## VOIP you already have a call in this room")
|
Timber.tag(loggerTag.value).w("you already have a call in this room")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (getCurrentCall() != null && getCurrentCall()?.mxCall?.state !is CallState.Connected || getCalls().size >= 2) {
|
if (getCurrentCall() != null && getCurrentCall()?.mxCall?.state !is CallState.Connected || getCalls().size >= 2) {
|
||||||
Timber.w("## VOIP cannot start outgoing call")
|
Timber.tag(loggerTag.value).w("cannot start outgoing call")
|
||||||
// Just ignore, maybe we could answer from other session?
|
// Just ignore, maybe we could answer from other session?
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -300,10 +303,10 @@ class WebRtcCallManager @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCallIceCandidateReceived(mxCall: MxCall, iceCandidatesContent: CallCandidatesContent) {
|
override fun onCallIceCandidateReceived(mxCall: MxCall, iceCandidatesContent: CallCandidatesContent) {
|
||||||
Timber.v("## VOIP onCallIceCandidateReceived for call ${mxCall.callId}")
|
Timber.tag(loggerTag.value).v("onCallIceCandidateReceived for call ${mxCall.callId}")
|
||||||
val call = callsByCallId[iceCandidatesContent.callId]
|
val call = callsByCallId[iceCandidatesContent.callId]
|
||||||
?: return Unit.also {
|
?: return Unit.also {
|
||||||
Timber.w("onCallIceCandidateReceived for non active call? ${iceCandidatesContent.callId}")
|
Timber.tag(loggerTag.value).w("onCallIceCandidateReceived for non active call? ${iceCandidatesContent.callId}")
|
||||||
}
|
}
|
||||||
call.onCallIceCandidateReceived(iceCandidatesContent)
|
call.onCallIceCandidateReceived(iceCandidatesContent)
|
||||||
}
|
}
|
||||||
|
@ -340,14 +343,14 @@ class WebRtcCallManager @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCallInviteReceived(mxCall: MxCall, callInviteContent: CallInviteContent) {
|
override fun onCallInviteReceived(mxCall: MxCall, callInviteContent: CallInviteContent) {
|
||||||
Timber.v("## VOIP onCallInviteReceived callId ${mxCall.callId}")
|
Timber.tag(loggerTag.value).v("onCallInviteReceived callId ${mxCall.callId}")
|
||||||
val nativeRoomId = callUserMapper?.nativeRoomForVirtualRoom(mxCall.roomId) ?: mxCall.roomId
|
val nativeRoomId = callUserMapper?.nativeRoomForVirtualRoom(mxCall.roomId) ?: mxCall.roomId
|
||||||
if (getCallsByRoomId(nativeRoomId).isNotEmpty()) {
|
if (getCallsByRoomId(nativeRoomId).isNotEmpty()) {
|
||||||
Timber.w("## VOIP you already have a call in this room")
|
Timber.tag(loggerTag.value).w("you already have a call in this room")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if ((getCurrentCall() != null && getCurrentCall()?.mxCall?.state !is CallState.Connected) || getCalls().size >= 2) {
|
if ((getCurrentCall() != null && getCurrentCall()?.mxCall?.state !is CallState.Connected) || getCalls().size >= 2) {
|
||||||
Timber.w("## VOIP receiving incoming call but cannot handle it")
|
Timber.tag(loggerTag.value).w("receiving incoming call but cannot handle it")
|
||||||
// Just ignore, maybe we could answer from other session?
|
// Just ignore, maybe we could answer from other session?
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -376,7 +379,7 @@ class WebRtcCallManager @Inject constructor(
|
||||||
override fun onCallAnswerReceived(callAnswerContent: CallAnswerContent) {
|
override fun onCallAnswerReceived(callAnswerContent: CallAnswerContent) {
|
||||||
val call = callsByCallId[callAnswerContent.callId]
|
val call = callsByCallId[callAnswerContent.callId]
|
||||||
?: return Unit.also {
|
?: return Unit.also {
|
||||||
Timber.w("onCallAnswerReceived for non active call? ${callAnswerContent.callId}")
|
Timber.tag(loggerTag.value).w("onCallAnswerReceived for non active call? ${callAnswerContent.callId}")
|
||||||
}
|
}
|
||||||
val mxCall = call.mxCall
|
val mxCall = call.mxCall
|
||||||
// Update service state
|
// Update service state
|
||||||
|
@ -390,7 +393,7 @@ class WebRtcCallManager @Inject constructor(
|
||||||
override fun onCallHangupReceived(callHangupContent: CallHangupContent) {
|
override fun onCallHangupReceived(callHangupContent: CallHangupContent) {
|
||||||
val call = callsByCallId[callHangupContent.callId]
|
val call = callsByCallId[callHangupContent.callId]
|
||||||
?: return Unit.also {
|
?: return Unit.also {
|
||||||
Timber.w("onCallHangupReceived for non active call? ${callHangupContent.callId}")
|
Timber.tag(loggerTag.value).w("onCallHangupReceived for non active call? ${callHangupContent.callId}")
|
||||||
}
|
}
|
||||||
call.onCallHangupReceived(callHangupContent)
|
call.onCallHangupReceived(callHangupContent)
|
||||||
}
|
}
|
||||||
|
@ -398,7 +401,7 @@ class WebRtcCallManager @Inject constructor(
|
||||||
override fun onCallRejectReceived(callRejectContent: CallRejectContent) {
|
override fun onCallRejectReceived(callRejectContent: CallRejectContent) {
|
||||||
val call = callsByCallId[callRejectContent.callId]
|
val call = callsByCallId[callRejectContent.callId]
|
||||||
?: return Unit.also {
|
?: return Unit.also {
|
||||||
Timber.w("onCallRejectReceived for non active call? ${callRejectContent.callId}")
|
Timber.tag(loggerTag.value).w("onCallRejectReceived for non active call? ${callRejectContent.callId}")
|
||||||
}
|
}
|
||||||
call.onCallRejectReceived(callRejectContent)
|
call.onCallRejectReceived(callRejectContent)
|
||||||
}
|
}
|
||||||
|
@ -406,7 +409,7 @@ class WebRtcCallManager @Inject constructor(
|
||||||
override fun onCallSelectAnswerReceived(callSelectAnswerContent: CallSelectAnswerContent) {
|
override fun onCallSelectAnswerReceived(callSelectAnswerContent: CallSelectAnswerContent) {
|
||||||
val call = callsByCallId[callSelectAnswerContent.callId]
|
val call = callsByCallId[callSelectAnswerContent.callId]
|
||||||
?: return Unit.also {
|
?: return Unit.also {
|
||||||
Timber.w("onCallSelectAnswerReceived for non active call? ${callSelectAnswerContent.callId}")
|
Timber.tag(loggerTag.value).w("onCallSelectAnswerReceived for non active call? ${callSelectAnswerContent.callId}")
|
||||||
}
|
}
|
||||||
call.onCallSelectedAnswerReceived(callSelectAnswerContent)
|
call.onCallSelectedAnswerReceived(callSelectAnswerContent)
|
||||||
}
|
}
|
||||||
|
@ -414,13 +417,13 @@ class WebRtcCallManager @Inject constructor(
|
||||||
override fun onCallNegotiateReceived(callNegotiateContent: CallNegotiateContent) {
|
override fun onCallNegotiateReceived(callNegotiateContent: CallNegotiateContent) {
|
||||||
val call = callsByCallId[callNegotiateContent.callId]
|
val call = callsByCallId[callNegotiateContent.callId]
|
||||||
?: return Unit.also {
|
?: return Unit.also {
|
||||||
Timber.w("onCallNegotiateReceived for non active call? ${callNegotiateContent.callId}")
|
Timber.tag(loggerTag.value).w("onCallNegotiateReceived for non active call? ${callNegotiateContent.callId}")
|
||||||
}
|
}
|
||||||
call.onCallNegotiateReceived(callNegotiateContent)
|
call.onCallNegotiateReceived(callNegotiateContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCallManagedByOtherSession(callId: String) {
|
override fun onCallManagedByOtherSession(callId: String) {
|
||||||
Timber.v("## VOIP onCallManagedByOtherSession: $callId")
|
Timber.tag(loggerTag.value).v("onCallManagedByOtherSession: $callId")
|
||||||
onCallEnded(callId, EndCallReason.ANSWERED_ELSEWHERE, false)
|
onCallEnded(callId, EndCallReason.ANSWERED_ELSEWHERE, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,8 +433,8 @@ class WebRtcCallManager @Inject constructor(
|
||||||
}
|
}
|
||||||
val call = callsByCallId[callAssertedIdentityContent.callId]
|
val call = callsByCallId[callAssertedIdentityContent.callId]
|
||||||
?: return Unit.also {
|
?: return Unit.also {
|
||||||
Timber.w("onCallAssertedIdentityReceived for non active call? ${callAssertedIdentityContent.callId}")
|
Timber.tag(loggerTag.value).w("onCallAssertedIdentityReceived for non active call? ${callAssertedIdentityContent.callId}")
|
||||||
}
|
}
|
||||||
call.onCallAssertedIdentityReceived(callAssertedIdentityContent)
|
call.onCallAssertedIdentityReceived(callAssertedIdentityContent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -468,7 +468,8 @@ class NotificationUtils @Inject constructor(private val context: Context,
|
||||||
setSmallIcon(R.drawable.ic_call_answer)
|
setSmallIcon(R.drawable.ic_call_answer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.setTimeoutAfter(2000)
|
// This is a trick to make the previous notification with same id disappear as cancel notification is not working with Foreground Service.
|
||||||
|
.setTimeoutAfter(1)
|
||||||
.setColor(ThemeUtils.getColor(context, android.R.attr.colorPrimary))
|
.setColor(ThemeUtils.getColor(context, android.R.attr.colorPrimary))
|
||||||
.setCategory(NotificationCompat.CATEGORY_CALL)
|
.setCategory(NotificationCompat.CATEGORY_CALL)
|
||||||
.build()
|
.build()
|
||||||
|
|
Loading…
Reference in a new issue