diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index 61798817772b4..fce95c88e060f 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -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 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 + " }"; diff --git a/core/java/android/view/RoundedCorners.java b/core/java/android/view/RoundedCorners.java index 6079d8e3f1189..3eade77a0232f 100644 --- a/core/java/android/view/RoundedCorners.java +++ b/core/java/android/view/RoundedCorners.java @@ -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); } /** diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 8287de22c49af..3b4fcc0adccba 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -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="); diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index e0dc4e4916f46..a492b7abacbc7 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -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 diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index 3d19f54ad1325..dff7ff9931d71 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -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);