Display our own video (if enabled) while waiting for other participants to join the conference

This commit is contained in:
Sylvain Berfini 2024-04-09 11:09:22 +02:00
parent 065cdfa8c1
commit fea42aba3b
3 changed files with 113 additions and 18 deletions

View file

@ -19,12 +19,14 @@
*/
package org.linphone.ui.call.conference.fragment
import android.annotation.SuppressLint
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.os.Bundle
import android.os.SystemClock
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.ViewModelProvider
@ -83,6 +85,33 @@ class ActiveConferenceCallFragment : GenericCallFragment() {
override fun onSlide(bottomSheet: View, slideOffset: Float) { }
}
// For moving video preview purposes
private var previewX: Float = 0f
private var previewY: Float = 0f
private val previewTouchListener = View.OnTouchListener { view, event ->
when (event.action) {
MotionEvent.ACTION_DOWN -> {
previewX = view.x - event.rawX
previewY = view.y - event.rawY
true
}
MotionEvent.ACTION_MOVE -> {
view.animate()
.x(event.rawX + previewX)
.y(event.rawY + previewY)
.setDuration(0)
.start()
true
}
else -> {
view.performClick()
false
}
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@ -163,6 +192,15 @@ class ActiveConferenceCallFragment : GenericCallFragment() {
actionsBottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
}
callViewModel.conferenceModel.participants.observe(viewLifecycleOwner) { participants ->
coreContext.postOnCoreThread { core ->
if (participants.size == 1) {
Log.i("$TAG We are alone in that conference, using nativePreviewWindowId")
core.nativePreviewWindowId = binding.localPreviewVideoSurface
}
}
}
binding.setBackClickListener {
requireActivity().finish()
}
@ -201,12 +239,22 @@ class ActiveConferenceCallFragment : GenericCallFragment() {
}
}
@SuppressLint("ClickableViewAccessibility")
override fun onResume() {
super.onResume()
coreContext.postOnCoreThread {
binding.localPreviewVideoSurface.setOnTouchListener(previewTouchListener)
// Need to be done manually
callViewModel.updateCallDuration()
}
}
@SuppressLint("ClickableViewAccessibility")
override fun onPause() {
super.onPause()
binding.localPreviewVideoSurface.setOnTouchListener(null)
}
}

View file

@ -102,9 +102,9 @@ class ConferenceViewModel {
) {
if (conference.isMe(device.address)) {
val direction = device.getStreamCapability(StreamType.Video)
isMeParticipantSendingVideo.postValue(
direction == MediaDirection.SendRecv || direction == MediaDirection.SendOnly
)
val sendingVideo = direction == MediaDirection.SendRecv || direction == MediaDirection.SendOnly
isMeParticipantSendingVideo.postValue(sendingVideo)
Log.i("$TAG We ${if (sendingVideo) "are" else "aren't"} sending video")
}
}
@ -147,7 +147,24 @@ class ConferenceViewModel {
Log.i(
"$TAG Participant device added: ${participantDevice.address.asStringUriOnly()}"
)
addParticipantDevice(participantDevice)
// Since we do not compute our own devices until another participant joins,
// We have to do it when someone else joins
if (participantDevices.value.orEmpty().isEmpty()) {
val list = arrayListOf<ConferenceParticipantDeviceModel>()
val ourDevices = conference.me.devices
Log.i("$TAG We have [${ourDevices.size}] devices, now it's time to add them")
for (device in ourDevices) {
val model = ConferenceParticipantDeviceModel(device, true)
list.add(model)
}
val newModel = ConferenceParticipantDeviceModel(participantDevice)
list.add(newModel)
participantDevices.postValue(sortParticipantDevicesList(list))
} else {
addParticipantDevice(participantDevice)
}
}
@WorkerThread
@ -202,6 +219,10 @@ class ConferenceViewModel {
override fun onStateChanged(conference: Conference, state: Conference.State) {
Log.i("$TAG State changed [$state]")
if (conference.state == Conference.State.Created) {
val isIn = conference.isIn
isPaused.postValue(!isIn)
Log.i("$TAG We ${if (isIn) "are" else "aren't"} in the conference")
computeParticipants()
}
}
@ -226,15 +247,20 @@ class ConferenceViewModel {
isCurrentCallInConference.postValue(true)
conference = conf
conference.addListener(conferenceListener)
isPaused.postValue(conference.isIn)
val isIn = conference.isIn
isPaused.postValue(!isIn)
Log.i("$TAG We ${if (isIn) "are" else "aren't"} in the conference right now")
val screenSharing = conference.screenSharingParticipant != null
isScreenSharing.postValue(screenSharing)
val confSubject = conference.subject.orEmpty()
Log.i(
"$TAG Configuring conference with subject [${conference.subject}] from call [${call.callLog.callId}]"
"$TAG Configuring conference with subject [$confSubject] from call [${call.callLog.callId}]"
)
sipUri.postValue(conference.conferenceAddress.asStringUriOnly())
subject.postValue(conference.subject)
subject.postValue(confSubject)
if (conference.state == Conference.State.Created) {
computeParticipants()
@ -420,10 +446,10 @@ class ConferenceViewModel {
val meParticipantModel = ConferenceParticipantModel(meParticipant, admin, true, null, null)
participantsList.add(meParticipantModel)
if (!skipDevices) {
val ourDevices = conference.me.devices
Log.i("$TAG We have [${ourDevices.size}] devices")
for (device in ourDevices) {
val ourDevices = conference.me.devices
Log.i("$TAG We have [${ourDevices.size}] devices")
for (device in ourDevices) {
if (!skipDevices) {
val model = ConferenceParticipantDeviceModel(device, true)
devicesList.add(model)
@ -433,12 +459,12 @@ class ConferenceViewModel {
activeSpeaker.postValue(model)
activeSpeakerParticipantDeviceFound = true
}
val direction = device.getStreamCapability(StreamType.Video)
isMeParticipantSendingVideo.postValue(
direction == MediaDirection.SendRecv || direction == MediaDirection.SendOnly
)
}
val direction = device.getStreamCapability(StreamType.Video)
val sendingVideo = direction == MediaDirection.SendRecv || direction == MediaDirection.SendOnly
isMeParticipantSendingVideo.postValue(sendingVideo)
Log.i("$TAG We ${if (sendingVideo) "are" else "aren't"} sending video right now")
}
if (!activeSpeakerParticipantDeviceFound && devicesList.isNotEmpty()) {
@ -453,7 +479,14 @@ class ConferenceViewModel {
participants.postValue(sortParticipantList(participantsList))
if (!skipDevices) {
checkIfTooManyParticipantDevicesForGridLayout(devicesList)
participantDevices.postValue(sortParticipantDevicesList(devicesList))
if (participantsList.size == 1) {
Log.i("$TAG We are alone in that conference, not posting devices list for now")
participantDevices.postValue(arrayListOf())
} else {
participantDevices.postValue(sortParticipantDevicesList(devicesList))
}
participantsLabel.postValue(
AppUtils.getStringWithPlural(
R.plurals.conference_participants_list_title,

View file

@ -235,6 +235,20 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<org.linphone.ui.call.view.RoundCornersTextureView
android:id="@+id/local_preview_video_surface"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:layout_marginBottom="@{viewModel.fullScreenMode || viewModel.pipMode ? @dimen/zero : @dimen/call_main_actions_menu_margin, default=@dimen/call_main_actions_menu_margin}"
android:visibility="@{conferenceViewModel.isMeParticipantSendingVideo &amp;&amp; conferenceViewModel.participants.size() == 1 &amp;&amp; !conferenceViewModel.isPaused ? View.VISIBLE : View.GONE}"
app:alignTopRight="true"
app:displayMode="black_bars"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHeight_max="200dp"
app:layout_constraintWidth_max="200dp" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/conference_layout_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
@ -242,7 +256,7 @@
android:layout_height="0dp"
android:layout_marginBottom="@{viewModel.fullScreenMode || viewModel.pipMode ? @dimen/zero : @dimen/call_main_actions_menu_margin, default=@dimen/call_main_actions_menu_margin}"
android:layout_marginTop="@{viewModel.fullScreenMode || viewModel.pipMode ? @dimen/zero : @dimen/call_remote_video_top_margin, default=@dimen/call_remote_video_top_margin}"
android:visibility="@{conferenceViewModel.participantDevices.size() > 1 &amp;&amp; !conferenceViewModel.isPaused ? View.VISIBLE : View.GONE}"
android:visibility="@{conferenceViewModel.participants.size() > 1 &amp;&amp; !conferenceViewModel.isPaused ? View.VISIBLE : View.GONE, default=gone}"
app:navGraph="@navigation/conference_nav_graph"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"