diff --git a/build.gradle b/build.gradle index b6c4a17559..48274d88bf 100644 --- a/build.gradle +++ b/build.gradle @@ -44,6 +44,8 @@ allprojects { includeGroupByRegex 'com\\.github\\.chrisbanes' // PFLockScreen-Android includeGroupByRegex 'com\\.github\\.vector-im' + // DraggableView + includeGroupByRegex 'com\\.github\\.hyuwah' // Chat effects includeGroupByRegex 'com\\.github\\.jetradarmobile' diff --git a/library/ui-styles/src/main/res/values/dimens.xml b/library/ui-styles/src/main/res/values/dimens.xml index 88338f799b..e2e50449ce 100644 --- a/library/ui-styles/src/main/res/values/dimens.xml +++ b/library/ui-styles/src/main/res/values/dimens.xml @@ -28,6 +28,10 @@ 20dp 4dp + 128dp + 88dp + 8dp + 76dp diff --git a/vector/build.gradle b/vector/build.gradle index 7c01cb550e..36eee3329f 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -396,6 +396,7 @@ dependencies { implementation "androidx.autofill:autofill:$autofill_version" implementation 'jp.wasabeef:glide-transformations:4.3.0' implementation 'com.github.vector-im:PFLockScreen-Android:1.0.0-beta12' + implementation 'com.github.hyuwah:DraggableView:1.0.0' // Custom Tab implementation 'androidx.browser:browser:1.3.0' 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 new file mode 100644 index 0000000000..318664d720 --- /dev/null +++ b/vector/src/main/java/im/vector/app/core/ui/views/CurrentCallsCardView.kt @@ -0,0 +1,156 @@ +/* + * 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 deleted file mode 100644 index d1332f18dc..0000000000 --- a/vector/src/main/java/im/vector/app/core/ui/views/CurrentCallsView.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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.util.AttributeSet -import android.widget.RelativeLayout -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 -) : RelativeLayout(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)) - 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.remoteOnHold - } - 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/KnownCallsViewHolder.kt b/vector/src/main/java/im/vector/app/core/ui/views/KnownCallsViewHolder.kt deleted file mode 100644 index d49cf929b6..0000000000 --- a/vector/src/main/java/im/vector/app/core/ui/views/KnownCallsViewHolder.kt +++ /dev/null @@ -1,114 +0,0 @@ -/* - * 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 androidx.core.view.isVisible -import com.google.android.material.card.MaterialCardView -import im.vector.app.core.epoxy.onClick -import im.vector.app.features.call.utils.EglUtils -import im.vector.app.features.call.webrtc.WebRtcCall -import org.matrix.android.sdk.api.session.call.CallState -import org.webrtc.RendererCommon -import org.webrtc.SurfaceViewRenderer - -class KnownCallsViewHolder { - - private var activeCallPiP: SurfaceViewRenderer? = null - private var currentCallsView: CurrentCallsView? = null - private var pipWrapper: MaterialCardView? = null - private var currentCall: WebRtcCall? = null - private var calls: List = emptyList() - - private var activeCallPipInitialized = false - - private val tickListener = object : WebRtcCall.Listener { - override fun onTick(formattedDuration: String) { - currentCallsView?.render(calls, formattedDuration) - } - } - - fun updateCall(currentCall: WebRtcCall?, calls: List) { - activeCallPiP?.let { - this.currentCall?.detachRenderers(listOf(it)) - } - this.currentCall?.removeListener(tickListener) - this.currentCall = currentCall - this.currentCall?.addListener(tickListener) - this.calls = calls - val hasActiveCall = currentCall?.mxCall?.state is CallState.Connected - if (hasActiveCall) { - val isVideoCall = currentCall?.mxCall?.isVideoCall == true - if (isVideoCall) initIfNeeded() - currentCallsView?.isVisible = !isVideoCall - currentCallsView?.render(calls, currentCall?.formattedDuration() ?: "") - pipWrapper?.isVisible = isVideoCall - activeCallPiP?.isVisible = isVideoCall - activeCallPiP?.let { - currentCall?.attachViewRenderers(null, it, null) - } - } else { - currentCallsView?.isVisible = false - activeCallPiP?.isVisible = false - pipWrapper?.isVisible = false - activeCallPiP?.let { - currentCall?.detachRenderers(listOf(it)) - } - } - } - - private fun initIfNeeded() { - if (!activeCallPipInitialized && activeCallPiP != null) { - activeCallPiP?.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL) - EglUtils.rootEglBase?.let { eglBase -> - activeCallPiP?.init(eglBase.eglBaseContext, null) - activeCallPiP?.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_BALANCED) - activeCallPiP?.setEnableHardwareScaler(true /* enabled */) - activeCallPiP?.setZOrderMediaOverlay(true) - activeCallPipInitialized = true - } - } - } - - fun bind(activeCallPiP: SurfaceViewRenderer, - activeCallView: CurrentCallsView, - pipWrapper: MaterialCardView, - interactionListener: CurrentCallsView.Callback) { - this.activeCallPiP = activeCallPiP - this.currentCallsView = activeCallView - this.pipWrapper = pipWrapper - this.currentCallsView?.callback = interactionListener - pipWrapper.onClick { - interactionListener.onTapToReturnToCall() - } - this.currentCall?.addListener(tickListener) - } - - fun unBind() { - activeCallPiP?.let { - currentCall?.detachRenderers(listOf(it)) - } - if (activeCallPipInitialized) { - activeCallPiP?.release() - } - this.currentCallsView?.callback = null - this.currentCall?.removeListener(tickListener) - pipWrapper?.setOnClickListener(null) - activeCallPiP = null - currentCallsView = null - pipWrapper = null - } -} diff --git a/vector/src/main/java/im/vector/app/features/call/CallControlsBottomSheet.kt b/vector/src/main/java/im/vector/app/features/call/CallControlsBottomSheet.kt index a3e3a5d71d..c4b2c8370e 100644 --- a/vector/src/main/java/im/vector/app/features/call/CallControlsBottomSheet.kt +++ b/vector/src/main/java/im/vector/app/features/call/CallControlsBottomSheet.kt @@ -73,7 +73,6 @@ class CallControlsBottomSheet : VectorBaseBottomSheetDialogFragment { - views.ringingControls.isVisible = true - views.ringingControlAccept.isVisible = false - views.ringingControlDecline.isVisible = true - views.connectedControls.isVisible = false - } is CallState.LocalRinging -> { views.ringingControls.isVisible = true views.ringingControlAccept.isVisible = true views.ringingControlDecline.isVisible = true views.connectedControls.isVisible = false } - is CallState.Connected -> { - if (callState.iceConnectionState == MxPeerConnectionState.CONNECTED) { - views.ringingControls.isVisible = false - views.connectedControls.isVisible = true - views.videoToggleIcon.isVisible = state.isVideoCall - } else { - views.ringingControls.isVisible = true - views.ringingControlAccept.isVisible = false - views.ringingControlDecline.isVisible = true - views.connectedControls.isVisible = false - } + is CallState.Connected, + is CallState.Dialing, + is CallState.Answering -> { + views.ringingControls.isVisible = false + views.connectedControls.isVisible = true + views.videoToggleIcon.isVisible = state.isVideoCall + views.moreIcon.isVisible = callState is CallState.Connected && callState.iceConnectionState == MxPeerConnectionState.CONNECTED } - is CallState.Terminated, - null -> { + else -> { views.ringingControls.isVisible = false views.connectedControls.isVisible = false } diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt index 602a68ea74..8bbc54f070 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallActivity.kt @@ -35,6 +35,7 @@ import com.airbnb.mvrx.Fail import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.viewModel import com.airbnb.mvrx.withState +import com.google.android.material.card.MaterialCardView import com.google.android.material.dialog.MaterialAlertDialogBuilder import im.vector.app.R import im.vector.app.core.di.ScreenComponent @@ -53,6 +54,8 @@ import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.room.detail.RoomDetailActivity import im.vector.app.features.home.room.detail.RoomDetailArgs +import io.github.hyuwah.draggableviewlib.DraggableView +import io.github.hyuwah.draggableviewlib.setupDraggable import io.reactivex.android.schedulers.AndroidSchedulers import kotlinx.parcelize.Parcelize import org.matrix.android.sdk.api.extensions.orFalse @@ -96,6 +99,7 @@ class VectorCallActivity : VectorBaseActivity(), CallContro } private var rootEglBase: EglBase? = null + private var pipDraggrableView: DraggableView? = null var surfaceRenderersAreInitialized = false @@ -188,19 +192,19 @@ class VectorCallActivity : VectorBaseActivity(), CallContro is CallState.Idle, is CallState.CreateOffer, is CallState.LocalRinging, - is CallState.Dialing -> { + is CallState.Dialing -> { views.callVideoGroup.isInvisible = true views.callInfoGroup.isVisible = true views.callToolbar.setSubtitle(R.string.call_ring) configureCallInfo(state) } - is CallState.Answering -> { + is CallState.Answering -> { views.callVideoGroup.isInvisible = true views.callInfoGroup.isVisible = true views.callToolbar.setSubtitle(R.string.call_connecting) configureCallInfo(state) } - is CallState.Connected -> { + is CallState.Connected -> { views.callToolbar.subtitle = state.formattedDuration if (callState.iceConnectionState == MxPeerConnectionState.CONNECTED) { if (state.isLocalOnHold || state.isRemoteOnHold) { @@ -248,10 +252,10 @@ class VectorCallActivity : VectorBaseActivity(), CallContro views.callToolbar.setSubtitle(R.string.call_connecting) } } - is CallState.Terminated -> { + is CallState.Terminated -> { finish() } - null -> { + null -> { } } } @@ -290,7 +294,7 @@ class VectorCallActivity : VectorBaseActivity(), CallContro addPlaceholder = true ) views.otherKnownCallLayout.isVisible = true - views.otherSmallIsHeldIcon.isVisible = otherCall?.let { it.isLocalOnHold || it.remoteOnHold }.orFalse() + views.otherSmallIsHeldIcon.isVisible = otherCall?.let { it.isLocalOnHold || it.isRemoteOnHold }.orFalse() } } @@ -303,6 +307,7 @@ class VectorCallActivity : VectorBaseActivity(), CallContro finish() } } + pipDraggrableView = views.pipRendererWrapper.setupDraggable().build() } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { @@ -322,21 +327,21 @@ class VectorCallActivity : VectorBaseActivity(), CallContro } // Init Picture in Picture renderer - views.pipRenderer.init(rootEglBase!!.eglBaseContext, null) - views.pipRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT) - + views.pipRenderer.apply { + init(rootEglBase!!.eglBaseContext, null) + setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_BALANCED) + setEnableHardwareScaler(true) + setZOrderMediaOverlay(true) + } // Init Full Screen renderer views.fullscreenRenderer.init(rootEglBase!!.eglBaseContext, null) views.fullscreenRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL) - - views.pipRenderer.setZOrderMediaOverlay(true) - views.pipRenderer.setEnableHardwareScaler(true /* enabled */) views.fullscreenRenderer.setEnableHardwareScaler(true /* enabled */) callManager.getCallById(callArgs.callId)?.attachViewRenderers(views.pipRenderer, views.fullscreenRenderer, intent.getStringExtra(EXTRA_MODE)?.takeIf { isFirstCreation() }) - views.pipRenderer.setOnClickListener { + views.pipRendererWrapper.setOnClickListener { callViewModel.handle(VectorCallViewActions.ToggleCamera) } surfaceRenderersAreInitialized = true diff --git a/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt b/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt index e7b2b629e1..4c268ac8fb 100644 --- a/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/call/VectorCallViewModel.kt @@ -60,7 +60,7 @@ class VectorCallViewModel @AssistedInject constructor( setState { copy( isLocalOnHold = call?.isLocalOnHold ?: false, - isRemoteOnHold = call?.remoteOnHold ?: false + isRemoteOnHold = call?.isRemoteOnHold ?: false ) } } @@ -189,12 +189,14 @@ class VectorCallViewModel @AssistedInject constructor( } setState { copy( + isAudioMuted = webRtcCall.micMuted, + isVideoEnabled = !webRtcCall.videoMuted, isVideoCall = webRtcCall.mxCall.isVideoCall, callState = Success(webRtcCall.mxCall.state), callInfo = webRtcCall.extractCallInfo(), device = currentSoundDevice ?: CallAudioManager.Device.PHONE, isLocalOnHold = webRtcCall.isLocalOnHold, - isRemoteOnHold = webRtcCall.remoteOnHold, + isRemoteOnHold = webRtcCall.isRemoteOnHold, availableDevices = callManager.audioManager.availableDevices, isFrontCamera = webRtcCall.currentCameraType() == CameraType.FRONT, canSwitchCamera = webRtcCall.canSwitchCamera(), diff --git a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt index d3a9dd7edf..6d838b2812 100644 --- a/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt +++ b/vector/src/main/java/im/vector/app/features/call/webrtc/WebRtcCall.kt @@ -164,7 +164,7 @@ class WebRtcCall( private set var videoMuted = false private set - var remoteOnHold = false + var isRemoteOnHold = false private set var isLocalOnHold = false private set @@ -586,12 +586,12 @@ class WebRtcCall( } private fun updateMuteStatus() { - val micShouldBeMuted = micMuted || remoteOnHold + val micShouldBeMuted = micMuted || isRemoteOnHold localAudioTrack?.setEnabled(!micShouldBeMuted) - remoteAudioTrack?.setEnabled(!remoteOnHold) - val vidShouldBeMuted = videoMuted || remoteOnHold + remoteAudioTrack?.setEnabled(!isRemoteOnHold) + val vidShouldBeMuted = videoMuted || isRemoteOnHold localVideoTrack?.setEnabled(!vidShouldBeMuted) - remoteVideoTrack?.setEnabled(!remoteOnHold) + remoteVideoTrack?.setEnabled(!isRemoteOnHold) } /** @@ -617,16 +617,16 @@ class WebRtcCall( fun updateRemoteOnHold(onHold: Boolean) { sessionScope?.launch(dispatcher) { - if (remoteOnHold == onHold) return@launch + if (isRemoteOnHold == onHold) return@launch val direction: RtpTransceiver.RtpTransceiverDirection if (onHold) { wasLocalOnHold = isLocalOnHold - remoteOnHold = true + isRemoteOnHold = true isLocalOnHold = true direction = RtpTransceiver.RtpTransceiverDirection.SEND_ONLY timer.pause() } else { - remoteOnHold = false + isRemoteOnHold = false isLocalOnHold = wasLocalOnHold onCallBecomeActive(this@WebRtcCall) direction = RtpTransceiver.RtpTransceiverDirection.SEND_RECV 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 dda3df3881..f06b39fff3 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 @@ -22,7 +22,6 @@ import android.view.Menu import android.view.MenuItem import android.view.View import android.view.ViewGroup -import androidx.core.view.get import androidx.core.view.isVisible import androidx.core.view.iterator import androidx.fragment.app.Fragment @@ -38,9 +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.CurrentCallsView +import im.vector.app.core.ui.views.CurrentCallsCardView import im.vector.app.core.ui.views.KeysBackupBanner -import im.vector.app.core.ui.views.KnownCallsViewHolder import im.vector.app.databinding.FragmentHomeDetailBinding import im.vector.app.features.call.SharedKnownCallsViewModel import im.vector.app.features.call.VectorCallActivity @@ -58,6 +56,7 @@ 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 @@ -70,11 +69,12 @@ class HomeDetailFragment @Inject constructor( private val colorProvider: ColorProvider, private val alertManager: PopupAlertManager, private val callManager: WebRtcCallManager, - private val vectorPreferences: VectorPreferences + private val vectorPreferences: VectorPreferences, + private val session: Session ) : VectorBaseFragment(), KeysBackupBanner.Delegate, - CurrentCallsView.Callback, - ServerBackupStatusViewModel.Factory { + ServerBackupStatusViewModel.Factory, + CurrentCallsCardView.Callback { private val viewModel: HomeDetailViewModel by fragmentViewModel() private val unknownDeviceDetectorSharedViewModel: UnknownDeviceDetectorSharedViewModel by activityViewModel() @@ -117,8 +117,6 @@ class HomeDetailFragment @Inject constructor( return FragmentHomeDetailBinding.inflate(inflater, container, false) } - private val activeCallViewHolder = KnownCallsViewHolder() - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) sharedActionViewModel = activityViewModelProvider.get(HomeSharedActionViewModel::class.java) @@ -190,7 +188,7 @@ class HomeDetailFragment @Inject constructor( sharedCallActionViewModel .liveKnownCalls .observe(viewLifecycleOwner, { - activeCallViewHolder.updateCall(callManager.getCurrentCall(), callManager.getCalls()) + views.currentCallsCardView.render(callManager.getCurrentCall(), callManager.getCalls()) invalidateOptionsMenu() }) } @@ -291,12 +289,11 @@ class HomeDetailFragment @Inject constructor( } private fun setupActiveCallView() { - activeCallViewHolder.bind( - views.activeCallPiP, - views.activeCallView, - views.activeCallPiPWrap, - this - ) + views.currentCallsCardView.apply { + this.avatarRenderer = this@HomeDetailFragment.avatarRenderer + this.session = this@HomeDetailFragment.session + this.callback = this@HomeDetailFragment + } } 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 3d99dc785f..636abab793 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 @@ -90,9 +90,8 @@ 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.ActiveConferenceView -import im.vector.app.core.ui.views.CurrentCallsView +import im.vector.app.core.ui.views.CurrentCallsCardView import im.vector.app.core.ui.views.FailedMessagesWarningView -import im.vector.app.core.ui.views.KnownCallsViewHolder import im.vector.app.core.ui.views.NotificationAreaView import im.vector.app.core.utils.Debouncer import im.vector.app.core.utils.DimensionConverter @@ -239,7 +238,7 @@ class RoomDetailFragment @Inject constructor( AttachmentTypeSelectorView.Callback, AttachmentsHelper.Callback, GalleryOrCameraDialogHelper.Listener, - CurrentCallsView.Callback { + CurrentCallsCardView.Callback { companion object { /** @@ -298,7 +297,6 @@ class RoomDetailFragment @Inject constructor( private lateinit var attachmentTypeSelector: AttachmentTypeSelectorView private var lockSendButton = false - private val knownCallsViewHolder = KnownCallsViewHolder() private lateinit var emojiPopup: EmojiPopup @@ -344,7 +342,7 @@ class RoomDetailFragment @Inject constructor( knownCallsViewModel .liveKnownCalls .observe(viewLifecycleOwner, { - knownCallsViewHolder.updateCall(callManager.getCurrentCall(), it) + views.currentCallsCardView.render(callManager.getCurrentCall(), it) invalidateOptionsMenu() }) @@ -687,7 +685,6 @@ class RoomDetailFragment @Inject constructor( override fun onDestroyView() { timelineEventController.callback = null timelineEventController.removeModelBuildListener(modelBuildListener) - views.activeCallView.callback = null modelBuildListener = null autoCompleter.clear() debouncer.cancelAll() @@ -698,7 +695,6 @@ class RoomDetailFragment @Inject constructor( } override fun onDestroy() { - knownCallsViewHolder.unBind() roomDetailViewModel.handle(RoomDetailAction.ExitTrackingUnreadMessagesState) super.onDestroy() } @@ -734,12 +730,11 @@ class RoomDetailFragment @Inject constructor( } private fun setupActiveCallView() { - knownCallsViewHolder.bind( - views.activeCallPiP, - views.activeCallView, - views.activeCallPiPWrap, - this - ) + views.currentCallsCardView.apply { + this.callback = this@RoomDetailFragment + this.avatarRenderer = this@RoomDetailFragment.avatarRenderer + this.session = this@RoomDetailFragment.session + } } private fun navigateToEvent(action: RoomDetailViewEvents.NavigateToEvent) { diff --git a/vector/src/main/res/drawable/ic_call_video_small.xml b/vector/src/main/res/drawable/ic_call_video_small.xml index abb2d85719..5e2ae23598 100644 --- a/vector/src/main/res/drawable/ic_call_video_small.xml +++ b/vector/src/main/res/drawable/ic_call_video_small.xml @@ -9,4 +9,5 @@ + diff --git a/vector/src/main/res/drawable/ic_call_videocam_off_active.xml b/vector/src/main/res/drawable/ic_call_videocam_off_active.xml deleted file mode 100644 index 106317ed56..0000000000 --- a/vector/src/main/res/drawable/ic_call_videocam_off_active.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - diff --git a/vector/src/main/res/drawable/ic_call_videocam_off_default.xml b/vector/src/main/res/drawable/ic_call_videocam_off_default.xml deleted file mode 100644 index 0b3d9baf04..0000000000 --- a/vector/src/main/res/drawable/ic_call_videocam_off_default.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - diff --git a/vector/src/main/res/drawable/ic_mic_off.xml b/vector/src/main/res/drawable/ic_mic_off.xml new file mode 100644 index 0000000000..8e199f115a --- /dev/null +++ b/vector/src/main/res/drawable/ic_mic_off.xml @@ -0,0 +1,10 @@ + + + diff --git a/vector/src/main/res/drawable/ic_mic_on.xml b/vector/src/main/res/drawable/ic_mic_on.xml new file mode 100644 index 0000000000..791b475704 --- /dev/null +++ b/vector/src/main/res/drawable/ic_mic_on.xml @@ -0,0 +1,10 @@ + + + diff --git a/vector/src/main/res/drawable/ic_microphone_off.xml b/vector/src/main/res/drawable/ic_microphone_off.xml deleted file mode 100644 index 92d5044902..0000000000 --- a/vector/src/main/res/drawable/ic_microphone_off.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - diff --git a/vector/src/main/res/drawable/ic_microphone_on.xml b/vector/src/main/res/drawable/ic_microphone_on.xml deleted file mode 100644 index aaa9987860..0000000000 --- a/vector/src/main/res/drawable/ic_microphone_on.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/vector/src/main/res/drawable/ic_video.xml b/vector/src/main/res/drawable/ic_video.xml index d751601281..e5f4eacedb 100644 --- a/vector/src/main/res/drawable/ic_video.xml +++ b/vector/src/main/res/drawable/ic_video.xml @@ -1,10 +1,11 @@ - - + + + + + diff --git a/vector/src/main/res/drawable/ic_videocam.xml b/vector/src/main/res/drawable/ic_videocam.xml deleted file mode 100644 index b7a50f9a57..0000000000 --- a/vector/src/main/res/drawable/ic_videocam.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - diff --git a/vector/src/main/res/layout/activity_call.xml b/vector/src/main/res/layout/activity_call.xml index 94794799c4..0a6c89426f 100644 --- a/vector/src/main/res/layout/activity_call.xml +++ b/vector/src/main/res/layout/activity_call.xml @@ -29,15 +29,24 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> - + app:layout_constraintBottom_toTopOf="@id/callControlsView"> + + + + - - + app:layout_constraintTop_toBottomOf="@+id/homeKeysBackupBanner" /> - + android:foreground="?attr/selectableItemBackground" + app:cardCornerRadius="@dimen/call_pip_radius" + app:layout_constraintBottom_toTopOf="@+id/bottomNavigationView" + app:layout_constraintEnd_toEndOf="parent" /> - - - - - - + app:layout_constraintBottom_toTopOf="@id/failedMessagesWarningView"/> - - - @@ -102,8 +102,8 @@ android:clickable="true" android:contentDescription="@string/a11y_stop_camera" android:focusable="true" - android:padding="16dp" - android:src="@drawable/ic_call_videocam_off_default" + android:padding="12dp" + android:src="@drawable/ic_video" app:backgroundTint="?android:colorBackground" app:tint="?vctr_content_primary" tools:ignore="MissingConstraints,MissingPrefix" /> @@ -139,7 +139,7 @@ - - - - -