Merge "Add mechanism for customization of rounded corner insets"

This commit is contained in:
Shawn Lin
2021-11-16 04:24:25 +00:00
committed by Android (Google) Code Review
5 changed files with 170 additions and 15 deletions

View File

@@ -168,6 +168,16 @@ public class InsetsState implements Parcelable {
private final DisplayCutout.ParcelableWrapper mDisplayCutout =
new DisplayCutout.ParcelableWrapper();
/**
* The frame that rounded corners are relative to.
*
* There are 2 cases that will draw fake rounded corners:
* 1. In split-screen mode
* 2. Devices with a task bar
* We need to report these fake rounded corners to apps by re-calculating based on this frame.
*/
private final Rect mRoundedCornerFrame = new Rect();
/** The rounded corners on the display */
private RoundedCorners mRoundedCorners = RoundedCorners.NO_ROUNDED_CORNERS;
@@ -274,12 +284,17 @@ public class InsetsState implements Parcelable {
}
private RoundedCorners calculateRelativeRoundedCorners(Rect frame) {
if (mDisplayFrame.equals(frame)) {
return mRoundedCorners;
}
if (frame == null) {
return RoundedCorners.NO_ROUNDED_CORNERS;
}
// If mRoundedCornerFrame is set, we should calculate the new RoundedCorners based on this
// frame. It's used for split-screen mode and devices with a task bar.
if (!mRoundedCornerFrame.isEmpty() && !mRoundedCornerFrame.equals(mDisplayFrame)) {
return mRoundedCorners.insetWithFrame(frame, mRoundedCornerFrame);
}
if (mDisplayFrame.equals(frame)) {
return mRoundedCorners;
}
final int insetLeft = frame.left - mDisplayFrame.left;
final int insetTop = frame.top - mDisplayFrame.top;
final int insetRight = mDisplayFrame.right - frame.right;
@@ -530,6 +545,15 @@ public class InsetsState implements Parcelable {
return mRoundedCorners;
}
/**
* Set the frame that will be used to calculate the rounded corners.
*
* @see #mRoundedCornerFrame
*/
public void setRoundedCornerFrame(Rect frame) {
mRoundedCornerFrame.set(frame);
}
public void setPrivacyIndicatorBounds(PrivacyIndicatorBounds bounds) {
mPrivacyIndicatorBounds = bounds;
}
@@ -575,6 +599,7 @@ public class InsetsState implements Parcelable {
mDisplayFrame.scale(scale);
mDisplayCutout.scale(scale);
mRoundedCorners = mRoundedCorners.scale(scale);
mRoundedCornerFrame.scale(scale);
mPrivacyIndicatorBounds = mPrivacyIndicatorBounds.scale(scale);
for (int i = 0; i < SIZE; i++) {
final InsetsSource source = mSources[i];
@@ -596,6 +621,7 @@ public class InsetsState implements Parcelable {
mDisplayFrame.set(other.mDisplayFrame);
mDisplayCutout.set(other.mDisplayCutout);
mRoundedCorners = other.getRoundedCorners();
mRoundedCornerFrame.set(other.mRoundedCornerFrame);
mPrivacyIndicatorBounds = other.getPrivacyIndicatorBounds();
if (copySources) {
for (int i = 0; i < SIZE; i++) {
@@ -620,6 +646,7 @@ public class InsetsState implements Parcelable {
mDisplayFrame.set(other.mDisplayFrame);
mDisplayCutout.set(other.mDisplayCutout);
mRoundedCorners = other.getRoundedCorners();
mRoundedCornerFrame.set(other.mRoundedCornerFrame);
mPrivacyIndicatorBounds = other.getPrivacyIndicatorBounds();
final ArraySet<Integer> t = toInternalType(types);
for (int i = t.size() - 1; i >= 0; i--) {
@@ -740,6 +767,7 @@ public class InsetsState implements Parcelable {
pw.println(newPrefix + "mDisplayFrame=" + mDisplayFrame);
pw.println(newPrefix + "mDisplayCutout=" + mDisplayCutout.get());
pw.println(newPrefix + "mRoundedCorners=" + mRoundedCorners);
pw.println(newPrefix + "mRoundedCornerFrame=" + mRoundedCornerFrame);
pw.println(newPrefix + "mPrivacyIndicatorBounds=" + mPrivacyIndicatorBounds);
for (int i = 0; i < SIZE; i++) {
InsetsSource source = mSources[i];
@@ -836,6 +864,7 @@ public class InsetsState implements Parcelable {
if (!mDisplayFrame.equals(state.mDisplayFrame)
|| !mDisplayCutout.equals(state.mDisplayCutout)
|| !mRoundedCorners.equals(state.mRoundedCorners)
|| !mRoundedCornerFrame.equals(state.mRoundedCornerFrame)
|| !mPrivacyIndicatorBounds.equals(state.mPrivacyIndicatorBounds)) {
return false;
}
@@ -861,7 +890,7 @@ public class InsetsState implements Parcelable {
@Override
public int hashCode() {
return Objects.hash(mDisplayFrame, mDisplayCutout, Arrays.hashCode(mSources),
mRoundedCorners, mPrivacyIndicatorBounds);
mRoundedCorners, mPrivacyIndicatorBounds, mRoundedCornerFrame);
}
public InsetsState(Parcel in) {
@@ -879,6 +908,7 @@ public class InsetsState implements Parcelable {
mDisplayCutout.writeToParcel(dest, flags);
dest.writeTypedArray(mSources, 0 /* parcelableFlags */);
dest.writeTypedObject(mRoundedCorners, flags);
mRoundedCornerFrame.writeToParcel(dest, flags);
dest.writeTypedObject(mPrivacyIndicatorBounds, flags);
}
@@ -898,6 +928,7 @@ public class InsetsState implements Parcelable {
mDisplayCutout.readFromParcel(in);
in.readTypedArray(mSources, InsetsSource.CREATOR);
mRoundedCorners = in.readTypedObject(RoundedCorners.CREATOR);
mRoundedCornerFrame.readFromParcel(in);
mPrivacyIndicatorBounds = in.readTypedObject(PrivacyIndicatorBounds.CREATOR);
}
@@ -914,6 +945,7 @@ public class InsetsState implements Parcelable {
+ "mDisplayFrame=" + mDisplayFrame
+ ", mDisplayCutout=" + mDisplayCutout
+ ", mRoundedCorners=" + mRoundedCorners
+ " mRoundedCornerFrame=" + mRoundedCornerFrame
+ ", mPrivacyIndicatorBounds=" + mPrivacyIndicatorBounds
+ ", mSources= { " + joiner
+ " }";

View File

@@ -28,7 +28,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.DisplayUtils;
@@ -319,6 +319,53 @@ public class RoundedCorners implements Parcelable {
return isRound;
}
/**
* Insets the reference frame of the rounded corners.
*
* @param frame the frame of a window or any rectangle bounds
* @param roundedCornerFrame the frame that used to calculate relative {@link RoundedCorner}
* @return a copy of this instance which has been inset
*/
public RoundedCorners insetWithFrame(Rect frame, Rect roundedCornerFrame) {
int insetLeft = frame.left - roundedCornerFrame.left;
int insetTop = frame.top - roundedCornerFrame.top;
int insetRight = roundedCornerFrame.right - frame.right;
int insetBottom = roundedCornerFrame.bottom - frame.bottom;
final RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
int centerX, centerY;
for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; i++) {
if (mRoundedCorners[i].isEmpty()) {
roundedCorners[i] = new RoundedCorner(i);
continue;
}
final int radius = mRoundedCorners[i].getRadius();
switch (i) {
case POSITION_TOP_LEFT:
centerX = radius;
centerY = radius;
break;
case POSITION_TOP_RIGHT:
centerX = roundedCornerFrame.width() - radius;
centerY = radius;
break;
case POSITION_BOTTOM_RIGHT:
centerX = roundedCornerFrame.width() - radius;
centerY = roundedCornerFrame.height() - radius;
break;
case POSITION_BOTTOM_LEFT:
centerX = radius;
centerY = roundedCornerFrame.height() - radius;
break;
default:
throw new IllegalArgumentException(
"The position is not one of the RoundedCornerPosition =" + i);
}
roundedCorners[i] = insetRoundedCorner(i, radius, centerX, centerY, insetLeft, insetTop,
insetRight, insetBottom);
}
return new RoundedCorners(roundedCorners);
}
/**
* Insets the reference frame of the rounded corners.
*
@@ -327,19 +374,19 @@ public class RoundedCorners implements Parcelable {
public RoundedCorners inset(int insetLeft, int insetTop, int insetRight, int insetBottom) {
final RoundedCorner[] roundedCorners = new RoundedCorner[ROUNDED_CORNER_POSITION_LENGTH];
for (int i = 0; i < ROUNDED_CORNER_POSITION_LENGTH; i++) {
roundedCorners[i] = insetRoundedCorner(i, insetLeft, insetTop, insetRight, insetBottom);
roundedCorners[i] = insetRoundedCorner(i, mRoundedCorners[i].getRadius(),
mRoundedCorners[i].getCenter().x, mRoundedCorners[i].getCenter().y, insetLeft,
insetTop, insetRight, insetBottom);
}
return new RoundedCorners(roundedCorners);
}
private RoundedCorner insetRoundedCorner(@Position int position, int insetLeft,
int insetTop, int insetRight, int insetBottom) {
private RoundedCorner insetRoundedCorner(@Position int position, int radius, int centerX,
int centerY, int insetLeft, int insetTop, int insetRight, int insetBottom) {
if (mRoundedCorners[position].isEmpty()) {
return new RoundedCorner(position);
}
final int radius = mRoundedCorners[position].getRadius();
final Point center = mRoundedCorners[position].getCenter();
boolean hasRoundedCorner;
switch (position) {
case POSITION_TOP_LEFT:
@@ -360,8 +407,8 @@ public class RoundedCorners implements Parcelable {
}
return new RoundedCorner(
position, radius,
hasRoundedCorner ? center.x - insetLeft : 0,
hasRoundedCorner ? center.y - insetTop : 0);
hasRoundedCorner ? centerX - insetLeft : 0,
hasRoundedCorner ? centerY - insetTop : 0);
}
/**

View File

@@ -3573,6 +3573,17 @@ public interface WindowManager extends ViewManager {
*/
public Insets providedInternalImeInsets = Insets.NONE;
/**
* If specified, the frame that used to calculate relative {@link RoundedCorner} will be
* the window frame of this window minus the insets that this window provides.
*
* Task bar will draw fake rounded corners above itself, so we need this insets to calculate
* correct rounded corners for this window.
*
* @hide
*/
public boolean insetsRoundedCornerFrame = false;
/**
* {@link LayoutParams} to be applied to the window when layout with a assigned rotation.
* This will make layout during rotation change smoothly.
@@ -3948,6 +3959,7 @@ public interface WindowManager extends ViewManager {
}
providedInternalInsets.writeToParcel(out, 0 /* parcelableFlags */);
providedInternalImeInsets.writeToParcel(out, 0 /* parcelableFlags */);
out.writeBoolean(insetsRoundedCornerFrame);
if (paramsForRotation != null) {
checkNonRecursiveParams();
out.writeInt(paramsForRotation.length);
@@ -4028,6 +4040,7 @@ public interface WindowManager extends ViewManager {
}
providedInternalInsets = Insets.CREATOR.createFromParcel(in);
providedInternalImeInsets = Insets.CREATOR.createFromParcel(in);
insetsRoundedCornerFrame = in.readBoolean();
int paramsForRotationLength = in.readInt();
if (paramsForRotationLength > 0) {
paramsForRotation = new LayoutParams[paramsForRotationLength];
@@ -4339,6 +4352,11 @@ public interface WindowManager extends ViewManager {
changes |= LAYOUT_CHANGED;
}
if (insetsRoundedCornerFrame != o.insetsRoundedCornerFrame) {
insetsRoundedCornerFrame = o.insetsRoundedCornerFrame;
changes |= LAYOUT_CHANGED;
}
if (!Arrays.equals(paramsForRotation, o.paramsForRotation)) {
paramsForRotation = o.paramsForRotation;
checkNonRecursiveParams();
@@ -4548,6 +4566,10 @@ public interface WindowManager extends ViewManager {
sb.append(" providedInternalImeInsets=");
sb.append(providedInternalImeInsets);
}
if (insetsRoundedCornerFrame) {
sb.append(" insetsRoundedCornerFrame=");
sb.append(insetsRoundedCornerFrame);
}
if (paramsForRotation != null && paramsForRotation.length != 0) {
sb.append(System.lineSeparator());
sb.append(prefix).append(" paramsForRotation=");

View File

@@ -302,6 +302,10 @@ public class DisplayPolicy {
// needs to be opaque.
private WindowState mNavBarBackgroundWindow;
// The window that draws fake rounded corners and should provide insets to calculate the correct
// rounded corner insets.
private WindowState mRoundedCornerWindow;
/**
* Windows to determine the color of status bar. See {@link #mNavBarColorWindowCandidate} for
* the conditions of being candidate window.
@@ -927,6 +931,18 @@ public class DisplayPolicy {
mExtraNavBarAltPosition = getAltBarPosition(attrs);
}
if (attrs.insetsRoundedCornerFrame) {
// Currently, only support one rounded corner window which is the TaskBar.
if (mRoundedCornerWindow != null && mRoundedCornerWindow != win) {
throw new IllegalArgumentException("Found multiple rounded corner window :"
+ " current = " + mRoundedCornerWindow
+ " new = " + win);
}
mRoundedCornerWindow = win;
} else if (mRoundedCornerWindow == win) {
mRoundedCornerWindow = null;
}
attrs.flags = sanitizeFlagSlippery(attrs.flags, attrs.privateFlags, win.getName());
}
@@ -1250,6 +1266,10 @@ public class DisplayPolicy {
if (mLastFocusedWindow == win) {
mLastFocusedWindow = null;
}
if (mRoundedCornerWindow == win) {
mRoundedCornerWindow = null;
}
mInsetsSourceWindowsExceptIme.remove(win);
}
@@ -1280,6 +1300,10 @@ public class DisplayPolicy {
return mNavigationBar != null ? mNavigationBar : mNavigationBarAlt;
}
WindowState getRoundedCornerWindow() {
return mRoundedCornerWindow;
}
/**
* Control the animation to run when a window's state changes. Return a positive number to
* force the animation to a specific resource ID, {@link #ANIMATION_STYLEABLE} to use the

View File

@@ -33,6 +33,8 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.StatusBarManager;
import android.graphics.Insets;
import android.graphics.Rect;
import android.util.IntArray;
import android.util.SparseArray;
import android.view.InsetsAnimationControlCallbacks;
@@ -50,7 +52,6 @@ import android.view.WindowInsets.Type;
import android.view.WindowInsetsAnimation;
import android.view.WindowInsetsAnimation.Bounds;
import android.view.WindowInsetsAnimationControlListener;
import android.view.WindowInsetsAnimationController;
import android.view.WindowManager;
import com.android.internal.R;
@@ -220,8 +221,9 @@ class InsetsPolicy {
*/
InsetsState getInsetsForWindow(WindowState target) {
final InsetsState originalState = mStateController.getInsetsForWindow(target);
final InsetsState state = adjustVisibilityForTransientTypes(originalState);
return adjustVisibilityForIme(target, state, state == originalState);
InsetsState state = adjustVisibilityForTransientTypes(originalState);
state = adjustVisibilityForIme(target, state, state == originalState);
return adjustInsetsForRoundedCorners(target, state, state == originalState);
}
/**
@@ -286,6 +288,34 @@ class InsetsPolicy {
return originalState;
}
private InsetsState adjustInsetsForRoundedCorners(WindowState w, InsetsState originalState,
boolean copyState) {
final WindowState roundedCornerWindow = mPolicy.getRoundedCornerWindow();
final Task task = w.getTask();
final boolean isInSplitScreenMode = task != null && task.inMultiWindowMode()
&& task.getRootTask() != null
&& task.getRootTask().getAdjacentTaskFragment() != null;
if (task != null && !task.getWindowConfiguration().tasksAreFloating()
&& (roundedCornerWindow != null || isInSplitScreenMode)) {
// Instead of using display frame to calculating rounded corner, for the fake rounded
// corners drawn by divider bar or task bar, we need to re-calculate rounded corners
// based on task bounds and if the task bounds is intersected with task bar, we should
// exclude the intersected part.
final Rect roundedCornerFrame = new Rect(task.getBounds());
if (roundedCornerWindow != null
&& roundedCornerWindow.getControllableInsetProvider() != null) {
final InsetsSource source =
roundedCornerWindow.getControllableInsetProvider().getSource();
final Insets insets = source.calculateInsets(roundedCornerFrame, false);
roundedCornerFrame.inset(insets);
}
final InsetsState state = copyState ? new InsetsState(originalState) : originalState;
state.setRoundedCornerFrame(roundedCornerFrame);
return state;
}
return originalState;
}
void onInsetsModified(InsetsControlTarget caller) {
mStateController.onInsetsModified(caller);
checkAbortTransient(caller);