mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-02-25 04:06:14 +00:00
Move pip above controls when expanded or not enough space in landscape.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user