Added 30 secs timeout to call update dialog

This commit is contained in:
Sylvain Berfini 2020-08-20 11:40:12 +02:00
parent 74cd0f1bf8
commit 932a3a7265
5 changed files with 71 additions and 19 deletions

View file

@ -29,6 +29,7 @@ import android.view.ViewGroup
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import java.util.*
import org.linphone.R import org.linphone.R
import org.linphone.activities.call.viewmodels.CallsViewModel import org.linphone.activities.call.viewmodels.CallsViewModel
import org.linphone.activities.call.viewmodels.ControlsViewModel import org.linphone.activities.call.viewmodels.ControlsViewModel
@ -47,6 +48,8 @@ class ControlsFragment : Fragment() {
private lateinit var controlsViewModel: ControlsViewModel private lateinit var controlsViewModel: ControlsViewModel
private lateinit var sharedViewModel: SharedCallViewModel private lateinit var sharedViewModel: SharedCallViewModel
private var dialog: Dialog? = null
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
@ -87,8 +90,12 @@ class ControlsFragment : Fragment() {
callsViewModel.callUpdateEvent.observe(viewLifecycleOwner, Observer { callsViewModel.callUpdateEvent.observe(viewLifecycleOwner, Observer {
it.consume { call -> it.consume { call ->
if (call.state == Call.State.StreamsRunning) {
dialog?.dismiss()
} else if (call.state == Call.State.UpdatedByRemote) {
showCallUpdateDialog(call) showCallUpdateDialog(call)
} }
}
}) })
controlsViewModel.chatClickedEvent.observe(viewLifecycleOwner, Observer { controlsViewModel.chatClickedEvent.observe(viewLifecycleOwner, Observer {
@ -132,18 +139,18 @@ class ControlsFragment : Fragment() {
private fun showCallUpdateDialog(call: Call) { private fun showCallUpdateDialog(call: Call) {
val viewModel = DialogViewModel(AppUtils.getString(R.string.call_video_update_requested_dialog)) val viewModel = DialogViewModel(AppUtils.getString(R.string.call_video_update_requested_dialog))
val dialog: Dialog = DialogUtils.getDialog(requireContext(), viewModel) dialog = DialogUtils.getDialog(requireContext(), viewModel)
viewModel.showCancelButton({ viewModel.showCancelButton({
callsViewModel.answerCallUpdateRequest(call, false) callsViewModel.answerCallUpdateRequest(call, false)
dialog.dismiss() dialog?.dismiss()
}, getString(R.string.dialog_decline)) }, getString(R.string.dialog_decline))
viewModel.showOkButton({ viewModel.showOkButton({
callsViewModel.answerCallUpdateRequest(call, true) callsViewModel.answerCallUpdateRequest(call, true)
dialog.dismiss() dialog?.dismiss()
}, getString(R.string.dialog_accept)) }, getString(R.string.dialog_accept))
dialog.show() dialog?.show()
} }
} }

View file

@ -22,6 +22,11 @@ package org.linphone.activities.call.viewmodels
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import java.util.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.contact.GenericContactViewModel import org.linphone.contact.GenericContactViewModel
import org.linphone.core.Call import org.linphone.core.Call
@ -54,6 +59,8 @@ open class CallViewModel(val call: Call) : GenericContactViewModel(call.remoteAd
MutableLiveData<Event<Boolean>>() MutableLiveData<Event<Boolean>>()
} }
private var timer: Timer? = null
private val listener = object : CallListenerStub() { private val listener = object : CallListenerStub() {
override fun onStateChanged(call: Call, state: Call.State, message: String) { override fun onStateChanged(call: Call, state: Call.State, message: String) {
if (call != this@CallViewModel.call) return if (call != this@CallViewModel.call) return
@ -61,6 +68,7 @@ open class CallViewModel(val call: Call) : GenericContactViewModel(call.remoteAd
isPaused.value = state == Call.State.Paused isPaused.value = state == Call.State.Paused
if (state == Call.State.End || state == Call.State.Released || state == Call.State.Error) { if (state == Call.State.End || state == Call.State.Released || state == Call.State.Error) {
timer?.cancel()
callEndedEvent.value = Event(true) callEndedEvent.value = Event(true)
if (state == Call.State.Error) { if (state == Call.State.Error) {
@ -68,6 +76,13 @@ open class CallViewModel(val call: Call) : GenericContactViewModel(call.remoteAd
} }
} else if (call.state == Call.State.Connected) { } else if (call.state == Call.State.Connected) {
callConnectedEvent.value = Event(true) callConnectedEvent.value = Event(true)
} else if (call.state == Call.State.StreamsRunning) {
// Stop call update timer once user has accepted or declined call update
timer?.cancel()
} else if (call.state == Call.State.UpdatedByRemote) {
// User has 30 secs to accept or decline call update
// Dialog to accept or decline is handled by CallsViewModel & ControlsFragment
startTimer(call)
} }
} }
} }
@ -103,4 +118,20 @@ open class CallViewModel(val call: Call) : GenericContactViewModel(call.remoteAd
if (call.core.conferenceSize <= 1) call.core.leaveConference() if (call.core.conferenceSize <= 1) call.core.leaveConference()
} }
} }
private fun startTimer(call: Call) {
timer?.cancel()
timer = Timer("Call update timeout")
timer?.schedule(object : TimerTask() {
override fun run() {
// Decline call update
viewModelScope.launch {
withContext(Dispatchers.Main) {
coreContext.answerCallUpdateRequest(call, false)
}
}
}
}, 30000)
}
} }

View file

@ -76,17 +76,20 @@ class CallsViewModel : ViewModel() {
} else if (state == Call.State.Resuming) { } else if (state == Call.State.Resuming) {
removeCallFromPausedListIfPresent(call) removeCallFromPausedListIfPresent(call)
} else if (call.state == Call.State.UpdatedByRemote) { } else if (call.state == Call.State.UpdatedByRemote) {
// If the correspondent proposes video while audio call, // If the correspondent asks to turn on video while audio call,
// defer update until user has chosen whether to accept it or not // defer update until user has chosen whether to accept it or not
val remoteVideo = call.remoteParams?.videoEnabled() ?: false val remoteVideo = call.remoteParams?.videoEnabled() ?: false
val localVideo = call.currentParams.videoEnabled() val localVideo = call.currentParams.videoEnabled()
val autoAccept = call.core.videoActivationPolicy.automaticallyAccept val autoAccept = call.core.videoActivationPolicy.automaticallyAccept
if (remoteVideo && !localVideo && !autoAccept) { if (remoteVideo && !localVideo && !autoAccept) {
call.deferUpdate() call.deferUpdate()
// TODO: start 30 secs timer and decline update if no answer when it triggers
callUpdateEvent.value = Event(call) callUpdateEvent.value = Event(call)
} }
} else { } else {
if (state == Call.State.StreamsRunning) {
callUpdateEvent.value = Event(call)
}
if (call.conference != null) { if (call.conference != null) {
addCallToConferenceListIfNotAlreadyInIt(call) addCallToConferenceListIfNotAlreadyInIt(call)
} else { } else {
@ -126,16 +129,7 @@ class CallsViewModel : ViewModel() {
} }
fun answerCallUpdateRequest(call: Call, accept: Boolean) { fun answerCallUpdateRequest(call: Call, accept: Boolean) {
val core = call.core coreContext.answerCallUpdateRequest(call, accept)
val params = core.createCallParams(call)
if (accept) {
params?.enableVideo(true)
core.enableVideoCapture(true)
core.enableVideoDisplay(true)
}
call.acceptUpdate(params)
} }
fun pauseConference() { fun pauseConference() {

View file

@ -22,6 +22,10 @@ package org.linphone.activities.call.viewmodels
import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.* import java.util.*
import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.core.AudioDevice import org.linphone.core.AudioDevice
@ -119,9 +123,13 @@ class ControlsFadingViewModel : ViewModel() {
timer = Timer("Hide UI controls scheduler") timer = Timer("Hide UI controls scheduler")
timer?.schedule(object : TimerTask() { timer?.schedule(object : TimerTask() {
override fun run() { override fun run() {
viewModelScope.launch {
withContext(Dispatchers.Main) {
val videoEnabled = coreContext.isVideoCallOrConferenceActive() val videoEnabled = coreContext.isVideoCallOrConferenceActive()
areControlsHidden.postValue(videoEnabled) areControlsHidden.postValue(videoEnabled)
} }
}
}
}, 3000) }, 3000)
} }
} }

View file

@ -299,6 +299,18 @@ class CoreContext(val context: Context, coreConfig: Config) {
/* Call related functions */ /* Call related functions */
fun answerCallUpdateRequest(call: Call, accept: Boolean) {
val params = core.createCallParams(call)
if (accept) {
params?.enableVideo(true)
core.enableVideoCapture(true)
core.enableVideoDisplay(true)
}
call.acceptUpdate(params)
}
fun answerCall(call: Call) { fun answerCall(call: Call) {
Log.i("[Context] Answering call $call") Log.i("[Context] Answering call $call")
val params = core.createCallParams(call) val params = core.createCallParams(call)