Loading packages/SystemUI/res/layout/rounded_corners_bottom.xml 0 → 100644 +40 −0 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> packages/SystemUI/res/layout/rounded_corners_top.xml 0 → 100644 +40 −0 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> packages/SystemUI/res/values/ids.xml +0 −6 Original line number Diff line number Diff line Loading @@ -167,11 +167,5 @@ <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> packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt +1 −6 Original line number Diff line number Diff line Loading @@ -369,15 +369,10 @@ 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() // Use requestLayout() to trigger transparent region recalculated requestLayout() invalidate() } private fun updateRoundedCornerDrawableBounds() { Loading packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +205 −64 Original line number Diff line number Diff line Loading @@ -77,7 +77,6 @@ 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; Loading Loading @@ -139,9 +138,6 @@ 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; Loading Loading @@ -296,11 +292,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(); updateHwLayerRoundedCornerDrawable(); updateRoundedCornerDrawable(); updateRoundedCornerRadii(); setupDecorations(); setupCameraListener(); Loading Loading @@ -352,7 +348,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab final String newUniqueId = mContext.getDisplay().getUniqueId(); if (!Objects.equals(newUniqueId, mDisplayUniqueId)) { mDisplayUniqueId = newUniqueId; mRoundedCornerResDelegate.updateDisplayUniqueId(newUniqueId, null); mRoundedCornerResDelegate.reloadAll(newUniqueId); final DisplayDecorationSupport newScreenDecorationSupport = mContext.getDisplay().getDisplayDecorationSupport(); // When the value of mSupportHwcScreenDecoration is changed, re-setup the whole Loading @@ -363,12 +359,12 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab setupDecorations(); return; } updateHwLayerRoundedCornerDrawable(); updateRoundedCornerDrawable(); } if (mScreenDecorHwcLayer != null) { mScreenDecorHwcLayer.onDisplayChanged(displayId); } updateView(); updateOrientation(); } }; Loading Loading @@ -410,22 +406,22 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } private void setupDecorations() { if (hasRoundedCorners() || shouldDrawCutout() || isPrivacyDotEnabled()) { List<DecorProvider> decorProviders = new ArrayList<>(mDotFactory.getProviders()); List<DecorProvider> decorProviders = mDotFactory.getProviders(); if (hasRoundedCorners() || shouldDrawCutout() || !decorProviders.isEmpty()) { if (mHwcScreenDecorationSupport != null) { createHwcOverlay(); } else { removeHwcOverlay(); decorProviders.addAll(mRoundedCornerFactory.getProviders()); } final DisplayCutout cutout = getCutout(); for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { if (shouldShowSwLayerCutout(i, cutout) || shouldShowSwLayerRoundedCorner(i, cutout) || shouldShowSwLayerPrivacyDot(i, cutout)) { if (shouldShowCutout(i, cutout) || shouldShowRoundedCorner(i, cutout) || shouldShowPrivacyDot(i, cutout)) { Pair<List<DecorProvider>, List<DecorProvider>> pair = DecorProviderKt.partitionAlignedBound(decorProviders, i); decorProviders = pair.getSecond(); createOverlay(i, pair.getFirst()); createOverlay(i, cutout, pair.getFirst()); } else { removeOverlay(i); } Loading Loading @@ -526,6 +522,7 @@ 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]; Loading @@ -550,7 +547,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab mCutoutViews[pos] = new DisplayCutoutView(mContext, pos); mCutoutViews[pos].setColor(mTintColor); overlayView.addView(mCutoutViews[pos]); mCutoutViews[pos].updateRotation(mRotation); updateView(pos, cutout); } mWindowManager.addView(overlayView, getWindowLayoutParams(pos)); Loading Loading @@ -606,7 +603,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab private OverlayWindow overlayForPosition( @BoundsPosition int pos, @NonNull List<DecorProvider> decorProviders) { final OverlayWindow currentOverlay = new OverlayWindow(mContext); final OverlayWindow currentOverlay = new OverlayWindow(LayoutInflater.from(mContext), pos); decorProviders.forEach(provider -> { removeOverlayView(provider.getViewId()); currentOverlay.addDecorProvider(provider, mRotation); Loading @@ -620,16 +617,22 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab return currentOverlay; } private void updateView() { if (mOverlays == null) { private void updateView(@BoundsPosition int pos, @Nullable DisplayCutout cutout) { if (mOverlays == null || mOverlays[pos] == null || mHwcScreenDecorationSupport != null) { return; } ++mProviderRefreshToken; for (final OverlayWindow overlay: mOverlays) { if (overlay == null) { continue; } overlay.onReloadResAndMeasure(null, mProviderRefreshToken, mRotation, mDisplayUniqueId); // 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); } } Loading Loading @@ -803,6 +806,7 @@ 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) { Loading Loading @@ -862,32 +866,109 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab mDotViewController.setNewRotation(newRotation); } if (!mPendingRotationChange && newRotation != mRotation) { if (mPendingRotationChange) { return; } if (newRotation != mRotation) { mRotation = newRotation; if (mScreenDecorHwcLayer != null) { mScreenDecorHwcLayer.pendingRotationChange = false; mScreenDecorHwcLayer.updateRotation(mRotation); updateHwLayerRoundedCornerSize(); updateHwLayerRoundedCornerDrawable(); } if (mOverlays != null) { updateLayoutParams(); // update cutout view rotation if (mCutoutViews != null) { for (final DisplayCutoutView cutoutView: mCutoutViews) { if (cutoutView == null) { final DisplayCutout cutout = getCutout(); for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { if (mOverlays[i] == null) { continue; } cutoutView.updateRotation(mRotation); updateView(i, cutout); } } } } // update views updateView(); 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); } } 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 mRoundedCornerFactory.getHasProviders(); return mRoundedCornerResDelegate.getBottomRoundedSize().getWidth() > 0 || mRoundedCornerResDelegate.getTopRoundedSize().getWidth() > 0 || mRoundedCornerResDelegate.isMultipleRadius(); } private boolean isDefaultShownOverlayPos(@BoundsPosition int pos, Loading @@ -906,19 +987,17 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } } private boolean shouldShowSwLayerRoundedCorner(@BoundsPosition int pos, private boolean shouldShowRoundedCorner(@BoundsPosition int pos, @Nullable DisplayCutout cutout) { return hasRoundedCorners() && isDefaultShownOverlayPos(pos, cutout) && mHwcScreenDecorationSupport == null; } private boolean shouldShowSwLayerPrivacyDot(@BoundsPosition int pos, @Nullable DisplayCutout cutout) { private boolean shouldShowPrivacyDot(@BoundsPosition int pos, @Nullable DisplayCutout cutout) { return isPrivacyDotEnabled() && isDefaultShownOverlayPos(pos, cutout); } private boolean shouldShowSwLayerCutout(@BoundsPosition int pos, @Nullable DisplayCutout cutout) { private boolean shouldShowCutout(@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() Loading Loading @@ -953,31 +1032,52 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab return; } mExecutor.execute(() -> { if (mOverlays == null || !SIZE.equals(key)) { 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; } ++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) { if (mOverlays == null) { return; } final ColorStateList colorStateList = ColorStateList.valueOf(mTintColor); for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { if (mOverlays[i] == null) { continue; } overlay.onReloadResAndMeasure(filterIds, mProviderRefreshToken, mRotation, mDisplayUniqueId); 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); } updateHwLayerRoundedCornerSize(); }); } private void updateHwLayerRoundedCornerDrawable() { Loading @@ -994,6 +1094,25 @@ 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; Loading @@ -1005,6 +1124,28 @@ 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 Loading
packages/SystemUI/res/layout/rounded_corners_bottom.xml 0 → 100644 +40 −0 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>
packages/SystemUI/res/layout/rounded_corners_top.xml 0 → 100644 +40 −0 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>
packages/SystemUI/res/values/ids.xml +0 −6 Original line number Diff line number Diff line Loading @@ -167,11 +167,5 @@ <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>
packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt +1 −6 Original line number Diff line number Diff line Loading @@ -369,15 +369,10 @@ 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() // Use requestLayout() to trigger transparent region recalculated requestLayout() invalidate() } private fun updateRoundedCornerDrawableBounds() { Loading
packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +205 −64 Original line number Diff line number Diff line Loading @@ -77,7 +77,6 @@ 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; Loading Loading @@ -139,9 +138,6 @@ 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; Loading Loading @@ -296,11 +292,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(); updateHwLayerRoundedCornerDrawable(); updateRoundedCornerDrawable(); updateRoundedCornerRadii(); setupDecorations(); setupCameraListener(); Loading Loading @@ -352,7 +348,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab final String newUniqueId = mContext.getDisplay().getUniqueId(); if (!Objects.equals(newUniqueId, mDisplayUniqueId)) { mDisplayUniqueId = newUniqueId; mRoundedCornerResDelegate.updateDisplayUniqueId(newUniqueId, null); mRoundedCornerResDelegate.reloadAll(newUniqueId); final DisplayDecorationSupport newScreenDecorationSupport = mContext.getDisplay().getDisplayDecorationSupport(); // When the value of mSupportHwcScreenDecoration is changed, re-setup the whole Loading @@ -363,12 +359,12 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab setupDecorations(); return; } updateHwLayerRoundedCornerDrawable(); updateRoundedCornerDrawable(); } if (mScreenDecorHwcLayer != null) { mScreenDecorHwcLayer.onDisplayChanged(displayId); } updateView(); updateOrientation(); } }; Loading Loading @@ -410,22 +406,22 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } private void setupDecorations() { if (hasRoundedCorners() || shouldDrawCutout() || isPrivacyDotEnabled()) { List<DecorProvider> decorProviders = new ArrayList<>(mDotFactory.getProviders()); List<DecorProvider> decorProviders = mDotFactory.getProviders(); if (hasRoundedCorners() || shouldDrawCutout() || !decorProviders.isEmpty()) { if (mHwcScreenDecorationSupport != null) { createHwcOverlay(); } else { removeHwcOverlay(); decorProviders.addAll(mRoundedCornerFactory.getProviders()); } final DisplayCutout cutout = getCutout(); for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { if (shouldShowSwLayerCutout(i, cutout) || shouldShowSwLayerRoundedCorner(i, cutout) || shouldShowSwLayerPrivacyDot(i, cutout)) { if (shouldShowCutout(i, cutout) || shouldShowRoundedCorner(i, cutout) || shouldShowPrivacyDot(i, cutout)) { Pair<List<DecorProvider>, List<DecorProvider>> pair = DecorProviderKt.partitionAlignedBound(decorProviders, i); decorProviders = pair.getSecond(); createOverlay(i, pair.getFirst()); createOverlay(i, cutout, pair.getFirst()); } else { removeOverlay(i); } Loading Loading @@ -526,6 +522,7 @@ 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]; Loading @@ -550,7 +547,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab mCutoutViews[pos] = new DisplayCutoutView(mContext, pos); mCutoutViews[pos].setColor(mTintColor); overlayView.addView(mCutoutViews[pos]); mCutoutViews[pos].updateRotation(mRotation); updateView(pos, cutout); } mWindowManager.addView(overlayView, getWindowLayoutParams(pos)); Loading Loading @@ -606,7 +603,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab private OverlayWindow overlayForPosition( @BoundsPosition int pos, @NonNull List<DecorProvider> decorProviders) { final OverlayWindow currentOverlay = new OverlayWindow(mContext); final OverlayWindow currentOverlay = new OverlayWindow(LayoutInflater.from(mContext), pos); decorProviders.forEach(provider -> { removeOverlayView(provider.getViewId()); currentOverlay.addDecorProvider(provider, mRotation); Loading @@ -620,16 +617,22 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab return currentOverlay; } private void updateView() { if (mOverlays == null) { private void updateView(@BoundsPosition int pos, @Nullable DisplayCutout cutout) { if (mOverlays == null || mOverlays[pos] == null || mHwcScreenDecorationSupport != null) { return; } ++mProviderRefreshToken; for (final OverlayWindow overlay: mOverlays) { if (overlay == null) { continue; } overlay.onReloadResAndMeasure(null, mProviderRefreshToken, mRotation, mDisplayUniqueId); // 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); } } Loading Loading @@ -803,6 +806,7 @@ 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) { Loading Loading @@ -862,32 +866,109 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab mDotViewController.setNewRotation(newRotation); } if (!mPendingRotationChange && newRotation != mRotation) { if (mPendingRotationChange) { return; } if (newRotation != mRotation) { mRotation = newRotation; if (mScreenDecorHwcLayer != null) { mScreenDecorHwcLayer.pendingRotationChange = false; mScreenDecorHwcLayer.updateRotation(mRotation); updateHwLayerRoundedCornerSize(); updateHwLayerRoundedCornerDrawable(); } if (mOverlays != null) { updateLayoutParams(); // update cutout view rotation if (mCutoutViews != null) { for (final DisplayCutoutView cutoutView: mCutoutViews) { if (cutoutView == null) { final DisplayCutout cutout = getCutout(); for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { if (mOverlays[i] == null) { continue; } cutoutView.updateRotation(mRotation); updateView(i, cutout); } } } } // update views updateView(); 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); } } 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 mRoundedCornerFactory.getHasProviders(); return mRoundedCornerResDelegate.getBottomRoundedSize().getWidth() > 0 || mRoundedCornerResDelegate.getTopRoundedSize().getWidth() > 0 || mRoundedCornerResDelegate.isMultipleRadius(); } private boolean isDefaultShownOverlayPos(@BoundsPosition int pos, Loading @@ -906,19 +987,17 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } } private boolean shouldShowSwLayerRoundedCorner(@BoundsPosition int pos, private boolean shouldShowRoundedCorner(@BoundsPosition int pos, @Nullable DisplayCutout cutout) { return hasRoundedCorners() && isDefaultShownOverlayPos(pos, cutout) && mHwcScreenDecorationSupport == null; } private boolean shouldShowSwLayerPrivacyDot(@BoundsPosition int pos, @Nullable DisplayCutout cutout) { private boolean shouldShowPrivacyDot(@BoundsPosition int pos, @Nullable DisplayCutout cutout) { return isPrivacyDotEnabled() && isDefaultShownOverlayPos(pos, cutout); } private boolean shouldShowSwLayerCutout(@BoundsPosition int pos, @Nullable DisplayCutout cutout) { private boolean shouldShowCutout(@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() Loading Loading @@ -953,31 +1032,52 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab return; } mExecutor.execute(() -> { if (mOverlays == null || !SIZE.equals(key)) { 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; } ++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) { if (mOverlays == null) { return; } final ColorStateList colorStateList = ColorStateList.valueOf(mTintColor); for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { if (mOverlays[i] == null) { continue; } overlay.onReloadResAndMeasure(filterIds, mProviderRefreshToken, mRotation, mDisplayUniqueId); 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); } updateHwLayerRoundedCornerSize(); }); } private void updateHwLayerRoundedCornerDrawable() { Loading @@ -994,6 +1094,25 @@ 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; Loading @@ -1005,6 +1124,28 @@ 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