Skip to content

Commit

Permalink
[SearchBar] Update predictive back device corner logic to handle each…
Browse files Browse the repository at this point in the history
… corner independently to fix issue where bottom corners are incorrectly rounded after canceling back gesture

PiperOrigin-RevId: 677788624
  • Loading branch information
dsn5ft committed Sep 23, 2024
1 parent 9a8ca4d commit 3ce7c2b
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
public class ClippableRoundedCornerLayout extends FrameLayout {

@Nullable private Path path;
private float cornerRadius;
@NonNull private float[] cornerRadii = new float[] {0, 0, 0, 0, 0, 0, 0, 0};

public ClippableRoundedCornerLayout(@NonNull Context context) {
super(context);
Expand All @@ -65,36 +65,37 @@ protected void dispatchDraw(Canvas canvas) {
canvas.restoreToCount(save);
}

public void resetClipBoundsAndCornerRadius() {
public void resetClipBoundsAndCornerRadii() {
path = null;
cornerRadius = 0f;
cornerRadii = new float[] {0, 0, 0, 0, 0, 0, 0, 0};
invalidate();
}

public float getCornerRadius() {
return cornerRadius;
@NonNull
public float[] getCornerRadii() {
return cornerRadii;
}

public void updateCornerRadius(float cornerRadius) {
updateClipBoundsAndCornerRadius(getLeft(), getTop(), getRight(), getBottom(), cornerRadius);
public void updateCornerRadii(@NonNull float[] cornerRadii) {
updateClipBoundsAndCornerRadii(getLeft(), getTop(), getRight(), getBottom(), cornerRadii);
}

public void updateClipBoundsAndCornerRadius(@NonNull Rect rect, float cornerRadius) {
updateClipBoundsAndCornerRadius(rect.left, rect.top, rect.right, rect.bottom, cornerRadius);
public void updateClipBoundsAndCornerRadii(@NonNull Rect rect, @NonNull float[] cornerRadii) {
updateClipBoundsAndCornerRadii(rect.left, rect.top, rect.right, rect.bottom, cornerRadii);
}

public void updateClipBoundsAndCornerRadius(
float left, float top, float right, float bottom, float cornerRadius) {
updateClipBoundsAndCornerRadius(new RectF(left, top, right, bottom), cornerRadius);
public void updateClipBoundsAndCornerRadii(
float left, float top, float right, float bottom, @NonNull float[] cornerRadii) {
updateClipBoundsAndCornerRadii(new RectF(left, top, right, bottom), cornerRadii);
}

public void updateClipBoundsAndCornerRadius(@NonNull RectF rectF, float cornerRadius) {
public void updateClipBoundsAndCornerRadii(@NonNull RectF rectF, @NonNull float[] cornerRadii) {
if (path == null) {
path = new Path();
}
this.cornerRadius = cornerRadius;
this.cornerRadii = cornerRadii;
path.reset();
path.addRoundRect(rectF, cornerRadius, cornerRadius, Path.Direction.CW);
path.addRoundRect(rectF, cornerRadii, Path.Direction.CW);
path.close();
invalidate();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import android.graphics.Rect;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.util.DisplayMetrics;
import android.view.RoundedCorner;
import android.view.View;
import android.view.WindowInsets;
Expand Down Expand Up @@ -61,7 +62,7 @@ public class MaterialMainContainerBackHelper extends MaterialBackAnimationHelper
private float initialTouchY;
@Nullable private Rect initialHideToClipBounds;
@Nullable private Rect initialHideFromClipBounds;
@Nullable private Integer expandedCornerSize;
@Nullable private float[] expandedCornerRadii;

public MaterialMainContainerBackHelper(@NonNull View view) {
super(view);
Expand Down Expand Up @@ -141,7 +142,8 @@ public void updateBackProgress(
view.setTranslationY(translationY);
if (view instanceof ClippableRoundedCornerLayout) {
((ClippableRoundedCornerLayout) view)
.updateCornerRadius(lerp(getExpandedCornerSize(), collapsedCornerSize, progress));
.updateCornerRadii(
lerpCornerRadii(getExpandedCornerRadii(), collapsedCornerSize, progress));
}
}

Expand Down Expand Up @@ -198,41 +200,92 @@ public void onAnimationEnd(Animator animation) {
private ValueAnimator createCornerAnimator(
ClippableRoundedCornerLayout clippableRoundedCornerLayout) {
ValueAnimator cornerAnimator =
ValueAnimator.ofFloat(
clippableRoundedCornerLayout.getCornerRadius(), getExpandedCornerSize());
ValueAnimator.ofObject(
(fraction, startValue, endValue) ->
lerpCornerRadii((float[]) startValue, (float[]) endValue, fraction),
clippableRoundedCornerLayout.getCornerRadii(),
getExpandedCornerRadii());
cornerAnimator.addUpdateListener(
animation ->
clippableRoundedCornerLayout.updateCornerRadius((Float) animation.getAnimatedValue()));
clippableRoundedCornerLayout.updateCornerRadii((float[]) animation.getAnimatedValue()));
return cornerAnimator;
}

public int getExpandedCornerSize() {
if (expandedCornerSize == null) {
expandedCornerSize = isAtTopOfScreen() ? getMaxDeviceCornerRadius() : 0;
private static float[] lerpCornerRadii(float[] startValue, float[] endValue, float fraction) {
return new float[] {
lerp(startValue[0], endValue[0], fraction),
lerp(startValue[1], endValue[1], fraction),
lerp(startValue[2], endValue[2], fraction),
lerp(startValue[3], endValue[3], fraction),
lerp(startValue[4], endValue[4], fraction),
lerp(startValue[5], endValue[5], fraction),
lerp(startValue[6], endValue[6], fraction),
lerp(startValue[7], endValue[7], fraction)
};
}

private static float[] lerpCornerRadii(float[] startValue, float endValue, float fraction) {
return new float[] {
lerp(startValue[0], endValue, fraction),
lerp(startValue[1], endValue, fraction),
lerp(startValue[2], endValue, fraction),
lerp(startValue[3], endValue, fraction),
lerp(startValue[4], endValue, fraction),
lerp(startValue[5], endValue, fraction),
lerp(startValue[6], endValue, fraction),
lerp(startValue[7], endValue, fraction)
};
}

@NonNull
public float[] getExpandedCornerRadii() {
if (expandedCornerRadii == null) {
expandedCornerRadii = calculateExpandedCornerRadii();
}
return expandedCornerSize;
return expandedCornerRadii;
}

private boolean isAtTopOfScreen() {
int[] location = new int[2];
view.getLocationOnScreen(location);
return location[1] == 0;
public void clearExpandedCornerRadii() {
expandedCornerRadii = null;
}

private int getMaxDeviceCornerRadius() {
private float[] calculateExpandedCornerRadii() {
if (VERSION.SDK_INT >= VERSION_CODES.S) {
final WindowInsets insets = view.getRootWindowInsets();
if (insets != null) {
return max(
max(
getRoundedCornerRadius(insets, RoundedCorner.POSITION_TOP_LEFT),
getRoundedCornerRadius(insets, RoundedCorner.POSITION_TOP_RIGHT)),
max(
getRoundedCornerRadius(insets, RoundedCorner.POSITION_BOTTOM_LEFT),
getRoundedCornerRadius(insets, RoundedCorner.POSITION_BOTTOM_RIGHT)));
DisplayMetrics displayMetrics = view.getResources().getDisplayMetrics();
int screenWidth = displayMetrics.widthPixels;
int screenHeight = displayMetrics.heightPixels;

int[] location = new int[2];
view.getLocationOnScreen(location);
int x = location[0];
int y = location[1];

int width = view.getWidth();
int height = view.getHeight();

int topLeft =
x == 0 && y == 0 ? getRoundedCornerRadius(insets, RoundedCorner.POSITION_TOP_LEFT) : 0;
int topRight =
x + width >= screenWidth && y == 0
? getRoundedCornerRadius(insets, RoundedCorner.POSITION_TOP_RIGHT)
: 0;
int bottomRight =
x + width >= screenWidth && y + height >= screenHeight
? getRoundedCornerRadius(insets, RoundedCorner.POSITION_BOTTOM_RIGHT)
: 0;
int bottomLeft =
x == 0 && y + height >= screenHeight
? getRoundedCornerRadius(insets, RoundedCorner.POSITION_BOTTOM_LEFT)
: 0;

return new float[] {
topLeft, topLeft, topRight, topRight, bottomRight, bottomRight, bottomLeft, bottomLeft
};
}
}
return 0;
return new float[] {0, 0, 0, 0, 0, 0, 0, 0};
}

@RequiresApi(VERSION_CODES.S)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,13 @@ public void onAnimationEnd(Animator animation) {
setContentViewsAlpha(show ? 1 : 0);
// After expanding or collapsing, we should reset the clip bounds so it can react to the
// screen or layout changes. Otherwise it will result in wrong clipping on the layout.
rootView.resetClipBoundsAndCornerRadius();
rootView.resetClipBoundsAndCornerRadii();

// After collapsing, we should reset the expanded corner radii in case the search view
// is shown in a different location the next time.
if (!show) {
backHelper.clearExpandedCornerRadii();
}
}
});
return animatorSet;
Expand Down Expand Up @@ -347,22 +353,50 @@ private Animator getRootViewAnimator(boolean show) {
Rect clipBounds = new Rect(fromClipBounds);

float fromCornerRadius = searchBar.getCornerSize();
float toCornerRadius = max(rootView.getCornerRadius(), backHelper.getExpandedCornerSize());
float[] toCornerRadius =
maxCornerRadii(rootView.getCornerRadii(), backHelper.getExpandedCornerRadii());

ValueAnimator animator =
ValueAnimator.ofObject(new RectEvaluator(clipBounds), fromClipBounds, toClipBounds);
animator.addUpdateListener(
valueAnimator -> {
float cornerRadius =
lerp(fromCornerRadius, toCornerRadius, valueAnimator.getAnimatedFraction());
rootView.updateClipBoundsAndCornerRadius(clipBounds, cornerRadius);
float[] cornerRadii =
lerpCornerRadii(
fromCornerRadius, toCornerRadius, valueAnimator.getAnimatedFraction());
rootView.updateClipBoundsAndCornerRadii(clipBounds, cornerRadii);
});
animator.setDuration(show ? SHOW_DURATION_MS : HIDE_DURATION_MS);
animator.setInterpolator(
ReversableAnimatedValueInterpolator.of(show, AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR));
return animator;
}

private static float[] maxCornerRadii(float[] startValue, float[] endValue) {
return new float[] {
max(startValue[0], endValue[0]),
max(startValue[1], endValue[1]),
max(startValue[2], endValue[2]),
max(startValue[3], endValue[3]),
max(startValue[4], endValue[4]),
max(startValue[5], endValue[5]),
max(startValue[6], endValue[6]),
max(startValue[7], endValue[7])
};
}

private static float[] lerpCornerRadii(float startValue, float[] endValue, float fraction) {
return new float[] {
lerp(startValue, endValue[0], fraction),
lerp(startValue, endValue[1], fraction),
lerp(startValue, endValue[2], fraction),
lerp(startValue, endValue[3], fraction),
lerp(startValue, endValue[4], fraction),
lerp(startValue, endValue[5], fraction),
lerp(startValue, endValue[6], fraction),
lerp(startValue, endValue[7], fraction)
};
}

private Animator getClearButtonAnimator(boolean show) {
ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
animator.setDuration(
Expand Down

0 comments on commit 3ce7c2b

Please sign in to comment.