mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-25 02:45:53 +03:00
Merge pull request #1993 from vector-im/feature/fix_call_in_background
Don't pause the sync thread if there is an active or pending call.
This commit is contained in:
commit
fbcc7aa211
5 changed files with 94 additions and 13 deletions
|
@ -16,6 +16,7 @@ Bugfix 🐛:
|
||||||
- Fix bad color for settings icon on Android < 24 (#1786)
|
- Fix bad color for settings icon on Android < 24 (#1786)
|
||||||
- Change user or room avatar: when selecting Gallery, I'm not proposed to crop the selected image (#1590)
|
- Change user or room avatar: when selecting Gallery, I'm not proposed to crop the selected image (#1590)
|
||||||
- Fix uploads still don't work with room v6 (#1879)
|
- Fix uploads still don't work with room v6 (#1879)
|
||||||
|
- Can't handle ongoing call events in background (#1992)
|
||||||
|
|
||||||
Translations 🗣:
|
Translations 🗣:
|
||||||
-
|
-
|
||||||
|
|
|
@ -34,4 +34,6 @@ interface CallSignalingService {
|
||||||
fun removeCallListener(listener: CallsListener)
|
fun removeCallListener(listener: CallsListener)
|
||||||
|
|
||||||
fun getCallWithId(callId: String) : MxCall?
|
fun getCallWithId(callId: String) : MxCall?
|
||||||
|
|
||||||
|
fun isThereAnyActiveCall(): Boolean
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* 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 org.matrix.android.sdk.internal.session.call
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import org.matrix.android.sdk.api.session.call.MxCall
|
||||||
|
import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@SessionScope
|
||||||
|
internal class ActiveCallHandler @Inject constructor() {
|
||||||
|
|
||||||
|
private val activeCallListLiveData: MutableLiveData<MutableList<MxCall>> by lazy {
|
||||||
|
MutableLiveData<MutableList<MxCall>>(mutableListOf())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addCall(call: MxCall) {
|
||||||
|
activeCallListLiveData.postValue(activeCallListLiveData.value?.apply { add(call) })
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeCall(callId: String) {
|
||||||
|
activeCallListLiveData.postValue(activeCallListLiveData.value?.apply { removeAll { it.callId == callId } })
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCallWithId(callId: String): MxCall? {
|
||||||
|
return activeCallListLiveData.value?.find { it.callId == callId }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getActiveCallsLiveData(): LiveData<MutableList<MxCall>> = activeCallListLiveData
|
||||||
|
}
|
|
@ -49,6 +49,7 @@ import javax.inject.Inject
|
||||||
internal class DefaultCallSignalingService @Inject constructor(
|
internal class DefaultCallSignalingService @Inject constructor(
|
||||||
@UserId
|
@UserId
|
||||||
private val userId: String,
|
private val userId: String,
|
||||||
|
private val activeCallHandler: ActiveCallHandler,
|
||||||
private val localEchoEventFactory: LocalEchoEventFactory,
|
private val localEchoEventFactory: LocalEchoEventFactory,
|
||||||
private val roomEventSender: RoomEventSender,
|
private val roomEventSender: RoomEventSender,
|
||||||
private val taskExecutor: TaskExecutor,
|
private val taskExecutor: TaskExecutor,
|
||||||
|
@ -57,8 +58,6 @@ internal class DefaultCallSignalingService @Inject constructor(
|
||||||
|
|
||||||
private val callListeners = mutableSetOf<CallsListener>()
|
private val callListeners = mutableSetOf<CallsListener>()
|
||||||
|
|
||||||
private val activeCalls = mutableListOf<MxCall>()
|
|
||||||
|
|
||||||
private val cachedTurnServerResponse = object {
|
private val cachedTurnServerResponse = object {
|
||||||
// Keep one minute safe to avoid considering the data is valid and then actually it is not when effectively using it.
|
// Keep one minute safe to avoid considering the data is valid and then actually it is not when effectively using it.
|
||||||
private val MIN_TTL = 60
|
private val MIN_TTL = 60
|
||||||
|
@ -97,7 +96,7 @@ internal class DefaultCallSignalingService @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createOutgoingCall(roomId: String, otherUserId: String, isVideoCall: Boolean): MxCall {
|
override fun createOutgoingCall(roomId: String, otherUserId: String, isVideoCall: Boolean): MxCall {
|
||||||
return MxCallImpl(
|
val call = MxCallImpl(
|
||||||
callId = UUID.randomUUID().toString(),
|
callId = UUID.randomUUID().toString(),
|
||||||
isOutgoing = true,
|
isOutgoing = true,
|
||||||
roomId = roomId,
|
roomId = roomId,
|
||||||
|
@ -106,8 +105,9 @@ internal class DefaultCallSignalingService @Inject constructor(
|
||||||
isVideoCall = isVideoCall,
|
isVideoCall = isVideoCall,
|
||||||
localEchoEventFactory = localEchoEventFactory,
|
localEchoEventFactory = localEchoEventFactory,
|
||||||
roomEventSender = roomEventSender
|
roomEventSender = roomEventSender
|
||||||
).also {
|
)
|
||||||
activeCalls.add(it)
|
activeCallHandler.addCall(call).also {
|
||||||
|
return call
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,8 +120,12 @@ internal class DefaultCallSignalingService @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getCallWithId(callId: String): MxCall? {
|
override fun getCallWithId(callId: String): MxCall? {
|
||||||
Timber.v("## VOIP getCallWithId $callId all calls ${activeCalls.map { it.callId }}")
|
Timber.v("## VOIP getCallWithId $callId all calls ${activeCallHandler.getActiveCallsLiveData().value?.map { it.callId }}")
|
||||||
return activeCalls.find { it.callId == callId }
|
return activeCallHandler.getCallWithId(callId)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isThereAnyActiveCall(): Boolean {
|
||||||
|
return activeCallHandler.getActiveCallsLiveData().value?.isNotEmpty() == true
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun onCallEvent(event: Event) {
|
internal fun onCallEvent(event: Event) {
|
||||||
|
@ -152,6 +156,7 @@ internal class DefaultCallSignalingService @Inject constructor(
|
||||||
// Always ignore local echos of invite
|
// Always ignore local echos of invite
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
event.getClearContent().toModel<CallInviteContent>()?.let { content ->
|
event.getClearContent().toModel<CallInviteContent>()?.let { content ->
|
||||||
val incomingCall = MxCallImpl(
|
val incomingCall = MxCallImpl(
|
||||||
callId = content.callId ?: return@let,
|
callId = content.callId ?: return@let,
|
||||||
|
@ -163,7 +168,7 @@ internal class DefaultCallSignalingService @Inject constructor(
|
||||||
localEchoEventFactory = localEchoEventFactory,
|
localEchoEventFactory = localEchoEventFactory,
|
||||||
roomEventSender = roomEventSender
|
roomEventSender = roomEventSender
|
||||||
)
|
)
|
||||||
activeCalls.add(incomingCall)
|
activeCallHandler.addCall(incomingCall)
|
||||||
onCallInvite(incomingCall, content)
|
onCallInvite(incomingCall, content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -185,8 +190,8 @@ internal class DefaultCallSignalingService @Inject constructor(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
activeCallHandler.removeCall(content.callId)
|
||||||
onCallHangup(content)
|
onCallHangup(content)
|
||||||
activeCalls.removeAll { it.callId == content.callId }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EventType.CALL_CANDIDATES -> {
|
EventType.CALL_CANDIDATES -> {
|
||||||
|
@ -195,7 +200,7 @@ internal class DefaultCallSignalingService @Inject constructor(
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
event.getClearContent().toModel<CallCandidatesContent>()?.let { content ->
|
event.getClearContent().toModel<CallCandidatesContent>()?.let { content ->
|
||||||
activeCalls.firstOrNull { it.callId == content.callId }?.let {
|
activeCallHandler.getCallWithId(content.callId)?.let {
|
||||||
onCallIceCandidate(it, content)
|
onCallIceCandidate(it, content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.session.sync.job
|
||||||
|
|
||||||
import androidx.lifecycle.LiveData
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
import com.squareup.moshi.JsonEncodingException
|
import com.squareup.moshi.JsonEncodingException
|
||||||
import org.matrix.android.sdk.api.failure.Failure
|
import org.matrix.android.sdk.api.failure.Failure
|
||||||
import org.matrix.android.sdk.api.failure.isTokenError
|
import org.matrix.android.sdk.api.failure.isTokenError
|
||||||
|
@ -30,11 +31,14 @@ import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
|
||||||
import org.matrix.android.sdk.internal.util.Debouncer
|
import org.matrix.android.sdk.internal.util.Debouncer
|
||||||
import org.matrix.android.sdk.internal.util.createUIHandler
|
import org.matrix.android.sdk.internal.util.createUIHandler
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.coroutines.cancelChildren
|
import kotlinx.coroutines.cancelChildren
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.matrix.android.sdk.api.session.call.MxCall
|
||||||
|
import org.matrix.android.sdk.internal.session.call.ActiveCallHandler
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.net.SocketTimeoutException
|
import java.net.SocketTimeoutException
|
||||||
import java.util.Timer
|
import java.util.Timer
|
||||||
|
@ -48,8 +52,9 @@ private const val DEFAULT_LONG_POOL_TIMEOUT = 30_000L
|
||||||
internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
|
internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
|
||||||
private val typingUsersTracker: DefaultTypingUsersTracker,
|
private val typingUsersTracker: DefaultTypingUsersTracker,
|
||||||
private val networkConnectivityChecker: NetworkConnectivityChecker,
|
private val networkConnectivityChecker: NetworkConnectivityChecker,
|
||||||
private val backgroundDetectionObserver: BackgroundDetectionObserver)
|
private val backgroundDetectionObserver: BackgroundDetectionObserver,
|
||||||
: Thread(), NetworkConnectivityChecker.Listener, BackgroundDetectionObserver.Listener {
|
private val activeCallHandler: ActiveCallHandler
|
||||||
|
) : Thread(), NetworkConnectivityChecker.Listener, BackgroundDetectionObserver.Listener {
|
||||||
|
|
||||||
private var state: SyncState = SyncState.Idle
|
private var state: SyncState = SyncState.Idle
|
||||||
private var liveState = MutableLiveData<SyncState>(state)
|
private var liveState = MutableLiveData<SyncState>(state)
|
||||||
|
@ -62,6 +67,12 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
|
||||||
private var isTokenValid = true
|
private var isTokenValid = true
|
||||||
private var retryNoNetworkTask: TimerTask? = null
|
private var retryNoNetworkTask: TimerTask? = null
|
||||||
|
|
||||||
|
private val activeCallListObserver = Observer<MutableList<MxCall>> { activeCalls ->
|
||||||
|
if (activeCalls.isEmpty() && backgroundDetectionObserver.isInBackground) {
|
||||||
|
pause()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
updateStateTo(SyncState.Idle)
|
updateStateTo(SyncState.Idle)
|
||||||
}
|
}
|
||||||
|
@ -115,9 +126,11 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
|
||||||
|
|
||||||
override fun run() {
|
override fun run() {
|
||||||
Timber.v("Start syncing...")
|
Timber.v("Start syncing...")
|
||||||
|
|
||||||
isStarted = true
|
isStarted = true
|
||||||
networkConnectivityChecker.register(this)
|
networkConnectivityChecker.register(this)
|
||||||
backgroundDetectionObserver.register(this)
|
backgroundDetectionObserver.register(this)
|
||||||
|
registerActiveCallsObserver()
|
||||||
while (state != SyncState.Killing) {
|
while (state != SyncState.Killing) {
|
||||||
Timber.v("Entering loop, state: $state")
|
Timber.v("Entering loop, state: $state")
|
||||||
if (!isStarted) {
|
if (!isStarted) {
|
||||||
|
@ -163,6 +176,19 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
|
||||||
updateStateTo(SyncState.Killed)
|
updateStateTo(SyncState.Killed)
|
||||||
backgroundDetectionObserver.unregister(this)
|
backgroundDetectionObserver.unregister(this)
|
||||||
networkConnectivityChecker.unregister(this)
|
networkConnectivityChecker.unregister(this)
|
||||||
|
unregisterActiveCallsObserver()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun registerActiveCallsObserver() {
|
||||||
|
syncScope.launch(Dispatchers.Main) {
|
||||||
|
activeCallHandler.getActiveCallsLiveData().observeForever(activeCallListObserver)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun unregisterActiveCallsObserver() {
|
||||||
|
syncScope.launch(Dispatchers.Main) {
|
||||||
|
activeCallHandler.getActiveCallsLiveData().removeObserver(activeCallListObserver)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun doSync(params: SyncTask.Params) {
|
private suspend fun doSync(params: SyncTask.Params) {
|
||||||
|
@ -215,6 +241,8 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMoveToBackground() {
|
override fun onMoveToBackground() {
|
||||||
pause()
|
if (activeCallHandler.getActiveCallsLiveData().value.isNullOrEmpty()) {
|
||||||
|
pause()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue