diff --git a/app/src/main/java/org/linphone/ui/call/CallActivity.kt b/app/src/main/java/org/linphone/ui/call/CallActivity.kt index c108aac0a..14fd17b08 100644 --- a/app/src/main/java/org/linphone/ui/call/CallActivity.kt +++ b/app/src/main/java/org/linphone/ui/call/CallActivity.kt @@ -42,6 +42,7 @@ import androidx.window.layout.WindowLayoutInfo import com.google.android.material.bottomsheet.BottomSheetDialogFragment import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.R import org.linphone.core.tools.Log import org.linphone.databinding.CallActivityBinding @@ -239,6 +240,15 @@ class CallActivity : GenericActivity() { bottomSheetDialog = null } + override fun onDestroy() { + super.onDestroy() + + coreContext.postOnCoreThread { core -> + Log.i("$TAG Activity destroyed, removing native video window ID") + core.nativeVideoWindowId = null + } + } + override fun onResume() { super.onResume() diff --git a/app/src/main/java/org/linphone/ui/call/fragment/ActiveConferenceCallFragment.kt b/app/src/main/java/org/linphone/ui/call/fragment/ActiveConferenceCallFragment.kt index 892307fb1..4d7f30a1f 100644 --- a/app/src/main/java/org/linphone/ui/call/fragment/ActiveConferenceCallFragment.kt +++ b/app/src/main/java/org/linphone/ui/call/fragment/ActiveConferenceCallFragment.kt @@ -33,6 +33,7 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.core.tools.Log import org.linphone.databinding.CallActiveConferenceFragmentBinding +import org.linphone.ui.call.model.ConferenceModel import org.linphone.ui.call.viewmodel.CallsViewModel import org.linphone.ui.call.viewmodel.CurrentCallViewModel import org.linphone.utils.Event @@ -98,6 +99,28 @@ class ActiveConferenceCallFragment : GenericCallFragment() { sharedViewModel.toggleFullScreenEvent.value = Event(hide) } + callViewModel.conferenceModel.conferenceLayout.observe(viewLifecycleOwner) { layout -> + coreContext.postOnCoreThread { core -> + when (layout) { + ConferenceModel.ACTIVE_SPEAKER_LAYOUT -> { + Log.i( + "$TAG Current layout is active speaker, setting native video window ID" + ) + core.nativeVideoWindowId = binding.activeSpeakerSurface + } + else -> { + Log.i( + "$TAG Current layout isn't active speaker, removing native video window ID" + ) + core.nativeVideoWindowId = null + } + } + } + + // Collapse bottom sheet after changing conference layout + actionsBottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED + } + binding.setCallsListClickListener { Log.i("$TAG Going to calls list fragment") val action = ActiveConferenceCallFragmentDirections.actionActiveConferenceCallFragmentToCallsListFragment() diff --git a/app/src/main/java/org/linphone/ui/call/model/ConferenceModel.kt b/app/src/main/java/org/linphone/ui/call/model/ConferenceModel.kt index d238b8e12..9c39f41d0 100644 --- a/app/src/main/java/org/linphone/ui/call/model/ConferenceModel.kt +++ b/app/src/main/java/org/linphone/ui/call/model/ConferenceModel.kt @@ -53,6 +53,8 @@ class ConferenceModel { val participantsLabel = MutableLiveData() + val activeSpeakerName = MutableLiveData() + val isCurrentCallInConference = MutableLiveData() val conferenceLayout = MutableLiveData() @@ -80,6 +82,24 @@ class ConferenceModel { removeParticipant(participant) } + @WorkerThread + override fun onActiveSpeakerParticipantDevice( + conference: Conference, + participantDevice: ParticipantDevice + ) { + val found = participantDevices.value.orEmpty().find { + it.device == participantDevice + } + if (found != null) { + val name = found.avatarModel.contactName ?: participantDevice.name ?: participantDevice.address.username + Log.i("$TAG Newly active speaker participant is [$name]") + activeSpeakerName.postValue(name.orEmpty()) + } else { + Log.i("$TAG Failed to find actively speaking participant...") + activeSpeakerName.postValue(participantDevice.name) + } + } + @WorkerThread override fun onParticipantAdminStatusChanged( conference: Conference, @@ -278,6 +298,12 @@ class ConferenceModel { for (device in participant.devices) { val model = ConferenceParticipantDeviceModel(device) devicesList.add(model) + + if (device.isSpeaking) { + val name = model.avatarModel.contactName ?: device.name ?: device.address.username + Log.i("$TAG Using participant is [$name] as current active speaker") + activeSpeakerName.postValue(name.orEmpty()) + } } } Log.i( diff --git a/app/src/main/java/org/linphone/ui/call/model/ConferenceParticipantDeviceModel.kt b/app/src/main/java/org/linphone/ui/call/model/ConferenceParticipantDeviceModel.kt index 37daf1261..118cde62a 100644 --- a/app/src/main/java/org/linphone/ui/call/model/ConferenceParticipantDeviceModel.kt +++ b/app/src/main/java/org/linphone/ui/call/model/ConferenceParticipantDeviceModel.kt @@ -63,7 +63,7 @@ class ConferenceParticipantDeviceModel @WorkerThread constructor( @WorkerThread override fun onIsMuted(participantDevice: ParticipantDevice, muted: Boolean) { - Log.i( + Log.d( "$TAG Participant device [${participantDevice.address.asStringUriOnly()}] is ${if (participantDevice.isMuted) "muted" else "no longer muted"}" ) isMuted.postValue(participantDevice.isMuted) @@ -74,10 +74,10 @@ class ConferenceParticipantDeviceModel @WorkerThread constructor( participantDevice: ParticipantDevice, speaking: Boolean ) { - Log.i( + Log.d( "$TAG Participant device [${participantDevice.address.asStringUriOnly()}] is ${if (participantDevice.isSpeaking) "speaking" else "no longer speaking"}" ) - isSpeaking.postValue(participantDevice.isSpeaking) + isSpeaking.postValue(speaking) } @WorkerThread @@ -86,11 +86,10 @@ class ConferenceParticipantDeviceModel @WorkerThread constructor( available: Boolean, streamType: StreamType? ) { - Log.i( + Log.d( "$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) @@ -104,13 +103,12 @@ class ConferenceParticipantDeviceModel @WorkerThread constructor( direction: MediaDirection?, streamType: StreamType? ) { - Log.i( + Log.d( "$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 + direction == MediaDirection.SendRecv || direction == MediaDirection.SendOnly ) } } diff --git a/app/src/main/res/layout/call_active_conference_fragment.xml b/app/src/main/res/layout/call_active_conference_fragment.xml index 01fe199b3..735f8a19b 100644 --- a/app/src/main/res/layout/call_active_conference_fragment.xml +++ b/app/src/main/res/layout/call_active_conference_fragment.xml @@ -6,6 +6,7 @@ + @@ -63,7 +64,7 @@ android:layout_marginStart="5dp" android:layout_marginEnd="5dp" android:text="@string/conference_call_empty" - android:textColor="?attr/color_main2_000" + android:textColor="@color/white" android:textSize="22sp" android:gravity="center" android:visibility="@{conferenceViewModel.participantDevices.size() > 1 ? View.GONE : View.VISIBLE}" @@ -102,7 +103,7 @@ 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_top_bar_info_height, default=@dimen/call_top_bar_info_height}" android:onClick="@{() -> viewModel.toggleFullScreen()}" - android:visibility="@{conferenceViewModel.participantDevices.size() > 1 ? View.VISIBLE : View.GONE, default=gone}" + android:visibility="@{conferenceViewModel.conferenceLayout == ConferenceModel.GRID_LAYOUT && conferenceViewModel.participantDevices.size() > 1 ? View.VISIBLE : View.GONE, default=gone}" entries="@{conferenceViewModel.participantDevices}" layout="@{@layout/call_conference_grid_cell}" app:layout_constraintTop_toTopOf="parent" @@ -110,6 +111,53 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/call_conference_grid_cell.xml b/app/src/main/res/layout/call_conference_grid_cell.xml index c1c9d9f37..603c32995 100644 --- a/app/src/main/res/layout/call_conference_grid_cell.xml +++ b/app/src/main/res/layout/call_conference_grid_cell.xml @@ -28,6 +28,7 @@ android:layout_width="0dp" android:layout_height="0dp" android:layout_marginTop="5dp" + android:visibility="@{model.isSendingVideo ? View.GONE : View.VISIBLE}" coilCallAvatar="@{model.avatarModel}" app:layout_constraintDimensionRatio="1:1" app:layout_constraintWidth_max="@dimen/avatar_in_call_size" diff --git a/app/src/main/res/values/dimen.xml b/app/src/main/res/values/dimen.xml index 51f617a94..5d123a909 100644 --- a/app/src/main/res/values/dimen.xml +++ b/app/src/main/res/values/dimen.xml @@ -17,6 +17,7 @@ 50dp 100dp 120dp + 50dp 8dp 12dp @@ -26,6 +27,7 @@ 22dp 3dp 5dp + 14dp 2dp @@ -50,6 +52,8 @@ 15dp 30dp + 120dp + 66dp 400dp