Add mechanism for customization of rounded corner insets
TaskBar and divider bar now draw fake rounded corners. We need a way to
report these fake rounded corners instead of the real ones.
1. A new field providedInternalTaskBarInsets is added in
WindowManager.Params for task bar(Launcher) to set.
- Set this field to the task bar height(not including rounded
corner height) when task bar is expanded.
- Set to 0 when task bar is stashed
2. A ned field mRoundedCornerFrame is added in InsetsState.
- The frame that rounded corners are relative to.
- It's used to re-calculate the fake rounded corners.
3. In WindowState.getInsetsState(), check below condition:
- It's a task
- The task bar insets is not empty
- In split screen
If all conditions are met, we use task bounds as new frame to
calculate rounded corners instead of display frame.
4. A new method RoundedCorners.insetsWithFrame() is added to handle such
case.
Bug: 196387239
Test: atest RoundedCornersTest
Change-Id: Icd6d39bd8cf6534531f7bbb209296785b76891db
This commit is contained in:
@@ -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
|
||||
+ " }";
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3554,6 +3554,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.
|
||||
@@ -3929,6 +3940,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);
|
||||
@@ -4009,6 +4021,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];
|
||||
@@ -4320,6 +4333,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();
|
||||
@@ -4529,6 +4547,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=");
|
||||
|
||||
@@ -304,6 +304,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.
|
||||
@@ -934,6 +938,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());
|
||||
}
|
||||
|
||||
@@ -1255,6 +1271,10 @@ public class DisplayPolicy {
|
||||
if (mLastFocusedWindow == win) {
|
||||
mLastFocusedWindow = null;
|
||||
}
|
||||
if (mRoundedCornerWindow == win) {
|
||||
mRoundedCornerWindow = null;
|
||||
}
|
||||
|
||||
mInsetsSourceWindowsExceptIme.remove(win);
|
||||
}
|
||||
|
||||
@@ -1285,6 +1305,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
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user