blob: c6131aa118dfc6a669530f0b8d205dda2ccd680b [file] [log] [blame]
// Copyright 2018 The Feed Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.android.libraries.feed.piet.ui;
import android.view.View;
import com.google.search.now.ui.piet.RoundedCornersProto.RoundedCorners;
import com.google.search.now.ui.piet.RoundedCornersProto.RoundedCorners.Corners;
import java.util.Arrays;
/** Helper class to help work with rounded corner views. */
public class RoundedCornerViewHelper {
/**
* Returns a float[] representing the mask for which corners should be rounded with the radius.
*/
static float[] createRoundedCornerBitMask(float radius, int cornerField, boolean isRtL) {
final int layoutDirection = isRtL ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR;
return createRoundedCornerBitMask(radius, cornerField, layoutDirection);
}
/**
* Returns a float[] representing the mask for which corners should be rounded with the radius.
*
* @param layoutDirection either 0 for {@link android.view.View#LAYOUT_DIRECTION_LTR} or 1 for
* {@link android.view.View#LAYOUT_DIRECTION_RTL}. If an invalid value is passed in, default
* to LTR.
*/
private static float[] createRoundedCornerBitMask(
float radius, int cornerField, int layoutDirection) {
float[] radii = new float[8];
// If we don't have any radius, don't bother creating the mask.
if (radius > 0) {
if (cornerField == Corners.CORNERS_UNSPECIFIED_VALUE) {
Arrays.fill(radii, 0, 8, radius);
return radii;
}
if (layoutDirection == View.LAYOUT_DIRECTION_RTL) {
if ((cornerField & Corners.TOP_START_VALUE) != 0) {
radii[2] = radius;
radii[3] = radius;
}
if ((cornerField & Corners.TOP_END_VALUE) != 0) {
radii[0] = radius;
radii[1] = radius;
}
if ((cornerField & Corners.BOTTOM_END_VALUE) != 0) {
radii[6] = radius;
radii[7] = radius;
}
if ((cornerField & Corners.BOTTOM_START_VALUE) != 0) {
radii[4] = radius;
radii[5] = radius;
}
} else {
if ((cornerField & Corners.TOP_START_VALUE) != 0) {
radii[0] = radius;
radii[1] = radius;
}
if ((cornerField & Corners.TOP_END_VALUE) != 0) {
radii[2] = radius;
radii[3] = radius;
}
if ((cornerField & Corners.BOTTOM_END_VALUE) != 0) {
radii[4] = radius;
radii[5] = radius;
}
if ((cornerField & Corners.BOTTOM_START_VALUE) != 0) {
radii[6] = radius;
radii[7] = radius;
}
}
}
return radii;
}
/**
* Evaluates whether the rounded corners are valid--meaning one of the radius values is set, and
* is greater than zero.
*/
public static boolean hasValidRoundedCorners(RoundedCorners roundedCorners, int radiusOverride) {
// TODO Remove deprecated radius code.
return ((roundedCorners.hasRadiusPercentageOfWidth()
&& (roundedCorners.getRadiusPercentageOfWidth() > 0))
|| (roundedCorners.hasRadiusPercentageOfHeight()
&& (roundedCorners.getRadiusPercentageOfHeight() > 0))
|| (roundedCorners.hasRadiusDp() && (roundedCorners.getRadiusDp() > 0))
|| (roundedCorners.hasRadius() && (roundedCorners.getRadius() > 0))
|| (radiusOverride > 0));
}
/**
* Returns the radius that was passed in, or a smaller radius if necessary.
*
* <p>If the current radius is bigger than the width or height, or if it has adjacent rounded
* corners and the radius is more than half of the width or height, the radius is made smaller. It
* shrinks on all sides, even if only one corner needs to shrink--Piet does not allow different
* corners to have different radii.
*/
static int adjustRadiusIfTooBig(
int width, int height, int currentRadius, RoundedCorners roundedCorners) {
int smallerSide = Math.min(height, width);
currentRadius = Math.min(currentRadius, smallerSide);
// This is expected to be by far the most common case, so check this first and return fast.
if (allFourCornersRounded(roundedCorners.getBitmask())) {
return Math.min(currentRadius, smallerSide / 2);
}
// If not all corners are rounded, it may not be necessary to truncate to half the smaller side.
// If there are only horizontally-adjacent or only vertically-adjacent corners, truncate the
// radius to half of that side of the view. Both need to be checked in case there are 3 corners
// rounded, which ends up being equivalent to truncating to half the smaller side.
if (hasVerticallyAdjacentRoundedCorners(roundedCorners.getBitmask())) {
currentRadius = Math.min(currentRadius, height / 2);
}
if (hasHorizontallyAdjacentRoundedCorners(roundedCorners.getBitmask())) {
currentRadius = Math.min(currentRadius, width / 2);
}
return currentRadius;
}
static boolean allFourCornersRounded(int bitmask) {
return bitmask == 0 || bitmask == 15;
}
private static boolean hasVerticallyAdjacentRoundedCorners(int bitmask) {
return (shouldRoundCorner(Corners.TOP_START, bitmask)
&& shouldRoundCorner(Corners.BOTTOM_START, bitmask))
|| (shouldRoundCorner(Corners.TOP_END, bitmask)
&& shouldRoundCorner(Corners.BOTTOM_END, bitmask));
}
private static boolean hasHorizontallyAdjacentRoundedCorners(int bitmask) {
return (shouldRoundCorner(Corners.TOP_START, bitmask)
&& shouldRoundCorner(Corners.TOP_END, bitmask))
|| (shouldRoundCorner(Corners.BOTTOM_START, bitmask)
&& shouldRoundCorner(Corners.BOTTOM_END, bitmask));
}
/**
* Returns whether the corner should be rounded, based on the bitmask.
*
* <p>A bitmask of 0 implies that all corners should be rounded. The binary bitmask digits
* correspond to the {@link Corners} value.
*/
static boolean shouldRoundCorner(Corners corner, int bitmask) {
return (bitmask == 0) || (bitmask & corner.getNumber()) != 0;
}
// Prevent instantiation
private RoundedCornerViewHelper() {}
}