LayoutUtils.java
/*
* Copyright 2020 The Android Open Source Project
*
* 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 androidx.wear.watchface.complications.rendering.utils;
import android.graphics.Rect;
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
/**
* Utilities for calculations related to bounds.
*
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
public class LayoutUtils {
private LayoutUtils() {}
/** Minimum aspect ratio for a rectangle to be qualified as wide rectangle. */
public static final float WIDE_RECTANGLE_MINIMUM_ASPECT_RATIO = 2.0f;
/**
* Returns true if the aspect ratio of bounds is greater than {@link
* #WIDE_RECTANGLE_MINIMUM_ASPECT_RATIO}.
*/
public static boolean isWideRectangle(@NonNull Rect bounds) {
return bounds.width() > bounds.height() * WIDE_RECTANGLE_MINIMUM_ASPECT_RATIO;
}
/**
* Sets the output to the square that has the same left edge as input. Sets to empty if input
* does not contain the square. This function is used to find icon / image position on wide
* rectangles.
*/
public static void getLeftPart(@NonNull Rect outRect, @NonNull Rect inRect) {
if (inRect.width() < inRect.height()) {
outRect.setEmpty(); // There is no left square
} else {
outRect.set(inRect.left, inRect.top, inRect.left + inRect.height(), inRect.bottom);
}
}
/** Sets the output to the remaining part from left part of the input. */
public static void getRightPart(@NonNull Rect outRect, @NonNull Rect inRect) {
if (inRect.width() < inRect.height()) {
outRect.set(inRect);
} else {
outRect.set(inRect.left + inRect.height(), inRect.top, inRect.right, inRect.bottom);
}
}
/** Sets the output to the top half of the input. */
public static void getTopHalf(@NonNull Rect outRect, @NonNull Rect inRect) {
outRect.set(inRect.left, inRect.top, inRect.right, (inRect.top + inRect.bottom) / 2);
}
/** Sets the output to the bottom half of the input. */
public static void getBottomHalf(@NonNull Rect outRect, @NonNull Rect inRect) {
outRect.set(inRect.left, (inRect.top + inRect.bottom) / 2, inRect.right, inRect.bottom);
}
/**
* Sets the output to the biggest square that fits in input rectangle and has the same center,
* works for all aspect ratios.
*/
public static void getCentralSquare(@NonNull Rect outRect, @NonNull Rect inRect) {
int edge = Math.min(inRect.width(), inRect.height());
outRect.set(
inRect.centerX() - edge / 2,
inRect.centerY() - edge / 2,
inRect.centerX() + edge / 2,
inRect.centerY() + edge / 2);
}
/**
* Sets the output to the rectangle obtained by scaling input's edges by a given fraction but
* keeping its center the same.
*/
public static void scaledAroundCenter(
@NonNull Rect outRect, @NonNull Rect inRect, float sizeFraction) {
outRect.set(inRect);
float paddingFraction = 0.5f - sizeFraction / 2;
outRect.inset(
(int) (outRect.width() * paddingFraction),
(int) (outRect.height() * paddingFraction));
}
/** Fits square bounds inside a container and tries to keep its center the same. */
public static void fitSquareToBounds(@NonNull Rect squareBounds, @NonNull Rect container) {
if (squareBounds.isEmpty()) {
return;
}
int originalCenterX = squareBounds.centerX();
int originalCenterY = squareBounds.centerY();
if (!squareBounds.intersect(container)) {
squareBounds.setEmpty();
return;
}
LayoutUtils.getCentralSquare(squareBounds, squareBounds);
// Try to move to original center
int dx = originalCenterX - squareBounds.centerX();
int dy = originalCenterY - squareBounds.centerY();
squareBounds.offset(dx, dy);
// If it causes an overflow, move it back
if (!container.contains(squareBounds)) {
squareBounds.offset(-dx, -dy);
}
}
/**
* Sets the output to inscribed rectangle of the rounded rectangle specified by input rectangle
* and radius.
*/
public static void getInnerBounds(@NonNull Rect outRect, @NonNull Rect inRect, float radius) {
outRect.set(inRect);
int padding = (int) Math.ceil((Math.sqrt(2.0f) - 1.0f) * radius);
outRect.inset(padding, padding);
}
}