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

Commit cb466474 authored by Milton Wu's avatar Milton Wu
Browse files

Move rounded corner to provider

Move rounded corners code from ScreenDecorations.
1. Move business logic to RoundedCornerDecorProviderFactory.
2. Move view and inflating into RounedCornerDecorProviderImpl.

Bug: 218951516

Test: atest ScreenDecorationsTest \
            OverlayWindowTest \
            PrivacyDotDecorProviderFactoryTest \
            RoundedCornerDecorProviderFactoryTest \
            RoundedCornerResDelegateTest
Test: Manually testing rounded corner when display size, rotation
      changed on normal devices, foldable devices, and hardware-overlay
      devices

Change-Id: I60e8a0ceaca19dc45117fb98db45f4063cbfe1b3
parent 48b334a4
Loading
Loading
Loading
Loading
+0 −40
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
** 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.
-->
<com.android.systemui.RegionInterceptingFrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/rounded_corners_bottom"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView
        android:id="@+id/left"
        android:layout_width="12dp"
        android:layout_height="12dp"
        android:layout_gravity="left|bottom"
        android:tint="#ff000000"
        android:visibility="gone"
        android:src="@drawable/rounded_corner_bottom"/>

    <ImageView
        android:id="@+id/right"
        android:layout_width="12dp"
        android:layout_height="12dp"
        android:tint="#ff000000"
        android:visibility="gone"
        android:layout_gravity="right|bottom"
        android:src="@drawable/rounded_corner_bottom"/>

</com.android.systemui.RegionInterceptingFrameLayout>
+0 −40
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
** 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.
-->
<com.android.systemui.RegionInterceptingFrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/rounded_corners_top"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView
        android:id="@+id/left"
        android:layout_width="12dp"
        android:layout_height="12dp"
        android:layout_gravity="left|top"
        android:tint="#ff000000"
        android:visibility="gone"
        android:src="@drawable/rounded_corner_top"/>

    <ImageView
        android:id="@+id/right"
        android:layout_width="12dp"
        android:layout_height="12dp"
        android:tint="#ff000000"
        android:visibility="gone"
        android:layout_gravity="right|top"
        android:src="@drawable/rounded_corner_top"/>

</com.android.systemui.RegionInterceptingFrameLayout>
+6 −0
Original line number Diff line number Diff line
@@ -167,5 +167,11 @@
    <item type="id" name="action_move_bottom_right"/>
    <item type="id" name="action_move_to_edge_and_hide"/>
    <item type="id" name="action_move_out_edge_and_show"/>

    <!-- rounded corner view id -->
    <item type="id" name="rounded_corner_top_left"/>
    <item type="id" name="rounded_corner_top_right"/>
    <item type="id" name="rounded_corner_bottom_left"/>
    <item type="id" name="rounded_corner_bottom_right"/>
</resources>
+6 −1
Original line number Diff line number Diff line
@@ -357,10 +357,15 @@ class ScreenDecorHwcLayer(context: Context, displayDecorationSupport: DisplayDec
     * Update the rounded corner size.
     */
    fun updateRoundedCornerSize(top: Int, bottom: Int) {
        if (roundedCornerTopSize == top && roundedCornerBottomSize == bottom) {
            return
        }
        roundedCornerTopSize = top
        roundedCornerBottomSize = bottom
        updateRoundedCornerDrawableBounds()
        invalidate()

        // Use requestLayout() to trigger transparent region recalculated
        requestLayout()
    }

    private fun updateRoundedCornerDrawableBounds() {
+64 −205
Original line number Diff line number Diff line
@@ -77,6 +77,7 @@ import com.android.systemui.decor.DecorProviderFactory;
import com.android.systemui.decor.DecorProviderKt;
import com.android.systemui.decor.OverlayWindow;
import com.android.systemui.decor.PrivacyDotDecorProviderFactory;
import com.android.systemui.decor.RoundedCornerDecorProviderFactory;
import com.android.systemui.decor.RoundedCornerResDelegate;
import com.android.systemui.qs.SettingObserver;
import com.android.systemui.settings.UserTracker;
@@ -138,6 +139,9 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
    @VisibleForTesting
    protected RoundedCornerResDelegate mRoundedCornerResDelegate;
    @VisibleForTesting
    protected DecorProviderFactory mRoundedCornerFactory;
    private int mProviderRefreshToken = 0;
    @VisibleForTesting
    protected OverlayWindow[] mOverlays = null;
    @Nullable
    private DisplayCutoutView[] mCutoutViews;
@@ -292,11 +296,11 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
        mDisplayUniqueId = mContext.getDisplay().getUniqueId();
        mRoundedCornerResDelegate = new RoundedCornerResDelegate(mContext.getResources(),
                mDisplayUniqueId);
        mRoundedCornerFactory = new RoundedCornerDecorProviderFactory(mRoundedCornerResDelegate);
        mWindowManager = mContext.getSystemService(WindowManager.class);
        mDisplayManager = mContext.getSystemService(DisplayManager.class);
        mHwcScreenDecorationSupport = mContext.getDisplay().getDisplayDecorationSupport();
        updateRoundedCornerDrawable();
        updateRoundedCornerRadii();
        updateHwLayerRoundedCornerDrawable();
        setupDecorations();
        setupCameraListener();

@@ -348,7 +352,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
                final String newUniqueId = mContext.getDisplay().getUniqueId();
                if (!Objects.equals(newUniqueId, mDisplayUniqueId)) {
                    mDisplayUniqueId = newUniqueId;
                    mRoundedCornerResDelegate.reloadAll(newUniqueId);
                    mRoundedCornerResDelegate.updateDisplayUniqueId(newUniqueId, null);
                    final DisplayDecorationSupport newScreenDecorationSupport =
                            mContext.getDisplay().getDisplayDecorationSupport();
                    // When the value of mSupportHwcScreenDecoration is changed, re-setup the whole
@@ -359,12 +363,12 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
                        setupDecorations();
                        return;
                    }
                    updateRoundedCornerDrawable();
                    updateHwLayerRoundedCornerDrawable();
                }
                if (mScreenDecorHwcLayer != null) {
                    mScreenDecorHwcLayer.onDisplayChanged(displayId);
                }
                updateOrientation();
                updateView();
            }
        };

@@ -406,22 +410,22 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
    }

    private void setupDecorations() {
        List<DecorProvider> decorProviders = mDotFactory.getProviders();

        if (hasRoundedCorners() || shouldDrawCutout() || !decorProviders.isEmpty()) {
        if (hasRoundedCorners() || shouldDrawCutout() || isPrivacyDotEnabled()) {
            List<DecorProvider> decorProviders = new ArrayList<>(mDotFactory.getProviders());
            if (mHwcScreenDecorationSupport != null) {
                createHwcOverlay();
            } else {
                removeHwcOverlay();
                decorProviders.addAll(mRoundedCornerFactory.getProviders());
            }
            final DisplayCutout cutout = getCutout();
            for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
                if (shouldShowCutout(i, cutout) || shouldShowRoundedCorner(i, cutout)
                        || shouldShowPrivacyDot(i, cutout)) {
                if (shouldShowSwLayerCutout(i, cutout) || shouldShowSwLayerRoundedCorner(i, cutout)
                        || shouldShowSwLayerPrivacyDot(i, cutout)) {
                    Pair<List<DecorProvider>, List<DecorProvider>> pair =
                            DecorProviderKt.partitionAlignedBound(decorProviders, i);
                    decorProviders = pair.getSecond();
                    createOverlay(i, cutout, pair.getFirst());
                    createOverlay(i, pair.getFirst());
                } else {
                    removeOverlay(i);
                }
@@ -522,7 +526,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab

    private void createOverlay(
            @BoundsPosition int pos,
            @Nullable DisplayCutout cutout,
            @NonNull List<DecorProvider> decorProviders) {
        if (mOverlays == null) {
            mOverlays = new OverlayWindow[BOUNDS_POSITION_LENGTH];
@@ -547,7 +550,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
            mCutoutViews[pos] = new DisplayCutoutView(mContext, pos);
            mCutoutViews[pos].setColor(mTintColor);
            overlayView.addView(mCutoutViews[pos]);
            updateView(pos, cutout);
            mCutoutViews[pos].updateRotation(mRotation);
        }

        mWindowManager.addView(overlayView, getWindowLayoutParams(pos));
@@ -603,7 +606,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
    private OverlayWindow overlayForPosition(
            @BoundsPosition int pos,
            @NonNull List<DecorProvider> decorProviders) {
        final OverlayWindow currentOverlay = new OverlayWindow(LayoutInflater.from(mContext), pos);
        final OverlayWindow currentOverlay = new OverlayWindow(mContext);
        decorProviders.forEach(provider -> {
            removeOverlayView(provider.getViewId());
            currentOverlay.addDecorProvider(provider, mRotation);
@@ -617,22 +620,16 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
        return currentOverlay;
    }

    private void updateView(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
        if (mOverlays == null || mOverlays[pos] == null || mHwcScreenDecorationSupport != null) {
    private void updateView() {
        if (mOverlays == null) {
            return;
        }

        // update rounded corner view rotation
        updateRoundedCornerView(pos, R.id.left, cutout);
        updateRoundedCornerView(pos, R.id.right, cutout);
        updateRoundedCornerSize(
                mRoundedCornerResDelegate.getTopRoundedSize(),
                mRoundedCornerResDelegate.getBottomRoundedSize());
        updateRoundedCornerImageView();

        // update cutout view rotation
        if (mCutoutViews != null && mCutoutViews[pos] != null) {
            mCutoutViews[pos].updateRotation(mRotation);
        ++mProviderRefreshToken;
        for (final OverlayWindow overlay: mOverlays) {
            if (overlay == null) {
                continue;
            }
            overlay.onReloadResAndMeasure(null, mProviderRefreshToken, mRotation, mDisplayUniqueId);
        }
    }

@@ -806,7 +803,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
            int oldRotation = mRotation;
            mPendingRotationChange = false;
            updateOrientation();
            updateRoundedCornerRadii();
            if (DEBUG) Log.i(TAG, "onConfigChanged from rot " + oldRotation + " to " + mRotation);
            setupDecorations();
            if (mOverlays != null) {
@@ -866,109 +862,32 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
            mDotViewController.setNewRotation(newRotation);
        }

        if (mPendingRotationChange) {
            return;
        }
        if (newRotation != mRotation) {
        if (!mPendingRotationChange && newRotation != mRotation) {
            mRotation = newRotation;
            if (mScreenDecorHwcLayer != null) {
                mScreenDecorHwcLayer.pendingRotationChange = false;
                mScreenDecorHwcLayer.updateRotation(mRotation);
                updateHwLayerRoundedCornerSize();
                updateHwLayerRoundedCornerDrawable();
            }
            if (mOverlays != null) {
            updateLayoutParams();
                final DisplayCutout cutout = getCutout();
                for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
                    if (mOverlays[i] == null) {
            // update cutout view rotation
            if (mCutoutViews != null) {
                for (final DisplayCutoutView cutoutView: mCutoutViews) {
                    if (cutoutView == null) {
                        continue;
                    }
                    updateView(i, cutout);
                }
                    cutoutView.updateRotation(mRotation);
                }
            }
        }

    private void updateRoundedCornerRadii() {
        // We should eventually move to just using the intrinsic size of the drawables since
        // they should be sized to the exact pixels they want to cover. Therefore I'm purposely not
        // upgrading all of the configs to contain (width, height) pairs. Instead assume that a
        // device configured using the single integer config value is okay with drawing the corners
        // as a square
        final Size oldRoundedDefaultTop = mRoundedCornerResDelegate.getTopRoundedSize();
        final Size oldRoundedDefaultBottom = mRoundedCornerResDelegate.getBottomRoundedSize();
        mRoundedCornerResDelegate.reloadAll(mDisplayUniqueId);
        final Size newRoundedDefaultTop = mRoundedCornerResDelegate.getTopRoundedSize();
        final Size newRoundedDefaultBottom = mRoundedCornerResDelegate.getBottomRoundedSize();

        if (oldRoundedDefaultTop.getWidth() != newRoundedDefaultTop.getWidth()
                || oldRoundedDefaultBottom.getWidth() != newRoundedDefaultBottom.getWidth()) {
            onTuningChanged(SIZE, null);
        }
        // update views
        updateView();
    }

    private void updateRoundedCornerView(@BoundsPosition int pos, int id,
            @Nullable DisplayCutout cutout) {
        final View rounded = mOverlays[pos].getRootView().findViewById(id);
        if (rounded == null) {
            return;
        }
        rounded.setVisibility(View.GONE);
        if (shouldShowRoundedCorner(pos, cutout)) {
            final int gravity = getRoundedCornerGravity(pos, id == R.id.left);
            ((FrameLayout.LayoutParams) rounded.getLayoutParams()).gravity = gravity;
            setRoundedCornerOrientation(rounded, gravity);
            rounded.setVisibility(View.VISIBLE);
        }
    }

    private int getRoundedCornerGravity(@BoundsPosition int pos, boolean isStart) {
        final int rotatedPos = getBoundPositionFromRotation(pos, mRotation);
        switch (rotatedPos) {
            case BOUNDS_POSITION_LEFT:
                return isStart ? Gravity.TOP | Gravity.LEFT : Gravity.BOTTOM | Gravity.LEFT;
            case BOUNDS_POSITION_TOP:
                return isStart ? Gravity.TOP | Gravity.LEFT : Gravity.TOP | Gravity.RIGHT;
            case BOUNDS_POSITION_RIGHT:
                return isStart ? Gravity.TOP | Gravity.RIGHT : Gravity.BOTTOM | Gravity.RIGHT;
            case BOUNDS_POSITION_BOTTOM:
                return isStart ? Gravity.BOTTOM | Gravity.LEFT : Gravity.BOTTOM | Gravity.RIGHT;
            default:
                throw new IllegalArgumentException("Incorrect position: " + rotatedPos);
        }
    }

    /**
     * Configures the rounded corner drawable's view matrix based on the gravity.
     *
     * The gravity describes which corner to configure for, and the drawable we are rotating is
     * assumed to be oriented for the top-left corner of the device regardless of the target corner.
     * Therefore we need to rotate 180 degrees to get a bottom-left corner, and mirror in the x- or
     * y-axis for the top-right and bottom-left corners.
     */
    private void setRoundedCornerOrientation(View corner, int gravity) {
        corner.setRotation(0);
        corner.setScaleX(1);
        corner.setScaleY(1);
        switch (gravity) {
            case Gravity.TOP | Gravity.LEFT:
                return;
            case Gravity.TOP | Gravity.RIGHT:
                corner.setScaleX(-1); // flip X axis
                return;
            case Gravity.BOTTOM | Gravity.LEFT:
                corner.setScaleY(-1); // flip Y axis
                return;
            case Gravity.BOTTOM | Gravity.RIGHT:
                corner.setRotation(180);
                return;
            default:
                throw new IllegalArgumentException("Unsupported gravity: " + gravity);
        }
    }
    private boolean hasRoundedCorners() {
        return mRoundedCornerResDelegate.getBottomRoundedSize().getWidth() > 0
                || mRoundedCornerResDelegate.getTopRoundedSize().getWidth() > 0
                || mRoundedCornerResDelegate.isMultipleRadius();
        return mRoundedCornerFactory.getHasProviders();
    }

    private boolean isDefaultShownOverlayPos(@BoundsPosition int pos,
@@ -987,17 +906,19 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
        }
    }

    private boolean shouldShowRoundedCorner(@BoundsPosition int pos,
    private boolean shouldShowSwLayerRoundedCorner(@BoundsPosition int pos,
            @Nullable DisplayCutout cutout) {
        return hasRoundedCorners() && isDefaultShownOverlayPos(pos, cutout)
                && mHwcScreenDecorationSupport == null;
    }

    private boolean shouldShowPrivacyDot(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
    private boolean shouldShowSwLayerPrivacyDot(@BoundsPosition int pos,
            @Nullable DisplayCutout cutout) {
        return isPrivacyDotEnabled() && isDefaultShownOverlayPos(pos, cutout);
    }

    private boolean shouldShowCutout(@BoundsPosition int pos, @Nullable DisplayCutout cutout) {
    private boolean shouldShowSwLayerCutout(@BoundsPosition int pos,
            @Nullable DisplayCutout cutout) {
        final Rect[] bounds = cutout == null ? null : cutout.getBoundingRectsAll();
        final int rotatedPos = getBoundPositionFromRotation(pos, mRotation);
        return (bounds != null && !bounds[rotatedPos].isEmpty()
@@ -1032,52 +953,31 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
            return;
        }
        mExecutor.execute(() -> {
            if (mOverlays == null) return;
            if (SIZE.equals(key)) {
                if (newValue != null) {
                    try {
                        mRoundedCornerResDelegate.updateTuningSizeFactor(
                                Integer.parseInt(newValue));
                    } catch (Exception e) {
                    }
                }
                updateRoundedCornerSize(
                        mRoundedCornerResDelegate.getTopRoundedSize(),
                        mRoundedCornerResDelegate.getBottomRoundedSize());
            }
        });
    }

    private void updateRoundedCornerDrawable() {
        mRoundedCornerResDelegate.reloadAll(mDisplayUniqueId);
        updateRoundedCornerImageView();
    }

    private void updateRoundedCornerImageView() {
        final Drawable top = mRoundedCornerResDelegate.getTopRoundedDrawable();
        final Drawable bottom = mRoundedCornerResDelegate.getBottomRoundedDrawable();

        if (mScreenDecorHwcLayer != null) {
            mScreenDecorHwcLayer.updateRoundedCornerDrawable(top, bottom);
            return;
        }

        if (mOverlays == null) {
            if (mOverlays == null || !SIZE.equals(key)) {
                return;
            }
        final ColorStateList colorStateList = ColorStateList.valueOf(mTintColor);
        for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
            if (mOverlays[i] == null) {
            ++mProviderRefreshToken;
            try {
                final int sizeFactor = Integer.parseInt(newValue);
                mRoundedCornerResDelegate.updateTuningSizeFactor(sizeFactor, mProviderRefreshToken);
            } catch (NumberFormatException e) {
                mRoundedCornerResDelegate.updateTuningSizeFactor(null, mProviderRefreshToken);
            }
            Integer[] filterIds = {
                    R.id.rounded_corner_top_left,
                    R.id.rounded_corner_top_right,
                    R.id.rounded_corner_bottom_left,
                    R.id.rounded_corner_bottom_right
            };
            for (final OverlayWindow overlay: mOverlays) {
                if (overlay == null) {
                    continue;
                }
            final ViewGroup overlayView = mOverlays[i].getRootView();
            ((ImageView) overlayView.findViewById(R.id.left)).setImageTintList(colorStateList);
            ((ImageView) overlayView.findViewById(R.id.right)).setImageTintList(colorStateList);
            ((ImageView) overlayView.findViewById(R.id.left)).setImageDrawable(
                    isTopRoundedCorner(i, R.id.left) ? top : bottom);
            ((ImageView) overlayView.findViewById(R.id.right)).setImageDrawable(
                    isTopRoundedCorner(i, R.id.right) ? top : bottom);
                overlay.onReloadResAndMeasure(filterIds, mProviderRefreshToken, mRotation,
                        mDisplayUniqueId);
            }
            updateHwLayerRoundedCornerSize();
        });
    }

    private void updateHwLayerRoundedCornerDrawable() {
@@ -1094,25 +994,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
        mScreenDecorHwcLayer.updateRoundedCornerDrawable(topDrawable, bottomDrawable);
    }

    @VisibleForTesting
    boolean isTopRoundedCorner(@BoundsPosition int pos, int id) {
        switch (pos) {
            case BOUNDS_POSITION_LEFT:
            case BOUNDS_POSITION_RIGHT:
                if (mRotation == ROTATION_270) {
                    return id == R.id.left ? false : true;
                } else {
                    return id == R.id.left ? true : false;
                }
            case BOUNDS_POSITION_TOP:
                return true;
            case BOUNDS_POSITION_BOTTOM:
                return false;
            default:
                throw new IllegalArgumentException("Unknown bounds position");
        }
    }

    private void updateHwLayerRoundedCornerSize() {
        if (mScreenDecorHwcLayer == null) {
            return;
@@ -1124,28 +1005,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
        mScreenDecorHwcLayer.updateRoundedCornerSize(topWidth, bottomWidth);
    }

    private void updateRoundedCornerSize(Size sizeTop, Size sizeBottom) {

        if (mScreenDecorHwcLayer != null) {
            mScreenDecorHwcLayer.updateRoundedCornerSize(sizeTop.getWidth(), sizeBottom.getWidth());
            return;
        }

        if (mOverlays == null) {
            return;
        }
        for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) {
            if (mOverlays[i] == null) {
                continue;
            }
            final ViewGroup overlayView = mOverlays[i].getRootView();
            setSize(overlayView.findViewById(R.id.left),
                    isTopRoundedCorner(i, R.id.left) ? sizeTop : sizeBottom);
            setSize(overlayView.findViewById(R.id.right),
                    isTopRoundedCorner(i, R.id.right) ? sizeTop : sizeBottom);
        }
    }

    @VisibleForTesting
    protected void setSize(View view, Size pixelSize) {
        LayoutParams params = view.getLayoutParams();
Loading