From d973cd784823dd0182715c715a0dadb79d41911f Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 12 Jul 2021 17:49:44 +0200 Subject: [PATCH] Webrtc call: get back on green banner --- .../app/core/ui/views/CurrentCallsCardView.kt | 156 ------------------ .../app/core/ui/views/CurrentCallsView.kt | 75 +++++++++ .../ui/views/CurrentCallsViewPresenter.kt | 60 +++++++ .../app/features/home/HomeDetailFragment.kt | 26 +-- .../home/room/detail/RoomDetailFragment.kt | 16 +- .../main/res/layout/fragment_home_detail.xml | 24 +-- .../main/res/layout/fragment_room_detail.xml | 32 ++-- .../main/res/layout/view_current_calls.xml | 25 +++ .../res/layout/view_current_calls_card.xml | 93 ----------- 9 files changed, 204 insertions(+), 303 deletions(-) delete mode 100644 vector/src/main/java/im/vector/app/core/ui/views/CurrentCallsCardView.kt create mode 100644 vector/src/main/java/im/vector/app/core/ui/views/CurrentCallsView.kt create mode 100644 vector/src/main/java/im/vector/app/core/ui/views/CurrentCallsViewPresenter.kt create mode 100644 vector/src/main/res/layout/view_current_calls.xml delete mode 100644 vector/src/main/res/layout/view_current_calls_card.xml diff --git a/vector/src/main/java/im/vector/app/core/ui/views/CurrentCallsCardView.kt b/vector/src/main/java/im/vector/app/core/ui/views/CurrentCallsCardView.kt deleted file mode 100644 index 318664d720..0000000000 --- a/vector/src/main/java/im/vector/app/core/ui/views/CurrentCallsCardView.kt +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (c) 2021 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 im.vector.app.core.ui.views - -import android.content.Context -import android.util.AttributeSet -import androidx.core.content.ContextCompat -import androidx.core.view.isVisible -import com.google.android.material.card.MaterialCardView -import im.vector.app.R -import im.vector.app.databinding.ViewCurrentCallsCardBinding -import im.vector.app.features.call.utils.EglUtils -import im.vector.app.features.call.webrtc.WebRtcCall -import im.vector.app.features.call.webrtc.getOpponentAsMatrixItem -import im.vector.app.features.home.AvatarRenderer -import io.github.hyuwah.draggableviewlib.DraggableView -import io.github.hyuwah.draggableviewlib.setupDraggable -import org.matrix.android.sdk.api.session.Session -import org.matrix.android.sdk.api.session.call.CallState -import org.webrtc.RendererCommon - -class CurrentCallsCardView @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0 -) : MaterialCardView(context, attrs, defStyleAttr) { - - interface Callback { - fun onTapToReturnToCall() - } - - private val views: ViewCurrentCallsCardBinding - - private var activeCallPipInitialized = false - private var currentCall: WebRtcCall? = null - private var draggableView: DraggableView? = null - - lateinit var avatarRenderer: AvatarRenderer - lateinit var session: Session - var callback: Callback? = null - - init { - inflate(context, R.layout.view_current_calls_card, this) - isVisible = false - views = ViewCurrentCallsCardBinding.bind(this) - draggableView = setupDraggable().build() - setOnClickListener { callback?.onTapToReturnToCall() } - } - - fun render(currentCall: WebRtcCall?, calls: List) { - views.activeCallPiP.let { - this.currentCall?.detachRenderers(listOf(it)) - } - this.currentCall = currentCall - if (currentCall != null) { - isVisible = true - when (currentCall.mxCall.state) { - is CallState.LocalRinging, CallState.Idle -> { - isVisible = false - } - is CallState.Connected -> { - views.activeCallProgress.isVisible = false - val isVideoCall = currentCall.mxCall.isVideoCall - if (isVideoCall) { - renderVideoCall(currentCall) - } else { - renderVoiceCall(currentCall, calls) - } - } - else -> { - renderConnectingState(currentCall) - } - } - } else { - // NO ACTIVE CALL - isVisible = false - } - } - - private fun renderConnectingState(currentCall: WebRtcCall) { - //TODO show dots - views.activeCallProgress.isVisible = true - views.activeCallPiP.isVisible = false - views.avatarViews.isVisible = false - currentCall.detachRenderers(listOf(views.activeCallPiP)) - } - - private fun renderVideoCall(currentCall: WebRtcCall) { - initIfNeeded() - views.activeCallPiP.isVisible = true - views.avatarViews.isVisible = false - currentCall.attachViewRenderers(null, views.activeCallPiP, null) - } - - private fun renderVoiceCall(currentCall: WebRtcCall, calls: List) { - views.activeCallPiP.isVisible = false - views.avatarViews.isVisible = true - val isActiveCallPaused = currentCall.isLocalOnHold || currentCall.isRemoteOnHold - views.activeCallPausedIcon.isVisible = isActiveCallPaused - val activeOpponentMatrixItem = currentCall.getOpponentAsMatrixItem(session) - if (isActiveCallPaused) { - val colorFilter = ContextCompat.getColor(context, R.color.bg_call_screen_blur) - activeOpponentMatrixItem?.also { - avatarRenderer.renderBlur(it, views.activeCallOpponentAvatar, sampling = 2, rounded = true, colorFilter = colorFilter, addPlaceholder = true) - } - } else { - activeOpponentMatrixItem?.also { - avatarRenderer.render(it, views.activeCallOpponentAvatar) - } - } - - val otherConnectedCall = calls.filter { - it.mxCall.state is CallState.Connected - }.firstOrNull { - it != currentCall - } - if (otherConnectedCall != null) { - views.otherCallOpponentAvatar.isVisible = true - views.otherCallPausedIcon.isVisible = true - otherConnectedCall.getOpponentAsMatrixItem(session)?.also { heldOpponentMatrixItem -> - avatarRenderer.render(heldOpponentMatrixItem, views.activeCallOpponentAvatar) - } - } else { - views.otherCallOpponentAvatar.isVisible = false - views.otherCallPausedIcon.isVisible = false - } - } - - private fun initIfNeeded() { - if (!activeCallPipInitialized) { - EglUtils.rootEglBase?.let { eglBase -> - views.activeCallPiP.apply { - init(eglBase.eglBaseContext, null) - setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_BALANCED) - setEnableHardwareScaler(true) - setZOrderMediaOverlay(true) - } - activeCallPipInitialized = true - } - } - } -} diff --git a/vector/src/main/java/im/vector/app/core/ui/views/CurrentCallsView.kt b/vector/src/main/java/im/vector/app/core/ui/views/CurrentCallsView.kt new file mode 100644 index 0000000000..84861c3b25 --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/ui/views/CurrentCallsView.kt @@ -0,0 +1,75 @@ +/* + * 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 im.vector.app.core.ui.views + +import android.content.Context +import android.content.res.ColorStateList +import android.os.Build +import android.util.AttributeSet +import android.util.TypedValue +import android.widget.FrameLayout +import android.widget.RelativeLayout +import androidx.appcompat.content.res.AppCompatResources +import im.vector.app.R +import im.vector.app.databinding.ViewCurrentCallsBinding +import im.vector.app.features.call.webrtc.WebRtcCall +import im.vector.app.features.themes.ThemeUtils +import org.matrix.android.sdk.api.session.call.CallState + +class CurrentCallsView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : FrameLayout(context, attrs, defStyleAttr) { + + interface Callback { + fun onTapToReturnToCall() + } + + val views: ViewCurrentCallsBinding + var callback: Callback? = null + + init { + inflate(context, R.layout.view_current_calls, this) + views = ViewCurrentCallsBinding.bind(this) + setBackgroundColor(ThemeUtils.getColor(context, R.attr.colorPrimary)) + val outValue = TypedValue().also { + context.theme.resolveAttribute(android.R.attr.selectableItemBackground, it, true) + } + foreground = AppCompatResources.getDrawable(context, outValue.resourceId) + setOnClickListener { callback?.onTapToReturnToCall() } + } + + fun render(calls: List, formattedDuration: String) { + val connectedCalls = calls.filter { + it.mxCall.state is CallState.Connected + } + val heldCalls = connectedCalls.filter { + it.isLocalOnHold || it.isRemoteOnHold + } + if (connectedCalls.isEmpty()) return + views.currentCallsInfo.text = if (connectedCalls.size == heldCalls.size) { + resources.getQuantityString(R.plurals.call_only_paused, heldCalls.size, heldCalls.size) + } else { + if (heldCalls.isEmpty()) { + resources.getString(R.string.call_only_active, formattedDuration) + } else { + resources.getQuantityString(R.plurals.call_one_active_and_other_paused, heldCalls.size, formattedDuration, heldCalls.size) + } + } + } +} diff --git a/vector/src/main/java/im/vector/app/core/ui/views/CurrentCallsViewPresenter.kt b/vector/src/main/java/im/vector/app/core/ui/views/CurrentCallsViewPresenter.kt new file mode 100644 index 0000000000..22e5926ea8 --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/ui/views/CurrentCallsViewPresenter.kt @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021 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 im.vector.app.core.ui.views + +import androidx.core.view.isVisible +import im.vector.app.features.call.webrtc.WebRtcCall +import org.matrix.android.sdk.api.session.call.CallState + +class CurrentCallsViewPresenter { + + private var currentCallsView: CurrentCallsView? = null + private var currentCall: WebRtcCall? = null + private var calls: List = emptyList() + + private val tickListener = object : WebRtcCall.Listener { + override fun onTick(formattedDuration: String) { + currentCallsView?.render(calls, formattedDuration) + } + } + + fun updateCall(currentCall: WebRtcCall?, calls: List) { + this.currentCall?.removeListener(tickListener) + this.currentCall = currentCall + this.currentCall?.addListener(tickListener) + this.calls = calls + val hasActiveCall = currentCall?.mxCall?.state is CallState.Connected + currentCallsView?.isVisible = hasActiveCall + if (hasActiveCall) { + currentCallsView?.render(calls, currentCall?.formattedDuration() ?: "") + } else { + currentCallsView?.isVisible = false + } + } + + fun bind(activeCallView: CurrentCallsView, interactionListener: CurrentCallsView.Callback) { + this.currentCallsView = activeCallView + this.currentCallsView?.callback = interactionListener + this.currentCall?.addListener(tickListener) + } + + fun unBind() { + this.currentCallsView?.callback = null + this.currentCall?.removeListener(tickListener) + currentCallsView = null + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt index f06b39fff3..b50935b75c 100644 --- a/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/HomeDetailFragment.kt @@ -37,7 +37,8 @@ import im.vector.app.core.platform.ToolbarConfigurable import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.resources.ColorProvider -import im.vector.app.core.ui.views.CurrentCallsCardView +import im.vector.app.core.ui.views.CurrentCallsView +import im.vector.app.core.ui.views.CurrentCallsViewPresenter import im.vector.app.core.ui.views.KeysBackupBanner import im.vector.app.databinding.FragmentHomeDetailBinding import im.vector.app.features.call.SharedKnownCallsViewModel @@ -56,7 +57,6 @@ import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.workers.signout.BannerState import im.vector.app.features.workers.signout.ServerBackupStatusViewModel import im.vector.app.features.workers.signout.ServerBackupStatusViewState -import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.group.model.GroupSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo @@ -69,12 +69,11 @@ class HomeDetailFragment @Inject constructor( private val colorProvider: ColorProvider, private val alertManager: PopupAlertManager, private val callManager: WebRtcCallManager, - private val vectorPreferences: VectorPreferences, - private val session: Session + private val vectorPreferences: VectorPreferences ) : VectorBaseFragment(), KeysBackupBanner.Delegate, - ServerBackupStatusViewModel.Factory, - CurrentCallsCardView.Callback { + CurrentCallsView.Callback, + ServerBackupStatusViewModel.Factory { private val viewModel: HomeDetailViewModel by fragmentViewModel() private val unknownDeviceDetectorSharedViewModel: UnknownDeviceDetectorSharedViewModel by activityViewModel() @@ -117,6 +116,8 @@ class HomeDetailFragment @Inject constructor( return FragmentHomeDetailBinding.inflate(inflater, container, false) } + private val currentCallsViewPresenter = CurrentCallsViewPresenter() + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) sharedActionViewModel = activityViewModelProvider.get(HomeSharedActionViewModel::class.java) @@ -188,11 +189,16 @@ class HomeDetailFragment @Inject constructor( sharedCallActionViewModel .liveKnownCalls .observe(viewLifecycleOwner, { - views.currentCallsCardView.render(callManager.getCurrentCall(), callManager.getCalls()) + currentCallsViewPresenter.updateCall(callManager.getCurrentCall(), callManager.getCalls()) invalidateOptionsMenu() }) } + override fun onDestroyView() { + currentCallsViewPresenter.unBind() + super.onDestroyView() + } + override fun onResume() { super.onResume() // update notification tab if needed @@ -289,11 +295,7 @@ class HomeDetailFragment @Inject constructor( } private fun setupActiveCallView() { - views.currentCallsCardView.apply { - this.avatarRenderer = this@HomeDetailFragment.avatarRenderer - this.session = this@HomeDetailFragment.session - this.callback = this@HomeDetailFragment - } + currentCallsViewPresenter.bind(views.currentCallsView, this) } private fun setupToolbar() { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index 224c52b30f..7e3f67a06a 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -89,7 +89,9 @@ import im.vector.app.core.intent.getMimeTypeFromUri import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.showOptimizedSnackbar import im.vector.app.core.resources.ColorProvider -import im.vector.app.core.ui.views.CurrentCallsCardView +import im.vector.app.core.ui.views.CurrentCallsViewPresenter + +import im.vector.app.core.ui.views.CurrentCallsView import im.vector.app.core.ui.views.FailedMessagesWarningView import im.vector.app.core.ui.views.NotificationAreaView import im.vector.app.core.utils.Debouncer @@ -240,7 +242,7 @@ class RoomDetailFragment @Inject constructor( AttachmentTypeSelectorView.Callback, AttachmentsHelper.Callback, GalleryOrCameraDialogHelper.Listener, - CurrentCallsCardView.Callback { + CurrentCallsView.Callback { companion object { /** @@ -299,6 +301,7 @@ class RoomDetailFragment @Inject constructor( private lateinit var attachmentTypeSelector: AttachmentTypeSelectorView private var lockSendButton = false + private val currentCallsViewPresenter = CurrentCallsViewPresenter() private lateinit var emojiPopup: EmojiPopup @@ -346,7 +349,7 @@ class RoomDetailFragment @Inject constructor( knownCallsViewModel .liveKnownCalls .observe(viewLifecycleOwner, { - views.currentCallsCardView.render(callManager.getCurrentCall(), it) + currentCallsViewPresenter.updateCall(callManager.getCurrentCall(), it) invalidateOptionsMenu() }) @@ -685,6 +688,7 @@ class RoomDetailFragment @Inject constructor( override fun onDestroyView() { timelineEventController.callback = null timelineEventController.removeModelBuildListener(modelBuildListener) + currentCallsViewPresenter.unBind() modelBuildListener = null autoCompleter.clear() debouncer.cancelAll() @@ -730,11 +734,7 @@ class RoomDetailFragment @Inject constructor( } private fun setupActiveCallView() { - views.currentCallsCardView.apply { - this.callback = this@RoomDetailFragment - this.avatarRenderer = this@RoomDetailFragment.avatarRenderer - this.session = this@RoomDetailFragment.session - } + currentCallsViewPresenter.bind(views.currentCallsView, this) } private fun navigateToEvent(action: RoomDetailViewEvents.NavigateToEvent) { diff --git a/vector/src/main/res/layout/fragment_home_detail.xml b/vector/src/main/res/layout/fragment_home_detail.xml index 4fab86ee7f..b84ab29bea 100644 --- a/vector/src/main/res/layout/fragment_home_detail.xml +++ b/vector/src/main/res/layout/fragment_home_detail.xml @@ -11,6 +11,15 @@ android:layout_height="wrap_content" app:layout_constraintTop_toTopOf="parent"> + + - - - + + + android:visibility="visible" + app:layout_constraintTop_toBottomOf="@id/syncStateView" /> + - - - + + + + + diff --git a/vector/src/main/res/layout/view_current_calls_card.xml b/vector/src/main/res/layout/view_current_calls_card.xml deleted file mode 100644 index 712de3280b..0000000000 --- a/vector/src/main/res/layout/view_current_calls_card.xml +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - -