mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-01-17 11:28:06 +00:00
Started to add video to conference participants + fixed call ended fragment timer
This commit is contained in:
parent
2eb8b496cd
commit
aa52f3d2b5
9 changed files with 177 additions and 15 deletions
|
|
@ -44,6 +44,7 @@ import org.linphone.R
|
|||
import org.linphone.core.tools.Log
|
||||
import org.linphone.databinding.CallActivityBinding
|
||||
import org.linphone.ui.call.fragment.ActiveCallFragmentDirections
|
||||
import org.linphone.ui.call.fragment.ActiveConferenceCallFragmentDirections
|
||||
import org.linphone.ui.call.fragment.AudioDevicesMenuDialogFragment
|
||||
import org.linphone.ui.call.fragment.IncomingCallFragmentDirections
|
||||
import org.linphone.ui.call.fragment.OutgoingCallFragmentDirections
|
||||
|
|
@ -187,17 +188,50 @@ class CallActivity : AppCompatActivity() {
|
|||
}
|
||||
|
||||
callsViewModel.goToActiveCallEvent.observe(this) {
|
||||
it.consume {
|
||||
it.consume { singleCall ->
|
||||
val navController = findNavController(R.id.call_nav_container)
|
||||
val action = when (navController.currentDestination?.id) {
|
||||
R.id.outgoingCallFragment -> {
|
||||
OutgoingCallFragmentDirections.actionOutgoingCallFragmentToActiveCallFragment()
|
||||
if (singleCall) {
|
||||
Log.i("$TAG Going from outgoing call fragment to call fragment")
|
||||
OutgoingCallFragmentDirections.actionOutgoingCallFragmentToActiveCallFragment()
|
||||
} else {
|
||||
Log.i(
|
||||
"$TAG Going from outgoing call fragment to conference call fragment"
|
||||
)
|
||||
OutgoingCallFragmentDirections.actionOutgoingCallFragmentToActiveConferenceCallFragment()
|
||||
}
|
||||
}
|
||||
R.id.incomingCallFragment -> {
|
||||
IncomingCallFragmentDirections.actionIncomingCallFragmentToActiveCallFragment()
|
||||
if (singleCall) {
|
||||
Log.i("$TAG Going from incoming call fragment to call fragment")
|
||||
IncomingCallFragmentDirections.actionIncomingCallFragmentToActiveCallFragment()
|
||||
} else {
|
||||
Log.i(
|
||||
"$TAG Going from incoming call fragment to conference call fragment"
|
||||
)
|
||||
IncomingCallFragmentDirections.actionIncomingCallFragmentToActiveConferenceCallFragment()
|
||||
}
|
||||
}
|
||||
R.id.activeConferenceCallFragment -> {
|
||||
if (singleCall) {
|
||||
Log.i("$TAG Going from conference call fragment to call fragment")
|
||||
ActiveConferenceCallFragmentDirections.actionActiveConferenceCallFragmentToActiveCallFragment()
|
||||
} else {
|
||||
Log.i(
|
||||
"$TAG Going from conference call fragment to conference call fragment"
|
||||
)
|
||||
ActiveConferenceCallFragmentDirections.actionGlobalActiveConferenceCallFragment()
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
ActiveCallFragmentDirections.actionGlobalActiveCallFragment()
|
||||
if (singleCall) {
|
||||
Log.i("$TAG Going from call fragment to call fragment")
|
||||
ActiveCallFragmentDirections.actionGlobalActiveCallFragment()
|
||||
} else {
|
||||
Log.i("$TAG Going from call fragment to conference call fragment")
|
||||
ActiveCallFragmentDirections.actionActiveCallFragmentToActiveConferenceCallFragment()
|
||||
}
|
||||
}
|
||||
}
|
||||
navController.navigate(action)
|
||||
|
|
|
|||
|
|
@ -253,9 +253,12 @@ class ActiveCallFragment : GenericCallFragment() {
|
|||
|
||||
callViewModel.goToConferenceEvent.observe(viewLifecycleOwner) {
|
||||
it.consume {
|
||||
Log.i("$TAG Going to conference fragment")
|
||||
val action = ActiveCallFragmentDirections.actionActiveCallFragmentToActiveConferenceCallFragment()
|
||||
findNavController().navigate(action)
|
||||
if (findNavController().currentDestination?.id == R.id.activeCallFragment) {
|
||||
Log.i("$TAG Going to conference fragment")
|
||||
val action =
|
||||
ActiveCallFragmentDirections.actionActiveCallFragmentToActiveConferenceCallFragment()
|
||||
findNavController().navigate(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,11 +19,15 @@
|
|||
*/
|
||||
package org.linphone.ui.call.model
|
||||
|
||||
import android.view.TextureView
|
||||
import androidx.annotation.UiThread
|
||||
import androidx.annotation.WorkerThread
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import org.linphone.LinphoneApplication.Companion.coreContext
|
||||
import org.linphone.core.MediaDirection
|
||||
import org.linphone.core.ParticipantDevice
|
||||
import org.linphone.core.ParticipantDeviceListenerStub
|
||||
import org.linphone.core.StreamType
|
||||
import org.linphone.core.tools.Log
|
||||
|
||||
class ConferenceParticipantDeviceModel @WorkerThread constructor(
|
||||
|
|
@ -40,7 +44,14 @@ class ConferenceParticipantDeviceModel @WorkerThread constructor(
|
|||
|
||||
val isSpeaking = MutableLiveData<Boolean>()
|
||||
|
||||
val isVideoAvailable = MutableLiveData<Boolean>()
|
||||
|
||||
val isSendingVideo = MutableLiveData<Boolean>()
|
||||
|
||||
private lateinit var textureView: TextureView
|
||||
|
||||
private val deviceListener = object : ParticipantDeviceListenerStub() {
|
||||
@WorkerThread
|
||||
override fun onStateChanged(
|
||||
participantDevice: ParticipantDevice,
|
||||
state: ParticipantDevice.State?
|
||||
|
|
@ -50,6 +61,7 @@ class ConferenceParticipantDeviceModel @WorkerThread constructor(
|
|||
)
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
override fun onIsMuted(participantDevice: ParticipantDevice, muted: Boolean) {
|
||||
Log.i(
|
||||
"$TAG Participant device [${participantDevice.address.asStringUriOnly()}] is ${if (participantDevice.isMuted) "muted" else "no longer muted"}"
|
||||
|
|
@ -57,6 +69,7 @@ class ConferenceParticipantDeviceModel @WorkerThread constructor(
|
|||
isMuted.postValue(participantDevice.isMuted)
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
override fun onIsSpeakingChanged(
|
||||
participantDevice: ParticipantDevice,
|
||||
speaking: Boolean
|
||||
|
|
@ -66,6 +79,41 @@ class ConferenceParticipantDeviceModel @WorkerThread constructor(
|
|||
)
|
||||
isSpeaking.postValue(participantDevice.isSpeaking)
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
override fun onStreamAvailabilityChanged(
|
||||
participantDevice: ParticipantDevice,
|
||||
available: Boolean,
|
||||
streamType: StreamType?
|
||||
) {
|
||||
Log.i(
|
||||
"$TAG Participant device [${participantDevice.address.asStringUriOnly()}] stream [$streamType] availability changed to ${if (available) "available" else "not available"}"
|
||||
)
|
||||
if (streamType == StreamType.Video) {
|
||||
val available = participantDevice.getStreamAvailability(StreamType.Video)
|
||||
isVideoAvailable.postValue(available)
|
||||
if (available) {
|
||||
updateWindowId(textureView)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
override fun onStreamCapabilityChanged(
|
||||
participantDevice: ParticipantDevice,
|
||||
direction: MediaDirection?,
|
||||
streamType: StreamType?
|
||||
) {
|
||||
Log.i(
|
||||
"$TAG Participant device [${participantDevice.address.asStringUriOnly()}] stream [$streamType] capability changed to [$direction]"
|
||||
)
|
||||
if (streamType == StreamType.Video) {
|
||||
val videoCapability = participantDevice.getStreamCapability(StreamType.Video)
|
||||
isSendingVideo.postValue(
|
||||
videoCapability == MediaDirection.SendRecv || videoCapability == MediaDirection.SendOnly
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
|
|
@ -76,10 +124,35 @@ class ConferenceParticipantDeviceModel @WorkerThread constructor(
|
|||
Log.i(
|
||||
"$TAG Participant [${device.address.asStringUriOnly()}] is in state [${device.state}]"
|
||||
)
|
||||
|
||||
isVideoAvailable.postValue(device.getStreamAvailability(StreamType.Video))
|
||||
val videoCapability = device.getStreamCapability(StreamType.Video)
|
||||
isSendingVideo.postValue(
|
||||
videoCapability == MediaDirection.SendRecv || videoCapability == MediaDirection.SendOnly
|
||||
)
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun destroy() {
|
||||
device.removeListener(deviceListener)
|
||||
}
|
||||
|
||||
@UiThread
|
||||
fun setTextureView(view: TextureView) {
|
||||
Log.i(
|
||||
"$TAG TextureView for participant [${device.address.asStringUriOnly()}] available from UI [$view]"
|
||||
)
|
||||
textureView = view
|
||||
coreContext.postOnCoreThread {
|
||||
updateWindowId(textureView)
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private fun updateWindowId(windowId: Any?) {
|
||||
Log.i(
|
||||
"$$TAG Setting participant [${device.address.asStringUriOnly()}] window ID [$windowId]"
|
||||
)
|
||||
device.nativeVideoWindowId = windowId
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ class CallsViewModel @UiThread constructor() : ViewModel() {
|
|||
)
|
||||
when (call.state) {
|
||||
Call.State.Connected -> {
|
||||
goToActiveCallEvent.postValue(Event(true))
|
||||
goToActiveCallEvent.postValue(Event(call.conference == null))
|
||||
}
|
||||
else -> {
|
||||
}
|
||||
|
|
@ -132,8 +132,15 @@ class CallsViewModel @UiThread constructor() : ViewModel() {
|
|||
Log.i("$TAG Asking activity to show incoming call fragment")
|
||||
showIncomingCallEvent.postValue(Event(true))
|
||||
} else {
|
||||
Log.i("$TAG Asking activity to show active call fragment")
|
||||
goToActiveCallEvent.postValue(Event(true))
|
||||
if (newCurrentCall.conference == null) {
|
||||
Log.i("$TAG Asking activity to show active call fragment")
|
||||
goToActiveCallEvent.postValue(Event(true))
|
||||
} else {
|
||||
Log.i(
|
||||
"$TAG Asking activity to show active conference call fragment"
|
||||
)
|
||||
goToActiveCallEvent.postValue(Event(false))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -160,7 +167,7 @@ class CallsViewModel @UiThread constructor() : ViewModel() {
|
|||
|
||||
when (currentCall.state) {
|
||||
Call.State.Connected, Call.State.StreamsRunning, Call.State.Paused, Call.State.Pausing, Call.State.PausedByRemote, Call.State.UpdatedByRemote, Call.State.Updating -> {
|
||||
goToActiveCallEvent.postValue(Event(true))
|
||||
goToActiveCallEvent.postValue(Event(currentCall.conference == null))
|
||||
}
|
||||
Call.State.OutgoingInit, Call.State.OutgoingRinging, Call.State.OutgoingProgress, Call.State.OutgoingEarlyMedia -> {
|
||||
showOutgoingCallEvent.postValue(Event(true))
|
||||
|
|
|
|||
|
|
@ -210,9 +210,11 @@ class CurrentCallViewModel @UiThread constructor() : ViewModel() {
|
|||
Log.e(
|
||||
"$TAG Failed to get a valid call to display, go to ended call fragment"
|
||||
)
|
||||
updateCallDuration()
|
||||
goToEndedCallEvent.postValue(Event(true))
|
||||
}
|
||||
} else {
|
||||
updateCallDuration()
|
||||
Log.i("$TAG Call is ending, go to ended call fragment")
|
||||
// Show that call was ended for a few seconds, then leave
|
||||
// TODO FIXME: do not show it when call is being ended due to user terminating the call
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import org.linphone.core.ConferenceInfo
|
|||
import org.linphone.core.Core
|
||||
import org.linphone.core.CoreListenerStub
|
||||
import org.linphone.core.Factory
|
||||
import org.linphone.core.MediaDirection
|
||||
import org.linphone.core.tools.Log
|
||||
import org.linphone.ui.main.contacts.model.ContactAvatarModel
|
||||
import org.linphone.utils.Event
|
||||
|
|
@ -135,6 +136,10 @@ class MeetingWaitingRoomViewModel @UiThread constructor() : ViewModel() {
|
|||
fun join() {
|
||||
coreContext.postOnCoreThread { core ->
|
||||
if (::conferenceInfo.isInitialized) {
|
||||
Log.i("$TAG Stopping video preview")
|
||||
core.nativePreviewWindowId = null
|
||||
core.isVideoPreviewEnabled = false
|
||||
|
||||
val conferenceUri = conferenceInfo.uri
|
||||
if (conferenceUri == null) {
|
||||
Log.e("$TAG Conference Info doesn't have a conference SIP URI to call!")
|
||||
|
|
@ -144,7 +149,8 @@ class MeetingWaitingRoomViewModel @UiThread constructor() : ViewModel() {
|
|||
val params = core.createCallParams(null)
|
||||
params ?: return@postOnCoreThread
|
||||
|
||||
params.isVideoEnabled = isVideoEnabled.value == true
|
||||
params.isVideoEnabled = true
|
||||
params.videoDirection = if (isVideoEnabled.value == true) MediaDirection.SendRecv else MediaDirection.RecvOnly
|
||||
params.isMicEnabled = isMicrophoneMuted.value == false
|
||||
params.account = core.defaultAccount
|
||||
coreContext.startCall(conferenceUri, params)
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import android.graphics.PorterDuff
|
|||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.LayoutInflater
|
||||
import android.view.TextureView
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.inputmethod.EditorInfo
|
||||
|
|
@ -62,6 +63,7 @@ import org.linphone.contacts.AvatarGenerator
|
|||
import org.linphone.core.ChatRoom
|
||||
import org.linphone.core.ConsolidatedPresence
|
||||
import org.linphone.core.tools.Log
|
||||
import org.linphone.ui.call.model.ConferenceParticipantDeviceModel
|
||||
|
||||
/**
|
||||
* This file contains all the data binding necessary for the app
|
||||
|
|
@ -376,6 +378,15 @@ private suspend fun loadContactPictureWithCoil(
|
|||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@BindingAdapter("participantTextureView")
|
||||
fun setParticipantTextureView(
|
||||
textureView: TextureView,
|
||||
model: ConferenceParticipantDeviceModel
|
||||
) {
|
||||
model.setTextureView(textureView)
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@BindingAdapter("onValueChanged")
|
||||
fun AppCompatEditText.editTextSetting(lambda: () -> Unit) {
|
||||
|
|
|
|||
|
|
@ -50,9 +50,10 @@
|
|||
android:id="@+id/participant_video_surface"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_margin="5dp"
|
||||
app:alignTopRight="false"
|
||||
app:displayMode="hybrid"
|
||||
participantTextureView="@{model}"
|
||||
android:visibility="@{model.isSendingVideo ? View.VISIBLE : View.GONE, default=gone}"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
|
@ -66,7 +67,7 @@
|
|||
android:layout_marginEnd="10dp"
|
||||
android:padding="2dp"
|
||||
android:src="@drawable/microphone_slash"
|
||||
android:background="@drawable/circle_white_button_background"
|
||||
android:background="@drawable/shape_circle_white_background"
|
||||
android:visibility="@{model.isMuted ? View.VISIBLE : View.GONE}"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
|
|
|||
|
|
@ -16,6 +16,12 @@
|
|||
app:popUpTo="@id/outgoingCallFragment"
|
||||
app:popUpToInclusive="true"
|
||||
app:launchSingleTop="true"/>
|
||||
<action
|
||||
android:id="@+id/action_outgoingCallFragment_to_activeConferenceCallFragment"
|
||||
app:destination="@id/activeConferenceCallFragment"
|
||||
app:popUpTo="@id/outgoingCallFragment"
|
||||
app:popUpToInclusive="true"
|
||||
app:launchSingleTop="true" />
|
||||
</fragment>
|
||||
|
||||
<action android:id="@+id/action_global_outgoingCallFragment"
|
||||
|
|
@ -35,6 +41,12 @@
|
|||
app:popUpTo="@id/incomingCallFragment"
|
||||
app:popUpToInclusive="true"
|
||||
app:launchSingleTop="true" />
|
||||
<action
|
||||
android:id="@+id/action_incomingCallFragment_to_activeConferenceCallFragment"
|
||||
app:destination="@id/activeConferenceCallFragment"
|
||||
app:popUpTo="@id/outgoingCallFragment"
|
||||
app:popUpToInclusive="true"
|
||||
app:launchSingleTop="true" />
|
||||
</fragment>
|
||||
|
||||
<action android:id="@+id/action_global_incomingCallFragment"
|
||||
|
|
@ -114,6 +126,19 @@
|
|||
android:id="@+id/activeConferenceCallFragment"
|
||||
android:name="org.linphone.ui.call.fragment.ActiveConferenceCallFragment"
|
||||
android:label="ActiveConferenceCallFragment"
|
||||
tools:layout="@layout/call_active_conference_fragment"/>
|
||||
tools:layout="@layout/call_active_conference_fragment">
|
||||
<action
|
||||
android:id="@+id/action_activeConferenceCallFragment_to_activeCallFragment"
|
||||
app:destination="@id/activeCallFragment"
|
||||
app:popUpTo="@id/activeConferenceCallFragment"
|
||||
app:popUpToInclusive="true"
|
||||
app:launchSingleTop="true" />
|
||||
</fragment>
|
||||
|
||||
<action android:id="@+id/action_global_activeConferenceCallFragment"
|
||||
app:destination="@id/activeConferenceCallFragment"
|
||||
app:popUpTo="@id/activeConferenceCallFragment"
|
||||
app:popUpToInclusive="true"
|
||||
app:launchSingleTop="true"/>
|
||||
|
||||
</navigation>
|
||||
Loading…
Add table
Reference in a new issue