From 2b318152fa2ab122fb156fd6ad6e5ebdfb0d7c87 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Thu, 14 Dec 2023 10:29:48 -0500 Subject: [PATCH] Fix call buttons overflowing bottom sheet. --- .../components/webrtc/WebRtcCallView.java | 51 ++--------------- .../components/webrtc/WebRtcControls.java | 18 ++++-- .../controls/ControlsAndInfoController.kt | 57 +++++++++++++++++++ .../main/res/layout/webrtc_call_controls.xml | 26 +++------ 4 files changed, 84 insertions(+), 68 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallView.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallView.java index 6665589f81..a55948be76 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcCallView.java @@ -136,10 +136,8 @@ public class WebRtcCallView extends InsetAwareConstraintLayout { private final Set topViews = new HashSet<>(); private final Set visibleViewSet = new HashSet<>(); private final Set allTimeVisibleViews = new HashSet<>(); - private final Set adjustableMarginsSet = new HashSet<>(); private final Set rotatableControls = new HashSet<>(); - private final ThrottledDebouncer throttledDebouncer = new ThrottledDebouncer(TRANSITION_DURATION_MILLIS); private WebRtcControls controls = WebRtcControls.NONE; @@ -243,11 +241,6 @@ public class WebRtcCallView extends InsetAwareConstraintLayout { incomingCallViews.add(footerGradient); incomingCallViews.add(incomingRingStatus); - adjustableMarginsSet.add(micToggle); - adjustableMarginsSet.add(cameraDirectionToggle); - adjustableMarginsSet.add(videoToggle); - adjustableMarginsSet.add(audioToggle); - audioToggle.setOnAudioOutputChangedListener(webRtcAudioDevice -> { runIfNonNull(controlsListener, listener -> { @@ -703,8 +696,6 @@ public class WebRtcCallView extends InsetAwareConstraintLayout { } if (webRtcControls.displayAudioToggle()) { - visibleViewSet.add(audioToggle); - audioToggle.setControlAvailability(webRtcControls.isEarpieceAvailableForAudioToggle(), webRtcControls.isBluetoothHeadsetAvailableForAudioToggle(), webRtcControls.isWiredHeadsetAvailableForAudioToggle()); @@ -712,30 +703,9 @@ public class WebRtcCallView extends InsetAwareConstraintLayout { audioToggle.updateAudioOutputState(webRtcControls.getAudioOutput()); } - if (webRtcControls.displayCameraToggle()) { - visibleViewSet.add(cameraDirectionToggle); - } - - if (webRtcControls.displayEndCall()) { - visibleViewSet.add(hangup); - visibleViewSet.add(footerGradient); - } - - if (webRtcControls.displayOverflow()) { - visibleViewSet.add(overflow); - } - - if (webRtcControls.displayMuteAudio()) { - visibleViewSet.add(micToggle); - } - - if (webRtcControls.displayVideoToggle()) { - visibleViewSet.add(videoToggle); - } - - if (webRtcControls.displaySmallOngoingCallButtons()) { + if (webRtcControls.displaySmallCallButtons()) { updateButtonStateForSmallButtons(); - } else if (webRtcControls.displayLargeOngoingCallButtons()) { + } else { updateButtonStateForLargeButtons(); } @@ -753,10 +723,6 @@ public class WebRtcCallView extends InsetAwareConstraintLayout { fullScreenShade.setVisibility(GONE); } - if (webRtcControls.displayRingToggle()) { - visibleViewSet.add(ringToggle); - } - if (webRtcControls.displayReactions()) { visibleViewSet.add(reactionViews); visibleViewSet.add(groupReactionsFeed); @@ -777,7 +743,7 @@ public class WebRtcCallView extends InsetAwareConstraintLayout { (!webRtcControls.showSmallHeader() && largeHeaderAvatar.getVisibility() == View.GONE) || forceUpdate) { - throttledDebouncer.publish(() -> fadeInNewUiState(webRtcControls.displaySmallOngoingCallButtons(), webRtcControls.showSmallHeader())); + throttledDebouncer.publish(() -> fadeInNewUiState(webRtcControls.showSmallHeader())); } onWindowSystemUiVisibilityChanged(getWindowSystemUiVisibility()); @@ -871,20 +837,13 @@ public class WebRtcCallView extends InsetAwareConstraintLayout { constraintSet.applyTo(this); } - private void fadeInNewUiState(boolean useSmallMargins, boolean showSmallHeader) { + private void fadeInNewUiState(boolean showSmallHeader) { for (View view : SetUtil.difference(allTimeVisibleViews, visibleViewSet)) { view.setVisibility(GONE); } for (View view : visibleViewSet) { view.setVisibility(VISIBLE); - - if (adjustableMarginsSet.contains(view)) { - MarginLayoutParams params = (MarginLayoutParams) view.getLayoutParams(); - params.setMarginEnd(ViewUtil.dpToPx(useSmallMargins ? SMALL_ONGOING_CALL_BUTTON_MARGIN_DP - : LARGE_ONGOING_CALL_BUTTON_MARGIN_DP)); - view.setLayoutParams(params); - } } if (showSmallHeader) { @@ -918,6 +877,7 @@ public class WebRtcCallView extends InsetAwareConstraintLayout { videoToggle.setBackgroundResource(R.drawable.webrtc_call_screen_video_toggle); audioToggle.setImageResource(R.drawable.webrtc_call_screen_speaker_toggle); ringToggle.setBackgroundResource(R.drawable.webrtc_call_screen_ring_toggle); + overflow.setBackgroundResource(R.drawable.webrtc_call_screen_overflow_menu); } private void updateButtonStateForSmallButtons() { @@ -928,6 +888,7 @@ public class WebRtcCallView extends InsetAwareConstraintLayout { videoToggle.setBackgroundResource(R.drawable.webrtc_call_screen_video_toggle_small); audioToggle.setImageResource(R.drawable.webrtc_call_screen_speaker_toggle_small); ringToggle.setBackgroundResource(R.drawable.webrtc_call_screen_ring_toggle_small); + overflow.setBackgroundResource(R.drawable.webrtc_call_screen_overflow_menu_small); } public void switchToSpeakerView() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcControls.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcControls.java index 4490963072..2a7b867df1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcControls.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/WebRtcControls.java @@ -204,12 +204,8 @@ public final class WebRtcControls { return isAtLeastOutgoing() && isRemoteVideoEnabled && callState != CallState.RECONNECTING; } - public boolean displaySmallOngoingCallButtons() { - return isAtLeastOutgoing() && displayAudioToggle() && displayCameraToggle(); - } - - public boolean displayLargeOngoingCallButtons() { - return isAtLeastOutgoing() && !(displayAudioToggle() && displayCameraToggle()); + public boolean displaySmallCallButtons() { + return displayedButtonCount() >= 5; } public boolean displayTopViews() { @@ -269,6 +265,16 @@ public final class WebRtcControls { return groupCallState != GroupCallState.NONE; } + private int displayedButtonCount() { + return (displayAudioToggle() ? 1 : 0) + + (displayCameraToggle() ? 1 : 0) + + (displayVideoToggle() ? 1 : 0) + + (displayMuteAudio() ? 1 : 0) + + (displayRingToggle() ? 1 : 0) + + (displayOverflow() ? 1 : 0) + + (displayEndCall() ? 1 : 0); + } + public enum CallState { NONE, ERROR, diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlsAndInfoController.kt b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlsAndInfoController.kt index 6ba6377c2d..f058965c95 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlsAndInfoController.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/controls/ControlsAndInfoController.kt @@ -9,14 +9,20 @@ import android.content.res.ColorStateList import android.os.Handler import android.view.View import android.widget.FrameLayout +import androidx.annotation.IdRes +import androidx.annotation.Px import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.compose.ui.platform.rememberNestedScrollInteropConnection import androidx.constraintlayout.widget.ConstraintLayout +import androidx.constraintlayout.widget.ConstraintSet import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.core.content.ContextCompat +import androidx.transition.AutoTransition +import androidx.transition.TransitionManager +import androidx.transition.TransitionSet import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback import com.google.android.material.bottomsheet.BottomSheetBehaviorHack @@ -52,6 +58,7 @@ class ControlsAndInfoController( private const val CONTROL_FADE_OUT_DONE = 0.23f private const val INFO_FADE_IN_START = CONTROL_FADE_OUT_DONE private const val INFO_FADE_IN_DONE = 0.8f + private const val CONTROL_TRANSITION_DURATION = 250L private val HIDE_CONTROL_DELAY = 5.seconds.inWholeMilliseconds } @@ -215,6 +222,14 @@ class ControlsAndInfoController( val previousState = controlState controlState = newControlState + showOrHideControlsOnUpdate(previousState) + + if (controlState.controlVisibilitiesChanged(previousState)) { + updateControlVisibilities() + } + } + + private fun showOrHideControlsOnUpdate(previousState: WebRtcControls) { if (controlState == WebRtcControls.PIP) { hide() return @@ -241,6 +256,32 @@ class ControlsAndInfoController( } } + private fun updateControlVisibilities() { + TransitionManager.endTransitions(callControls) + TransitionManager.beginDelayedTransition( + callControls, + AutoTransition().apply { + ordering = TransitionSet.ORDERING_TOGETHER + duration = CONTROL_TRANSITION_DURATION + } + ) + + val constraints = ConstraintSet().apply { + clone(callControls) + val margin = if (controlState.displaySmallCallButtons()) 4.dp else 8.dp + + setControlConstraints(R.id.call_screen_speaker_toggle, controlState.displayAudioToggle(), margin) + setControlConstraints(R.id.call_screen_camera_direction_toggle, controlState.displayCameraToggle(), margin) + setControlConstraints(R.id.call_screen_video_toggle, controlState.displayVideoToggle(), margin) + setControlConstraints(R.id.call_screen_audio_mic_toggle, controlState.displayMuteAudio(), margin) + setControlConstraints(R.id.call_screen_audio_ring_toggle, controlState.displayRingToggle(), margin) + setControlConstraints(R.id.call_screen_overflow_button, controlState.displayOverflow(), margin) + setControlConstraints(R.id.call_screen_end_call, controlState.displayEndCall(), margin) + } + + constraints.applyTo(callControls) + } + private fun onScheduledHide() { if (behavior.state != BottomSheetBehavior.STATE_EXPANDED && !isDisposed) { hide() @@ -279,6 +320,22 @@ class ControlsAndInfoController( return disposables.isDisposed } + private fun ConstraintSet.setControlConstraints(@IdRes viewId: Int, visible: Boolean, @Px horizontalMargins: Int) { + setVisibility(viewId, if (visible) View.VISIBLE else View.GONE) + setMargin(viewId, ConstraintSet.START, horizontalMargins) + setMargin(viewId, ConstraintSet.END, horizontalMargins) + } + + private fun WebRtcControls.controlVisibilitiesChanged(previousState: WebRtcControls): Boolean { + return displayAudioToggle() != previousState.displayAudioToggle() || + displayCameraToggle() != previousState.displayCameraToggle() || + displayVideoToggle() != previousState.displayVideoToggle() || + displayMuteAudio() != previousState.displayMuteAudio() || + displayRingToggle() != previousState.displayRingToggle() || + displayOverflow() != previousState.displayOverflow() || + displayEndCall() != previousState.displayEndCall() + } + interface BottomSheetVisibilityListener { fun onShown() fun onHidden() diff --git a/app/src/main/res/layout/webrtc_call_controls.xml b/app/src/main/res/layout/webrtc_call_controls.xml index ac3ce2c683..1b62cad7e9 100644 --- a/app/src/main/res/layout/webrtc_call_controls.xml +++ b/app/src/main/res/layout/webrtc_call_controls.xml @@ -36,15 +36,14 @@ android:id="@+id/call_info_compose" android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingTop="38dp" - android:orientation="vertical" /> + android:paddingTop="38dp" /> + android:layout_marginTop="38dp" + android:paddingBottom="48dp"> + app:srcCompat="@drawable/webrtc_call_screen_camera_toggle" /> + tools:checked="true" /> + app:layout_constraintStart_toEndOf="@id/call_screen_video_toggle" /> + app:layout_constraintStart_toEndOf="@id/call_screen_audio_mic_toggle" + tools:visibility="visible" /> -