diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/PictureInPictureExpansionHelper.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/PictureInPictureExpansionHelper.java index 532165e7af..df57b93a8d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/PictureInPictureExpansionHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/PictureInPictureExpansionHelper.java @@ -36,11 +36,14 @@ final class PictureInPictureExpansionHelper { private Point defaultDimensions; private Point expandedDimensions; - public PictureInPictureExpansionHelper(@NonNull View selfPip) { - this.selfPip = selfPip; - this.parent = (ViewGroup) selfPip.getParent(); - this.defaultDimensions = new Point(selfPip.getLayoutParams().width, selfPip.getLayoutParams().height); - this.expandedDimensions = new Point(ViewUtil.dpToPx(EXPANDED_PIP_WIDTH_DP), ViewUtil.dpToPx(EXPANDED_PIP_HEIGHT_DP)); + private final OnStateChangedListener onStateChangedListener; + + public PictureInPictureExpansionHelper(@NonNull View selfPip, @NonNull OnStateChangedListener onStateChangedListener) { + this.selfPip = selfPip; + this.parent = (ViewGroup) selfPip.getParent(); + this.defaultDimensions = new Point(selfPip.getLayoutParams().width, selfPip.getLayoutParams().height); + this.expandedDimensions = new Point(ViewUtil.dpToPx(EXPANDED_PIP_WIDTH_DP), ViewUtil.dpToPx(EXPANDED_PIP_HEIGHT_DP)); + this.onStateChangedListener = onStateChangedListener; } public boolean isExpandedOrExpanding() { @@ -82,12 +85,12 @@ final class PictureInPictureExpansionHelper { beginResizeSelfPipTransition(expandedDimensions, new Callback() { @Override public void onAnimationWillStart() { - state = State.IS_EXPANDING; + setState(State.IS_EXPANDING); } @Override public void onAnimationHasFinished() { - state = State.IS_EXPANDED; + setState(State.IS_EXPANDED); } }); } @@ -100,12 +103,12 @@ final class PictureInPictureExpansionHelper { beginResizeSelfPipTransition(defaultDimensions, new Callback() { @Override public void onAnimationWillStart() { - state = State.IS_SHRINKING; + setState(State.IS_SHRINKING); } @Override public void onAnimationHasFinished() { - state = State.IS_SHRUNKEN; + setState(State.IS_SHRUNKEN); } }); } @@ -136,6 +139,14 @@ final class PictureInPictureExpansionHelper { selfPip.setLayoutParams(params); } + private void setState(@NonNull State state) { + this.state = state; + + if (onStateChangedListener != null) { + onStateChangedListener.onStateChanged(state); + } + } + enum State { IS_EXPANDING, IS_EXPANDED, @@ -143,6 +154,10 @@ final class PictureInPictureExpansionHelper { IS_SHRUNKEN } + interface OnStateChangedListener { + void onStateChanged(@NonNull State state); + } + public interface Callback { /** * Called when an animation (shrink or expand) will begin. This happens before any animation diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/PictureInPictureGestureHelper.java b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/PictureInPictureGestureHelper.java index b42feef6a1..5b28fd5243 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/PictureInPictureGestureHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/webrtc/PictureInPictureGestureHelper.java @@ -45,9 +45,12 @@ public class PictureInPictureGestureHelper extends GestureDetector.SimpleOnGestu private int maximumFlingVelocity; private boolean isLockedToBottomEnd; private Interpolator interpolator; - private Corner currentCornerPosition = Corner.BOTTOM_RIGHT; - private int previousTopBoundary = -1; - private int previousBottomBoundary = -1; + private Corner currentCornerPosition = Corner.BOTTOM_RIGHT; + private int previousTopBoundary = -1; + private int expandedVerticalBoundary = -1; + private int collapsedVerticalBoundary = -1; + private BoundaryState boundaryState = BoundaryState.EXPANDED; + private boolean isCollapsedStateAllowed = false; @SuppressLint("ClickableViewAccessibility") public static PictureInPictureGestureHelper applyTo(@NonNull View child) { @@ -124,14 +127,47 @@ public class PictureInPictureGestureHelper extends GestureDetector.SimpleOnGestu child.setLayoutParams(layoutParams); } - public void setBottomVerticalBoundary(int bottomBoundary) { - if (bottomBoundary == previousBottomBoundary) { + public void setCollapsedVerticalBoundary(int bottomBoundary) { + final int oldBoundary = collapsedVerticalBoundary; + collapsedVerticalBoundary = bottomBoundary; + + if (oldBoundary != bottomBoundary && boundaryState == BoundaryState.COLLAPSED) { + applyBottomVerticalBoundary(bottomBoundary); + } + } + + public void setExpandedVerticalBoundary(int bottomBoundary) { + final int oldBoundary = expandedVerticalBoundary; + expandedVerticalBoundary = bottomBoundary; + + if (oldBoundary != bottomBoundary && boundaryState == BoundaryState.EXPANDED) { + applyBottomVerticalBoundary(bottomBoundary); + } + } + + public void setBoundaryState(@NonNull BoundaryState boundaryState) { + if (!isCollapsedStateAllowed && boundaryState == BoundaryState.COLLAPSED) { return; } - previousBottomBoundary = bottomBoundary; + final BoundaryState old = this.boundaryState; + this.boundaryState = boundaryState; + if (old != boundaryState) { + applyBottomVerticalBoundary(boundaryState == BoundaryState.EXPANDED ? expandedVerticalBoundary : collapsedVerticalBoundary); + } + } + + public void allowCollapsedState() { + if (isCollapsedStateAllowed) { + return; + } + + isCollapsedStateAllowed = true; + setBoundaryState(BoundaryState.COLLAPSED); + } + + private void applyBottomVerticalBoundary(int bottomBoundary) { extraPaddingBottom = parent.getMeasuredHeight() + parent.getTop() - bottomBoundary; - ViewUtil.setBottomMargin(child, extraPaddingBottom + framePadding); } @@ -405,4 +441,9 @@ public class PictureInPictureGestureHelper extends GestureDetector.SimpleOnGestu return interpolated; } } + + public enum BoundaryState { + EXPANDED, + COLLAPSED + } } 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 8bd19193c5..b3a5824252 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 @@ -131,6 +131,7 @@ public class WebRtcCallView extends InsetAwareConstraintLayout { private View missingPermissionContainer; private MaterialButton allowAccessButton; private Guideline callParticipantsOverflowGuideline; + private View callControlsSheet; private WebRtcCallParticipantsPagerAdapter pagerAdapter; private WebRtcCallParticipantsRecyclerAdapter recyclerAdapter; @@ -210,6 +211,7 @@ public class WebRtcCallView extends InsetAwareConstraintLayout { missingPermissionContainer = findViewById(R.id.missing_permissions_container); allowAccessButton = findViewById(R.id.allow_access_button); callParticipantsOverflowGuideline = findViewById(R.id.call_screen_participants_overflow_guideline); + callControlsSheet = findViewById(R.id.call_controls_info_parent); View decline = findViewById(R.id.call_screen_decline_call); View answerLabel = findViewById(R.id.call_screen_answer_call_label); @@ -296,7 +298,13 @@ public class WebRtcCallView extends InsetAwareConstraintLayout { answerWithoutVideo.setOnClickListener(v -> runIfNonNull(controlsListener, ControlsListener::onAcceptCallWithVoiceOnlyPressed)); pictureInPictureGestureHelper = PictureInPictureGestureHelper.applyTo(smallLocalRenderFrame); - pictureInPictureExpansionHelper = new PictureInPictureExpansionHelper(smallLocalRenderFrame); + pictureInPictureExpansionHelper = new PictureInPictureExpansionHelper(smallLocalRenderFrame, state -> { + if (state == PictureInPictureExpansionHelper.State.IS_SHRUNKEN) { + pictureInPictureGestureHelper.setBoundaryState(PictureInPictureGestureHelper.BoundaryState.COLLAPSED); + } else { + pictureInPictureGestureHelper.setBoundaryState(PictureInPictureGestureHelper.BoundaryState.EXPANDED); + } + }); smallLocalRenderFrame.setOnClickListener(v -> { if (controlsListener != null) { @@ -370,15 +378,15 @@ public class WebRtcCallView extends InsetAwareConstraintLayout { if (getContext().getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { aboveControls.addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { - pictureInPictureGestureHelper.setBottomVerticalBoundary(bottom + ViewUtil.getStatusBarHeight(v)); - }); - } else { - SlideUpWithCallControlsBehavior behavior = (SlideUpWithCallControlsBehavior) ((CoordinatorLayout.LayoutParams) aboveControls.getLayoutParams()).getBehavior(); - Objects.requireNonNull(behavior).setOnTopOfControlsChangedListener(topOfControls -> { - pictureInPictureGestureHelper.setBottomVerticalBoundary(topOfControls); + pictureInPictureGestureHelper.setCollapsedVerticalBoundary(bottom + ViewUtil.getStatusBarHeight(v)); }); } + SlideUpWithCallControlsBehavior behavior = (SlideUpWithCallControlsBehavior) ((CoordinatorLayout.LayoutParams) aboveControls.getLayoutParams()).getBehavior(); + Objects.requireNonNull(behavior).setOnTopOfControlsChangedListener(topOfControls -> { + pictureInPictureGestureHelper.setExpandedVerticalBoundary(topOfControls); + }); + if (callParticipantsOverflowGuideline != null) { callParticipantsRecycler.addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { callParticipantsOverflowGuideline.setGuidelineEnd(bottom - top); @@ -386,6 +394,19 @@ public class WebRtcCallView extends InsetAwareConstraintLayout { } } + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + + final int pipWidth = smallLocalRenderFrame.getMeasuredWidth(); + final int controlsWidth = callControlsSheet.getMeasuredWidth(); + final float protection = DimensionUnit.DP.toPixels(16 * 4); + final float requiredWidth = pipWidth + controlsWidth + protection; + + if (w > h && w >= requiredWidth) { + pictureInPictureGestureHelper.allowCollapsedState(); + } + } + @Override public WindowInsets onApplyWindowInsets(WindowInsets insets) { navBarBottomInset = WindowInsetsCompat.toWindowInsetsCompat(insets).getInsets(WindowInsetsCompat.Type.navigationBars()).bottom;