Fix call buttons overflowing bottom sheet.

This commit is contained in:
Cody Henthorne
2023-12-14 10:29:48 -05:00
parent 10a363248e
commit 2b318152fa
4 changed files with 84 additions and 68 deletions

View File

@@ -136,10 +136,8 @@ public class WebRtcCallView extends InsetAwareConstraintLayout {
private final Set<View> topViews = new HashSet<>();
private final Set<View> visibleViewSet = new HashSet<>();
private final Set<View> allTimeVisibleViews = new HashSet<>();
private final Set<View> adjustableMarginsSet = new HashSet<>();
private final Set<View> 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() {

View File

@@ -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,

View File

@@ -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()

View File

@@ -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" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/call_controls_constraint_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="48dp"
android:layout_marginTop="38dp">
android:layout_marginTop="38dp"
android:paddingBottom="48dp">
<org.thoughtcrime.securesms.components.webrtc.WebRtcAudioOutputToggleButton
android:id="@+id/call_screen_speaker_toggle"
@@ -73,13 +72,11 @@
android:clickable="false"
android:contentDescription="@string/WebRtcCallView__toggle_camera_direction"
android:scaleType="fitXY"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/call_screen_start_call_controls"
app:layout_constraintEnd_toStartOf="@id/call_screen_video_toggle"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toEndOf="@id/call_screen_speaker_toggle"
app:srcCompat="@drawable/webrtc_call_screen_camera_toggle"
tools:visibility="visible" />
app:srcCompat="@drawable/webrtc_call_screen_camera_toggle" />
<org.thoughtcrime.securesms.components.AccessibleToggleButton
android:id="@+id/call_screen_video_toggle"
@@ -90,13 +87,11 @@
android:background="@drawable/webrtc_call_screen_video_toggle"
android:contentDescription="@string/WebRtcCallView__toggle_camera"
android:stateListAnimator="@null"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/call_screen_start_call_controls"
app:layout_constraintEnd_toStartOf="@id/call_screen_audio_mic_toggle"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toEndOf="@id/call_screen_camera_direction_toggle"
tools:checked="true"
tools:visibility="visible" />
tools:checked="true" />
<org.thoughtcrime.securesms.components.AccessibleToggleButton
android:id="@+id/call_screen_audio_mic_toggle"
@@ -107,12 +102,10 @@
android:background="@drawable/webrtc_call_screen_mic_toggle"
android:contentDescription="@string/WebRtcCallView__toggle_mute"
android:stateListAnimator="@null"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/call_screen_start_call_controls"
app:layout_constraintEnd_toStartOf="@id/call_screen_audio_ring_toggle"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toEndOf="@id/call_screen_video_toggle"
tools:visibility="visible" />
app:layout_constraintStart_toEndOf="@id/call_screen_video_toggle" />
<org.thoughtcrime.securesms.components.AccessibleToggleButton
android:id="@+id/call_screen_audio_ring_toggle"
@@ -126,7 +119,8 @@
app:layout_constraintBottom_toTopOf="@id/call_screen_start_call_controls"
app:layout_constraintEnd_toStartOf="@id/call_screen_overflow_button"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toEndOf="@id/call_screen_audio_mic_toggle" />
app:layout_constraintStart_toEndOf="@id/call_screen_audio_mic_toggle"
tools:visibility="visible" />
<ImageView
android:id="@+id/call_screen_overflow_button"
@@ -135,19 +129,17 @@
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="30dp"
android:background="@drawable/webrtc_call_screen_overflow_menu"
android:clickable="false"
android:contentDescription="@string/WebRtcCallView__additional_actions"
android:scaleType="fitXY"
android:visibility="gone"
android:background="@drawable/webrtc_call_screen_circle_checked"
app:layout_constraintBottom_toTopOf="@id/call_screen_start_call_controls"
app:layout_constraintEnd_toStartOf="@id/call_screen_end_call"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toEndOf="@id/call_screen_audio_ring_toggle"
app:srcCompat="@drawable/symbol_more_24"
tools:visibility="visible" />
<ImageView
android:id="@+id/call_screen_end_call"
android:layout_width="@dimen/webrtc_button_size"