Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3fc90a72 authored by shawnlin's avatar shawnlin Committed by Shawn Lin
Browse files

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
parent 1a6add77
Loading
Loading
Loading
Loading
+36 −4
Original line number Diff line number Diff line
@@ -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
                + " }";
+55 −8
Original line number Diff line number Diff line
@@ -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);
    }

    /**
+22 −0
Original line number Diff line number Diff line
@@ -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=");
+24 −0
Original line number Diff line number Diff line
@@ -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 −3
Original line number Diff line number Diff line
@@ -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);