From e085aa14ed4e0d686a7965d9780e7b46b3119807 Mon Sep 17 00:00:00 2001 From: Alexander Lussier-Cullen <alexander.lussier-cullen@savoirfairelinux.com> Date: Wed, 20 Dec 2023 13:20:24 -0500 Subject: [PATCH] call: fix camera and screenshare states Add distinct logic and states for handling camera & screenshare video to solve related button state and video switching issues. GitLab: #1075 Change-Id: Id693cd17d6594e561ad4901a1670bb3a6620fc5b --- .../java/cx/ring/fragments/CallFragment.kt | 22 +++++------ .../java/cx/ring/tv/call/TVCallFragment.kt | 7 +++- .../kotlin/net/jami/call/CallPresenter.kt | 37 ++++++++++--------- .../src/main/kotlin/net/jami/call/CallView.kt | 3 +- .../main/kotlin/net/jami/model/Conference.kt | 12 ++++++ 5 files changed, 51 insertions(+), 30 deletions(-) diff --git a/jami-android/app/src/main/java/cx/ring/fragments/CallFragment.kt b/jami-android/app/src/main/java/cx/ring/fragments/CallFragment.kt index ca92ca757..735b0693f 100644 --- a/jami-android/app/src/main/java/cx/ring/fragments/CallFragment.kt +++ b/jami-android/app/src/main/java/cx/ring/fragments/CallFragment.kt @@ -948,7 +948,8 @@ class CallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView, canDial: Boolean, showPluginBtn: Boolean, onGoingCall: Boolean, - hasActiveVideo: Boolean + hasActiveCameraVideo: Boolean, + hasActiveScreenShare: Boolean ) { binding?.apply { pluginsBtnContainer.isVisible = showPluginBtn @@ -957,13 +958,14 @@ class CallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView, dialpadBtnContainer.isVisible = canDial callVideocamBtn.apply { - isChecked = !hasActiveVideo + isChecked = !hasActiveCameraVideo setImageResource(if (isChecked) R.drawable.baseline_videocam_off_24 else R.drawable.baseline_videocam_on_24) } callCameraFlipBtn.apply { isEnabled = !callVideocamBtn.isChecked - setImageResource(if (hasMultipleCamera && hasActiveVideo) R.drawable.baseline_flip_camera_24 else R.drawable.baseline_flip_camera_24_off) + setImageResource(if (hasMultipleCamera && hasActiveCameraVideo) R.drawable.baseline_flip_camera_24 else R.drawable.baseline_flip_camera_24_off) } + callSharescreenBtn.isChecked = hasActiveScreenShare callMicBtn.isChecked = isMicrophoneMuted } } @@ -1267,15 +1269,13 @@ class CallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView, .show() } } - + + override fun startScreenCapture() { + startActivityForResult(mProjectionManager.createScreenCaptureIntent(), REQUEST_CODE_SCREEN_SHARE) + } + fun shareScreenClicked() { - val binding = binding ?: return - if (!binding.callSharescreenBtn.isChecked) { - presenter.stopScreenShare() - displayLocalVideo(true) - } else { - startActivityForResult(mProjectionManager.createScreenCaptureIntent(), REQUEST_CODE_SCREEN_SHARE) - } + presenter.switchOnOffScreenShare() } fun micClicked() { diff --git a/jami-android/app/src/main/java/cx/ring/tv/call/TVCallFragment.kt b/jami-android/app/src/main/java/cx/ring/tv/call/TVCallFragment.kt index 822488a78..810d49cc0 100644 --- a/jami-android/app/src/main/java/cx/ring/tv/call/TVCallFragment.kt +++ b/jami-android/app/src/main/java/cx/ring/tv/call/TVCallFragment.kt @@ -285,7 +285,8 @@ class TVCallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView canDial: Boolean, showPluginBtn: Boolean, onGoingCall: Boolean, - hasActiveVideo: Boolean) { + hasActiveVideo: Boolean, + hasActiveScreenShare: Boolean) { } override fun resetBottomSheetState() {} @@ -517,6 +518,10 @@ class TVCallFragment : BaseSupportFragment<CallPresenter, CallView>(), CallView startActivityForResult(ActionHelper.getAddNumberIntentForContact(contact), ConversationFragment.REQ_ADD_CONTACT) } + override fun startScreenCapture() { + TODO("Not yet implemented") + } + override fun startAddParticipant(conferenceId: String) { startActivityForResult(Intent(Intent.ACTION_PICK) .setClass(requireActivity(), ConversationSelectionActivity::class.java) diff --git a/jami-android/libjamiclient/src/main/kotlin/net/jami/call/CallPresenter.kt b/jami-android/libjamiclient/src/main/kotlin/net/jami/call/CallPresenter.kt index c986deab3..e1277f5d8 100644 --- a/jami-android/libjamiclient/src/main/kotlin/net/jami/call/CallPresenter.kt +++ b/jami-android/libjamiclient/src/main/kotlin/net/jami/call/CallPresenter.kt @@ -59,8 +59,6 @@ class CallPresenter @Inject constructor( fun isSpeakerphoneOn(): Boolean = mHardwareService.isSpeakerphoneOn() var isMicrophoneMuted: Boolean = false var wantVideo = false - var videoIsMuted = false - private set fun isVideoActive(): Boolean = mConference?.hasActiveVideo() == true @@ -207,10 +205,11 @@ class CallPresenter @Inject constructor( val canDial = mOnGoingCall val displayPluginsButton = view?.displayPluginsButton() == true val showPluginBtn = displayPluginsButton && mOnGoingCall - val hasActiveVideo = conference.hasActiveVideo() - val hasMultipleCamera = mHardwareService.cameraCount() > 1 && mOnGoingCall && hasActiveVideo + val hasActiveCameraVideo = conference.hasActiveNonScreenShareVideo() + val hasActiveScreenShare = conference.hasActiveScreenSharing() + val hasMultipleCamera = mHardwareService.cameraCount() > 1 && mOnGoingCall && hasActiveCameraVideo val isConference = conference.isConference - view?.updateBottomSheetButtonStatus(isConference, isSpeakerphoneOn(), conference.isAudioMuted, hasMultipleCamera, canDial, showPluginBtn, mOnGoingCall, hasActiveVideo) + view?.updateBottomSheetButtonStatus(isConference, isSpeakerphoneOn(), conference.isAudioMuted, hasMultipleCamera, canDial, showPluginBtn, mOnGoingCall, hasActiveCameraVideo, hasActiveScreenShare) } fun chatClick() { @@ -255,13 +254,16 @@ class CallPresenter @Inject constructor( fun switchVideoInputClick() { val conference = mConference ?: return - mHardwareService.switchInput(conference.accountId, conference.id) + if(conference.hasActiveNonScreenShareVideo()) + mHardwareService.switchInput(conference.accountId, conference.id) } fun switchOnOffCamera() { val conference = mConference ?: return - videoIsMuted = !videoIsMuted - mCallService.requestVideoMedia(conference, !videoIsMuted) + if(conference.hasActiveScreenSharing()) + mHardwareService.switchInput(conference.accountId, conference.id, true) + else + mCallService.requestVideoMedia(conference, !conference.hasActiveNonScreenShareVideo()) } fun configurationChanged(rotation: Int) { @@ -399,9 +401,7 @@ class CallPresenter @Inject constructor( if (call.isSimpleCall) mCallService.unhold(call.accountId, call.id) else JamiService.addMainParticipant(call.accountId, call.id) } val hasVideo = call.hasVideo() - val hasActiveVideo = call.hasActiveVideo() - val hasActiveScreenShare = call.hasActiveScreenSharing() - videoIsMuted = !hasActiveVideo + val hasActiveCameraVideo = call.hasActiveNonScreenShareVideo() val view = view ?: return if (call.isOnGoing) { mOnGoingCall = true @@ -412,7 +412,7 @@ class CallPresenter @Inject constructor( mHardwareService.updatePreviewVideoSurface(call) videoSurfaceUpdateId(call.id) pluginSurfaceUpdateId(call.pluginId) - view.displayLocalVideo(hasActiveVideo && !hasActiveScreenShare && mDeviceRuntimeService.hasVideoPermission()) + view.displayLocalVideo(hasActiveCameraVideo && mDeviceRuntimeService.hasVideoPermission()) if (permissionChanged) { mHardwareService.switchInput(call.accountId, call.id, permissionChanged) permissionChanged = false @@ -641,6 +641,14 @@ class CallPresenter @Inject constructor( mCallService.raiseHand(call.accountId, call.id, mAccountService.getAccount(call.accountId)?.uri!!, state, getDeviceId()) } + fun switchOnOffScreenShare() { + val conference = mConference ?: return + if(conference.hasActiveScreenSharing()) + mHardwareService.switchInput(conference.accountId, conference.id, true) + else + view?.startScreenCapture() + } + fun startScreenShare(resultCode: Int, data: Any): Boolean { val conference = mConference ?: return false mNotificationService.preparePendingScreenshare(conference) { @@ -650,11 +658,6 @@ class CallPresenter @Inject constructor( return true } - fun stopScreenShare() { - val conference = mConference ?: return - mHardwareService.switchInput(conference.accountId, conference.id, true) - } - fun isMaximized(info: ParticipantInfo): Boolean { return mConference?.maximizedParticipant == info.contact.contact } diff --git a/jami-android/libjamiclient/src/main/kotlin/net/jami/call/CallView.kt b/jami-android/libjamiclient/src/main/kotlin/net/jami/call/CallView.kt index 1800b0498..d04d5b2e5 100644 --- a/jami-android/libjamiclient/src/main/kotlin/net/jami/call/CallView.kt +++ b/jami-android/libjamiclient/src/main/kotlin/net/jami/call/CallView.kt @@ -30,7 +30,7 @@ interface CallView { fun updateAudioState(state: AudioState) fun updateTime(duration: Long) fun updateCallStatus(callState: CallStatus) - fun updateBottomSheetButtonStatus(isConference: Boolean, isSpeakerOn: Boolean, isMicrophoneMuted: Boolean, hasMultipleCamera: Boolean, canDial: Boolean, showPluginBtn: Boolean, onGoingCall: Boolean, hasActiveVideo: Boolean) + fun updateBottomSheetButtonStatus(isConference: Boolean, isSpeakerOn: Boolean, isMicrophoneMuted: Boolean, hasMultipleCamera: Boolean, canDial: Boolean, showPluginBtn: Boolean, onGoingCall: Boolean, hasActiveCameraVideo: Boolean, hasActiveScreenShare: Boolean) fun resetBottomSheetState() fun initNormalStateDisplay() fun initIncomingCallDisplay(hasVideo: Boolean) @@ -38,6 +38,7 @@ interface CallView { fun resetPreviewVideoSize(previewWidth: Int?, previewHeight: Int?, rot: Int) fun goToConversation(accountId: String, conversationId: Uri) fun goToAddContact(contact: Contact) + fun startScreenCapture() fun startAddParticipant(conferenceId: String) fun finish() fun onUserLeave() diff --git a/jami-android/libjamiclient/src/main/kotlin/net/jami/model/Conference.kt b/jami-android/libjamiclient/src/main/kotlin/net/jami/model/Conference.kt index 06454fed1..3579315f9 100644 --- a/jami-android/libjamiclient/src/main/kotlin/net/jami/model/Conference.kt +++ b/jami-android/libjamiclient/src/main/kotlin/net/jami/model/Conference.kt @@ -190,6 +190,18 @@ class Conference(val accountId: String, val id: String) { return false } + fun hasActiveNonScreenShareVideo(): Boolean { + return mParticipants.any { call -> + val mediaList = call.mediaList ?: return@any false + mediaList.any { media -> + media.isEnabled && + !media.isMuted && + media.mediaType == Media.MediaType.MEDIA_TYPE_VIDEO && + media.source != "camera://desktop" + } + } + } + val timestampStart: Long get() { var t = Long.MAX_VALUE -- GitLab