diff options
20 files changed, 527 insertions, 117 deletions
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java index fffcafbf88fb..4d4c909d2894 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java @@ -14,7 +14,6 @@ package com.android.systemui.plugins.qs; -import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; @@ -57,7 +56,6 @@ public interface QS extends FragmentBase { void setQsExpansion(float qsExpansionFraction, float headerTranslation); void setHeaderListening(boolean listening); void notifyCustomizeChanged(); - void setContainer(ViewGroup container); void setExpandClickListener(OnClickListener onClickListener); @@ -75,6 +73,16 @@ public interface QS extends FragmentBase { return isShowingDetail(); } + /** + * If QS should translate as we pull it down, or if it should be static. + */ + void setTranslateWhileExpanding(boolean shouldTranslate); + + /** + * A rounded corner clipping that makes QS feel as if it were behind everything. + */ + void setFancyClipping(int top, int bottom, int cornerRadius, boolean visible); + @ProvidesInterface(version = HeightListener.VERSION) interface HeightListener { int VERSION = 1; diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml index 12c864cfcad3..bea50e87a29a 100644 --- a/packages/SystemUI/res/layout/super_notification_shade.xml +++ b/packages/SystemUI/res/layout/super_notification_shade.xml @@ -43,7 +43,7 @@ android:visibility="invisible" /> </com.android.systemui.statusbar.BackDropView> - <com.android.systemui.statusbar.ScrimView + <com.android.systemui.scrim.ScrimView android:id="@+id/scrim_behind" android:layout_width="match_parent" android:layout_height="match_parent" @@ -51,7 +51,7 @@ sysui:ignoreRightInset="true" /> - <com.android.systemui.statusbar.ScrimView + <com.android.systemui.scrim.ScrimView android:id="@+id/scrim_notifications" android:layout_width="match_parent" android:layout_height="match_parent" @@ -72,7 +72,7 @@ <include layout="@layout/brightness_mirror_container" /> - <com.android.systemui.statusbar.ScrimView + <com.android.systemui.scrim.ScrimView android:id="@+id/scrim_in_front" android:layout_width="match_parent" android:layout_height="match_parent" diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index fc80dbe021a7..588f4bb20340 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -16,6 +16,7 @@ package com.android.keyguard; +import android.graphics.Rect; import android.os.UserHandle; import android.util.Slog; @@ -48,6 +49,7 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV private final ConfigurationController mConfigurationController; private final DozeParameters mDozeParameters; private final KeyguardVisibilityHelper mKeyguardVisibilityHelper; + private final Rect mClipBounds = new Rect(); private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL; @@ -299,4 +301,17 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV mView.updateLogoutView(shouldShowLogout()); } }; + + /** + * Rect that specifies how KSV should be clipped, on its parent's coordinates. + */ + public void setClipBounds(Rect clipBounds) { + if (clipBounds != null) { + mClipBounds.set(clipBounds.left, (int) (clipBounds.top - mView.getY()), + clipBounds.right, (int) (clipBounds.bottom - mView.getY())); + mView.setClipBounds(mClipBounds); + } else { + mView.setClipBounds(null); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java index e44e305b6120..19edeb80c10c 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java @@ -98,7 +98,6 @@ import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.colorextraction.ColorExtractor; import com.android.internal.colorextraction.ColorExtractor.GradientColors; -import com.android.internal.colorextraction.drawable.ScrimDrawable; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; @@ -117,6 +116,7 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.GlobalActions.GlobalActionsManager; import com.android.systemui.plugins.GlobalActionsPanelPlugin; +import com.android.systemui.scrim.ScrimDrawable; import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.policy.ConfigurationController; diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java index 767d7ab5a490..411e0f07f2be 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java @@ -30,11 +30,11 @@ import android.widget.ProgressBar; import android.widget.TextView; import com.android.internal.R; -import com.android.internal.colorextraction.drawable.ScrimDrawable; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.settingslib.Utils; import com.android.systemui.Dependency; import com.android.systemui.plugins.GlobalActions; +import com.android.systemui.scrim.ScrimDrawable; import com.android.systemui.statusbar.BlurUtils; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.ScrimController; diff --git a/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java b/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java index 309b32fc85d2..cd3609108c28 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java @@ -29,6 +29,7 @@ public class NonInterceptingScrollView extends ScrollView { private final int mTouchSlop; private float mDownY; + private boolean mScrollEnabled = true; public NonInterceptingScrollView(Context context, AttributeSet attrs) { super(context, attrs); @@ -85,6 +86,16 @@ public class NonInterceptingScrollView extends ScrollView { return super.onInterceptTouchEvent(ev); } + @Override + public boolean canScrollVertically(int direction) { + return mScrollEnabled && super.canScrollVertically(direction); + } + + @Override + public boolean canScrollHorizontally(int direction) { + return mScrollEnabled && super.canScrollHorizontally(direction); + } + public int getScrollRange() { int scrollRange = 0; if (getChildCount() > 0) { @@ -94,4 +105,12 @@ public class NonInterceptingScrollView extends ScrollView { } return scrollRange; } + + /** + * Enable scrolling for this view. Needed because the view might be clipped but still intercepts + * touches on the lockscreen. + */ + public void setScrollingEnabled(boolean enabled) { + mScrollEnabled = enabled; + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java index cefcd4a5194c..294d76590fed 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java @@ -54,6 +54,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha private static final String MOVE_FULL_ROWS = "sysui_qs_move_whole_rows"; public static final float EXPANDED_TILE_DELAY = .86f; + public static final float SHORT_PARALLAX_AMOUNT = 0.1f; private static final long QQS_FADE_IN_DURATION = 200L; // Fade out faster than fade in to finish before QQS hides. private static final long QQS_FADE_OUT_DURATION = 50L; @@ -101,6 +102,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha private final Executor mExecutor; private final TunerService mTunerService; private boolean mShowCollapsedOnKeyguard; + private boolean mTranslateWhileExpanding; @Inject public QSAnimator(QS qs, QuickQSPanel quickPanel, QuickStatusBarHeader quickStatusBarHeader, @@ -242,6 +244,9 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha int width = mQs.getView() != null ? mQs.getView().getMeasuredWidth() : 0; int heightDiff = height - mQs.getHeader().getBottom() + mQs.getHeader().getPaddingBottom(); + if (!mTranslateWhileExpanding) { + heightDiff *= SHORT_PARALLAX_AMOUNT; + } firstPageBuilder.addFloat(tileLayout, "translationY", heightDiff, 0); int qqsTileHeight = 0; @@ -570,6 +575,13 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha setCurrentPosition(); }; + /** + * True whe QS will be pulled from the top, false when it will be clipped. + */ + public void setTranslateWhileExpanding(boolean shouldTranslate) { + mTranslateWhileExpanding = shouldTranslate; + } + static class HeightExpansionAnimator { private final List<View> mViews = new ArrayList<>(); private final ValueAnimator mAnimator; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java index 6b09e2eb7b8b..e89803d16779 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java @@ -20,6 +20,8 @@ import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS; import android.content.Context; import android.content.res.Configuration; +import android.graphics.Canvas; +import android.graphics.Path; import android.graphics.Point; import android.util.AttributeSet; import android.view.View; @@ -54,6 +56,10 @@ public class QSContainerImpl extends FrameLayout { private static final PhysicsAnimator.SpringConfig BACKGROUND_SPRING = new PhysicsAnimator.SpringConfig(SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_LOW_BOUNCY); + private int mFancyClippingTop; + private int mFancyClippingBottom; + private final float[] mFancyClippingRadii = new float[] {0, 0, 0, 0, 0, 0, 0, 0}; + private final Path mFancyClippingPath = new Path(); private int mBackgroundBottom = -1; private int mHeightOverride = -1; private View mQSDetail; @@ -70,6 +76,7 @@ public class QSContainerImpl extends FrameLayout { private int mContentPadding = -1; private boolean mAnimateBottomOnNextLayout; private int mNavBarInset = 0; + private boolean mClippingEnabled; public QSContainerImpl(Context context, AttributeSet attrs) { super(context, attrs); @@ -169,6 +176,15 @@ public class QSContainerImpl extends FrameLayout { MeasureSpec.makeMeasureSpec(getDisplayHeight(), MeasureSpec.EXACTLY)); } + @Override + public void dispatchDraw(Canvas canvas) { + if (!mFancyClippingPath.isEmpty()) { + canvas.translate(0, -getTranslationY()); + canvas.clipOutPath(mFancyClippingPath); + canvas.translate(0, getTranslationY()); + } + super.dispatchDraw(canvas); + } @Override protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, @@ -187,6 +203,7 @@ public class QSContainerImpl extends FrameLayout { super.onLayout(changed, left, top, right, bottom); updateExpansion(mAnimateBottomOnNextLayout /* animate */); mAnimateBottomOnNextLayout = false; + updateClippingPath(); } public void disable(int state1, int state2, boolean animate) { @@ -281,6 +298,7 @@ public class QSContainerImpl extends FrameLayout { public void setExpansion(float expansion) { mQsExpansion = expansion; + mQSPanelContainer.setScrollingEnabled(expansion > 0.0f); updateExpansion(); } @@ -318,4 +336,46 @@ public class QSContainerImpl extends FrameLayout { } return mSizePoint.y; } + + /** + * Clip QS bottom using a concave shape. + */ + public void setFancyClipping(int top, int bottom, int radius, boolean enabled) { + boolean updatePath = false; + if (mFancyClippingRadii[0] != radius) { + mFancyClippingRadii[0] = radius; + mFancyClippingRadii[1] = radius; + mFancyClippingRadii[2] = radius; + mFancyClippingRadii[3] = radius; + updatePath = true; + } + if (mFancyClippingTop != top) { + mFancyClippingTop = top; + updatePath = true; + } + if (mFancyClippingBottom != bottom) { + mFancyClippingBottom = bottom; + updatePath = true; + } + if (mClippingEnabled != enabled) { + mClippingEnabled = enabled; + updatePath = true; + } + + if (updatePath) { + updateClippingPath(); + } + } + + private void updateClippingPath() { + mFancyClippingPath.reset(); + if (!mClippingEnabled) { + invalidate(); + return; + } + + mFancyClippingPath.addRoundRect(0, mFancyClippingTop, getWidth(), + mFancyClippingBottom, mFancyClippingRadii, Path.Direction.CW); + invalidate(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index b95194adb9cc..d5cb777416a7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -107,6 +107,11 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca private QuickQSPanelController mQuickQSPanelController; private QSCustomizerController mQSCustomizerController; private FeatureFlags mFeatureFlags; + /** + * When true, QS will translate from outside the screen. It will be clipped with parallax + * otherwise. + */ + private boolean mTranslateWhileExpanding; @Inject public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler, @@ -254,6 +259,13 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca } } + @Override + public void setFancyClipping(int top, int bottom, int cornerRadius, boolean visible) { + if (getView() instanceof QSContainerImpl) { + ((QSContainerImpl) getView()).setFancyClipping(top, bottom, cornerRadius, visible); + } + } + private void setEditLocation(View view) { View edit = view.findViewById(android.R.id.edit); int[] loc = edit.getLocationOnScreen(); @@ -394,16 +406,23 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca } @Override + public void setTranslateWhileExpanding(boolean shouldTranslate) { + mTranslateWhileExpanding = shouldTranslate; + mQSAnimator.setTranslateWhileExpanding(shouldTranslate); + } + + @Override public void setQsExpansion(float expansion, float headerTranslation) { if (DEBUG) Log.d(TAG, "setQSExpansion " + expansion + " " + headerTranslation); if (mQSAnimator != null) { final boolean showQSOnLockscreen = expansion > 0; - final boolean showQSUnlocked = headerTranslation == 0; + final boolean showQSUnlocked = headerTranslation == 0 || !mTranslateWhileExpanding; mQSAnimator.startAlphaAnimation(showQSOnLockscreen || showQSUnlocked); } mContainer.setExpansion(expansion); - final float translationScaleY = expansion - 1; + final float translationScaleY = (mTranslateWhileExpanding + ? 1 : QSAnimator.SHORT_PARALLAX_AMOUNT) * (expansion - 1); boolean onKeyguardAndExpanded = isKeyguardShowing() && !mShowCollapsedOnKeyguard; if (!mHeaderAnimating && !headerWillBeAnimating()) { getView().setTranslationY( diff --git a/core/java/com/android/internal/colorextraction/drawable/ScrimDrawable.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java index 1fc126eb161d..0a55fbe8bf75 100644 --- a/core/java/com/android/internal/colorextraction/drawable/ScrimDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2021 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.colorextraction.drawable; +package com.android.systemui.scrim; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -49,6 +49,7 @@ public class ScrimDrawable extends Drawable { private float mCornerRadius; private Rect mBounds; private ConcaveInfo mConcaveInfo; + private int mBottomEdgePosition; public ScrimDrawable() { mPaint = new Paint(); @@ -143,21 +144,43 @@ public class ScrimDrawable extends Drawable { * Make bottom edge concave with provided corner radius */ public void setBottomEdgeConcave(float radius) { + if (radius == 0) { + // Disable clipping completely when there's no radius. + mConcaveInfo = null; + return; + } // only rounding top corners for clip out path float[] cornerRadii = new float[]{radius, radius, radius, radius, 0, 0, 0, 0}; mConcaveInfo = new ConcaveInfo(radius, cornerRadii); } + /** + * Location of concave edge. + * @see #setBottomEdgeConcave(float) + */ + public void setBottomEdgePosition(int y) { + if (mBottomEdgePosition == y) { + return; + } + mBottomEdgePosition = y; + if (mConcaveInfo == null) { + return; + } + updatePath(); + invalidateSelf(); + } + @Override public void draw(@NonNull Canvas canvas) { mPaint.setColor(mMainColor); mPaint.setAlpha(mAlpha); if (mConcaveInfo != null) { drawConcave(canvas); + } else { + canvas.drawRoundRect(getBounds().left, getBounds().top, getBounds().right, + getBounds().bottom + mCornerRadius, + /* x radius*/ mCornerRadius, /* y radius*/ mCornerRadius, mPaint); } - canvas.drawRoundRect(getBounds().left, getBounds().top, getBounds().right, - getBounds().bottom + mCornerRadius, - /* x radius*/ mCornerRadius, /* y radius*/ mCornerRadius, mPaint); } private void drawConcave(Canvas canvas) { @@ -165,19 +188,23 @@ public class ScrimDrawable extends Drawable { if (mBounds == null || getBounds().right != mBounds.right || getBounds().left != mBounds.left) { - mConcaveInfo.mPath.reset(); - float left = getBounds().left; - float right = getBounds().right; - float top = 0f; - float bottom = mConcaveInfo.mPathOverlap; - mConcaveInfo.mPath.addRoundRect(left, top, right, bottom, - mConcaveInfo.mCornerRadii, Path.Direction.CW); + mBounds = getBounds(); + updatePath(); } - mBounds = getBounds(); - int translation = (int) (mBounds.bottom - mConcaveInfo.mPathOverlap); - canvas.translate(0, translation); canvas.clipOutPath(mConcaveInfo.mPath); - canvas.translate(0, -translation); + canvas.drawRect(getBounds().left, getBounds().top, getBounds().right, + mBottomEdgePosition + mConcaveInfo.mPathOverlap, mPaint); + } + + private void updatePath() { + mConcaveInfo.mPath.reset(); + if (mBounds == null) { + mBounds = getBounds(); + } + float top = mBottomEdgePosition; + float bottom = mBottomEdgePosition + mConcaveInfo.mPathOverlap; + mConcaveInfo.mPath.addRoundRect(mBounds.left, top, mBounds.right, bottom, + mConcaveInfo.mCornerRadii, Path.Direction.CW); } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java index a537299d4979..0d9ade6da49c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java +++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 The Android Open Source Project + * Copyright (C) 2021 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. @@ -11,10 +11,10 @@ * 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 + * limitations under the License. */ -package com.android.systemui.statusbar; +package com.android.systemui.scrim; import static java.lang.Float.isNaN; @@ -38,7 +38,6 @@ import androidx.core.graphics.ColorUtils; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.colorextraction.ColorExtractor; -import com.android.internal.colorextraction.drawable.ScrimDrawable; import com.android.systemui.R; import java.util.concurrent.Executor; @@ -96,6 +95,9 @@ public class ScrimView extends View { }); } + /** + * Needed for WM Shell, which has its own thread structure. + */ public void setExecutor(Executor executor, Looper looper) { mExecutor = executor; mExecutorLooper = looper; @@ -108,7 +110,8 @@ public class ScrimView extends View { } } - public void setDrawable(Drawable drawable) { + @VisibleForTesting + void setDrawable(Drawable drawable) { executeOnExecutor(() -> { mDrawable = drawable; mDrawable.setCallback(this); @@ -144,16 +147,24 @@ public class ScrimView extends View { }); } + /** + * Sets the color of the scrim, without animating them. + */ public void setColors(@NonNull ColorExtractor.GradientColors colors) { setColors(colors, false); } + /** + * Sets the scrim colors, optionally animating them. + * @param colors The colors. + * @param animated If we should animate the transition. + */ public void setColors(@NonNull ColorExtractor.GradientColors colors, boolean animated) { if (colors == null) { throw new IllegalArgumentException("Colors cannot be null"); } executeOnExecutor(() -> { - synchronized(mColorLock) { + synchronized (mColorLock) { if (mColors.equals(colors)) { return; } @@ -168,17 +179,28 @@ public class ScrimView extends View { return mDrawable; } + /** + * Returns current scrim colors. + */ public ColorExtractor.GradientColors getColors() { - synchronized(mColorLock) { + synchronized (mColorLock) { mTmpColors.set(mColors); } return mTmpColors; } + /** + * Applies tint to this view, without animations. + */ public void setTint(int color) { setTint(color, false); } + /** + * Tints this view, optionally animating it. + * @param color The color. + * @param animated If we should animate. + */ public void setTint(int color, boolean animated) { executeOnExecutor(() -> { if (mTintColor == color) { @@ -200,8 +222,8 @@ public class ScrimView extends View { } else { boolean hasAlpha = Color.alpha(mTintColor) != 0; if (hasAlpha) { - PorterDuff.Mode targetMode = mColorFilter == null ? Mode.SRC_OVER : - mColorFilter.getMode(); + PorterDuff.Mode targetMode = mColorFilter == null + ? Mode.SRC_OVER : mColorFilter.getMode(); if (mColorFilter == null || mColorFilter.getColor() != mTintColor) { mColorFilter = new PorterDuffColorFilter(mTintColor, targetMode); } @@ -254,6 +276,9 @@ public class ScrimView extends View { return mViewAlpha; } + /** + * Sets a callback that is invoked whenever the alpha, color, or tint change. + */ public void setChangeRunnable(Runnable changeRunnable, Executor changeRunnableExecutor) { mChangeRunnable = changeRunnable; mChangeRunnableExecutor = changeRunnableExecutor; @@ -276,9 +301,9 @@ public class ScrimView extends View { * Make bottom edge concave so overlap between layers is not visible for alphas between 0 and 1 * @return height of concavity */ - public float enableBottomEdgeConcave() { + public float enableBottomEdgeConcave(boolean clipScrim) { if (mDrawable instanceof ScrimDrawable) { - float radius = getResources().getDimensionPixelSize(CORNER_RADIUS); + float radius = clipScrim ? getResources().getDimensionPixelSize(CORNER_RADIUS) : 0; ((ScrimDrawable) mDrawable).setBottomEdgeConcave(radius); return radius; } @@ -286,6 +311,16 @@ public class ScrimView extends View { } /** + * The position of the bottom of the scrim, used for clipping. + * @see #enableBottomEdgeConcave(boolean) + */ + public void setBottomEdgePosition(int y) { + if (mDrawable instanceof ScrimDrawable) { + ((ScrimDrawable) mDrawable).setBottomEdgePosition(y); + } + } + + /** * Enable view to have rounded corners with radius of {@link #CORNER_RADIUS} */ public void enableRoundedCorners() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 4fc49ed26f64..36a370c71216 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -780,7 +780,7 @@ public class NotificationStackScrollLayoutController { return mView.isLayoutRtl(); } - public float getLeft() { + public int getLeft() { return mView.getLeft(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 961699e9e600..e2af940e66a7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -515,6 +515,7 @@ public class NotificationPanelViewController extends PanelViewController { private boolean mDelayShowingKeyguardStatusBar; private boolean mAnimatingQS; + private final Rect mKeyguardStatusAreaClipBounds = new Rect(); private int mOldLayoutDirection; private NotificationShelfController mNotificationShelfController; @@ -522,6 +523,7 @@ public class NotificationPanelViewController extends PanelViewController { private final Executor mUiExecutor; private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL; + private int mScrimCornerRadius; private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() { @Override @@ -628,6 +630,7 @@ public class NotificationPanelViewController extends PanelViewController { mDozeParameters = dozeParameters; mBiometricUnlockController = biometricUnlockController; mScrimController = scrimController; + mScrimController.setClipsQsScrim(!mShouldUseSplitNotificationShade); mUserManager = userManager; mMediaDataManager = mediaDataManager; mQuickAccessWalletClient = quickAccessWalletClient; @@ -852,10 +855,16 @@ public class NotificationPanelViewController extends PanelViewController { public void updateResources() { mSplitShadeNotificationsTopPadding = mResources.getDimensionPixelSize(R.dimen.notifications_top_padding_split_shade); + mScrimCornerRadius = + mResources.getDimensionPixelSize(R.dimen.notification_scrim_corner_radius); int qsWidth = mResources.getDimensionPixelSize(R.dimen.qs_panel_width); int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width); mShouldUseSplitNotificationShade = Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources); + mScrimController.setClipsQsScrim(!mShouldUseSplitNotificationShade); + if (mQs != null) { + mQs.setTranslateWhileExpanding(mShouldUseSplitNotificationShade); + } // To change the constraints at runtime, all children of the ConstraintLayout must have ids ensureAllViewsHaveIds(mNotificationContainerParent); ConstraintSet constraintSet = new ConstraintSet(); @@ -2003,15 +2012,23 @@ public class NotificationPanelViewController extends PanelViewController { mDepthController.setQsPanelExpansion(qsExpansionFraction); } - private void setNotificationBounds(float qsExpansionFraction, int qsPanelBottomY) { - float top = 0; - float bottom = 0; - float left = 0; - float right = 0; - if (qsPanelBottomY > 0) { - // notification shade is expanding/expanded + /** + * Updates scrim bounds, QS clipping, and KSV clipping as well based on the bounds of the shade + * and QS state. + * + * @param qsFraction QS expansion fraction, from getQsExpansionFraction(). + * @param qsPanelBottomY Absolute y position of the bottom of QS as it's being pulled. + */ + private void setNotificationBounds(float qsFraction, int qsPanelBottomY) { + int top = 0; + int bottom = 0; + int left = 0; + int right = 0; + boolean visible = qsFraction > 0 || qsPanelBottomY > 0; + if (visible || !mShouldUseSplitNotificationShade) { if (!mShouldUseSplitNotificationShade) { - top = qsPanelBottomY; + float notificationTop = mAmbientState.getStackY() - mQsNotificationTopPadding; + top = (int) Math.min(qsPanelBottomY, notificationTop); bottom = getView().getBottom(); left = getView().getLeft(); right = getView().getRight(); @@ -2022,6 +2039,17 @@ public class NotificationPanelViewController extends PanelViewController { right = mNotificationStackScrollLayoutController.getRight(); } } + + if (!mShouldUseSplitNotificationShade) { + // Fancy clipping for quick settings + if (mQs != null) { + mQs.setFancyClipping(top, bottom, mScrimCornerRadius, visible); + } + // The padding on this area is large enough that we can use a cheaper clipping strategy + mKeyguardStatusAreaClipBounds.set(left, top, right, bottom); + mKeyguardStatusViewController.setClipBounds(visible + ? mKeyguardStatusAreaClipBounds : null); + } mScrimController.setNotificationsBounds(left, top, right, bottom); } @@ -2493,6 +2521,10 @@ public class NotificationPanelViewController extends PanelViewController { float appearAmount = mNotificationStackScrollLayoutController .calculateAppearFraction(mExpandedHeight); float startHeight = -mQsExpansionHeight; + if (!mShouldUseSplitNotificationShade && mBarState == StatusBarState.SHADE) { + // Small parallax as we pull down and clip QS + startHeight = -mQsExpansionHeight * 0.2f; + } if (mKeyguardBypassController.getBypassEnabled() && isOnKeyguard() && mNotificationStackScrollLayoutController.isPulseExpanding()) { if (!mPulseExpansionHandler.isExpanding() @@ -3128,8 +3160,10 @@ public class NotificationPanelViewController extends PanelViewController { mQs.setPanelView(mHeightListener); mQs.setExpandClickListener(mOnClickListener); mQs.setHeaderClickable(mQsExpansionEnabled); + mQs.setTranslateWhileExpanding(mShouldUseSplitNotificationShade); updateQSPulseExpansion(); mQs.setOverscrolling(mStackScrollerOverscrolling); + mQs.setTranslateWhileExpanding(mShouldUseSplitNotificationShade); // recompute internal state when qspanel height changes mQs.getView().addOnLayoutChangeListener( @@ -3925,7 +3959,10 @@ public class NotificationPanelViewController extends PanelViewController { // animate out // the top of QS if (!mQsExpanded) { - mQs.animateHeaderSlidingOut(); + // TODO(b/185683835) Nicer clipping when using new spacial model + if (mShouldUseSplitNotificationShade) { + mQs.animateHeaderSlidingOut(); + } } } else { mKeyguardStatusBar.setAlpha(1f); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java index 064086a828cb..5a2a6f21f94a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java @@ -147,9 +147,6 @@ public abstract class PanelViewController { private float mInitialTouchX; private boolean mTouchDisabled; - // AmbientState will never be null since it provides an @Inject constructor for Dagger to call. - private AmbientState mAmbientState; - /** * Whether or not the PanelView can be expanded or collapsed with a drag. */ @@ -172,6 +169,7 @@ public abstract class PanelViewController { protected final Resources mResources; protected final KeyguardStateController mKeyguardStateController; protected final SysuiStatusBarStateController mStatusBarStateController; + protected final AmbientState mAmbientState; protected void onExpandingFinished() { mBar.onExpandingFinished(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 5e9c758da07a..0d96ead964f7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -48,8 +48,8 @@ import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dock.DockManager; +import com.android.systemui.scrim.ScrimView; import com.android.systemui.statusbar.FeatureFlags; -import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.notification.stack.ViewState; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -96,6 +96,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump * When at least 1 scrim is fully opaque (alpha set to 1.) */ public static final int OPAQUE = 2; + private boolean mClipsQsScrim; @IntDef(prefix = {"VISIBILITY_"}, value = { TRANSPARENT, @@ -165,6 +166,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump // Assuming the shade is expanded during initialization private float mExpansionFraction = 1f; private float mQsExpansion; + private boolean mQsBottomVisible; private boolean mDarkenWhileDragging; private boolean mExpansionAffectsAlpha = true; @@ -183,6 +185,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump private int mInFrontTint; private int mBehindTint; + private int mNotificationsTint; private int mBubbleTint; private boolean mWallpaperVisibilityTimedOut; @@ -265,6 +268,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump mScrimForBubble = scrimForBubble; updateThemeColors(); + behindScrim.enableBottomEdgeConcave(mClipsQsScrim); mNotificationsScrim.enableRoundedCorners(); if (mScrimBehindChangeRunnable != null) { @@ -331,14 +335,17 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump mInFrontTint = state.getFrontTint(); mBehindTint = state.getBehindTint(); + mNotificationsTint = state.getNotifTint(); mBubbleTint = state.getBubbleTint(); mInFrontAlpha = state.getFrontAlpha(); mBehindAlpha = state.getBehindAlpha(); mBubbleAlpha = state.getBubbleAlpha(); - if (isNaN(mBehindAlpha) || isNaN(mInFrontAlpha)) { + mNotificationsAlpha = state.getNotifAlpha(); + if (isNaN(mBehindAlpha) || isNaN(mInFrontAlpha) || isNaN(mNotificationsAlpha)) { throw new IllegalStateException("Scrim opacity is NaN for state: " + state + ", front: " - + mInFrontAlpha + ", back: " + mBehindAlpha); + + mInFrontAlpha + ", back: " + mBehindAlpha + ", notif: " + + mNotificationsAlpha); } applyExpansionToAlpha(); @@ -397,7 +404,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump scheduleUpdate(); } - dispatchScrimState(mScrimBehind.getViewAlpha()); + dispatchBackScrimState(mScrimBehind.getViewAlpha()); } private boolean shouldFadeAwayWallpaper() { @@ -491,21 +498,25 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump */ public void setNotificationsBounds(float left, float top, float right, float bottom) { mNotificationsScrim.setDrawableBounds(left, top, right, bottom); + if (mClipsQsScrim) { + mScrimBehind.setBottomEdgePosition((int) top); + } } /** * Current state of the QuickSettings when pulling it from the top. * * @param expansionFraction From 0 to 1 where 0 means collapsed and 1 expanded. - * @param qsPanelBottomY absolute Y position of qs panel bottom + * @param qsPanelBottomY Absolute Y position of qs panel bottom */ public void setQsPosition(float expansionFraction, int qsPanelBottomY) { if (isNaN(expansionFraction)) { return; } - updateNotificationsScrimAlpha(expansionFraction, qsPanelBottomY); - if (mQsExpansion != expansionFraction) { + boolean qsBottomVisible = qsPanelBottomY > 0; + if (mQsExpansion != expansionFraction || mQsBottomVisible != qsBottomVisible) { mQsExpansion = expansionFraction; + mQsBottomVisible = qsBottomVisible; boolean relevantState = (mState == ScrimState.SHADE_LOCKED || mState == ScrimState.KEYGUARD || mState == ScrimState.PULSING @@ -517,31 +528,36 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump } } - private void updateNotificationsScrimAlpha(float qsExpansion, int qsPanelBottomY) { - float newAlpha = 0; - if (qsPanelBottomY > 0) { - float interpolator = 0; - if (mState == ScrimState.UNLOCKED || mState == ScrimState.SHADE_LOCKED) { - interpolator = getInterpolatedFraction(); - } else { - interpolator = qsExpansion; - } - newAlpha = MathUtils.lerp(0, 1, interpolator); + /** + * If QS and notification scrims should not overlap, and should be clipped to each other's + * bounds instead. + */ + public void setClipsQsScrim(boolean clipScrim) { + if (clipScrim == mClipsQsScrim) { + return; } - if (newAlpha != mNotificationsAlpha) { - mNotificationsAlpha = newAlpha; - // update alpha without animating - mNotificationsScrim.setViewAlpha(newAlpha); + mClipsQsScrim = clipScrim; + for (ScrimState state : ScrimState.values()) { + state.setClipQsScrim(mClipsQsScrim); + } + if (mScrimBehind != null) { + mScrimBehind.enableBottomEdgeConcave(mClipsQsScrim); } } + @VisibleForTesting + public boolean getClipQsScrim() { + return mClipsQsScrim; + } + private void setOrAdaptCurrentAnimation(@Nullable View scrim) { if (scrim == null) { return; } float alpha = getCurrentScrimAlpha(scrim); - if (isAnimating(scrim)) { + boolean qsScrimPullingDown = scrim == mScrimBehind && mQsBottomVisible; + if (isAnimating(scrim) && !qsScrimPullingDown) { // Adapt current animation. ValueAnimator previousAnimator = (ValueAnimator) scrim.getTag(TAG_KEY_ANIM); float previousEndValue = (Float) scrim.getTag(TAG_END_ALPHA); @@ -562,7 +578,19 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump return; } - if (mState == ScrimState.UNLOCKED || mState == ScrimState.BUBBLE_EXPANDED) { + if (mState == ScrimState.UNLOCKED) { + // Darken scrim as you pull down the shade when unlocked + float behindFraction = getInterpolatedFraction(); + behindFraction = (float) Math.pow(behindFraction, 0.8f); + if (mClipsQsScrim) { + mBehindAlpha = 1; + mNotificationsAlpha = behindFraction * mDefaultScrimAlpha; + } else { + mBehindAlpha = behindFraction * mDefaultScrimAlpha; + mNotificationsAlpha = mBehindAlpha; + } + mInFrontAlpha = 0; + } else if (mState == ScrimState.BUBBLE_EXPANDED) { // Darken scrim as you pull down the shade when unlocked float behindFraction = getInterpolatedFraction(); behindFraction = (float) Math.pow(behindFraction, 0.8f); @@ -573,27 +601,45 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump // Either darken of make the scrim transparent when you // pull down the shade float interpolatedFract = getInterpolatedFraction(); - float alphaBehind = mState.getBehindAlpha(); + float stateBehind = mClipsQsScrim ? mState.getNotifAlpha() : mState.getBehindAlpha(); + float backAlpha; if (mDarkenWhileDragging) { - mBehindAlpha = MathUtils.lerp(mDefaultScrimAlpha, alphaBehind, + backAlpha = MathUtils.lerp(mDefaultScrimAlpha, stateBehind, interpolatedFract); - mInFrontAlpha = mState.getFrontAlpha(); } else { - mBehindAlpha = MathUtils.lerp(0 /* start */, alphaBehind, + backAlpha = MathUtils.lerp(0 /* start */, stateBehind, interpolatedFract); - mInFrontAlpha = mState.getFrontAlpha(); } - mBehindTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getBehindTint(), - mState.getBehindTint(), interpolatedFract); + mInFrontAlpha = mState.getFrontAlpha(); + int backTint; + if (mClipsQsScrim) { + backTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getNotifTint(), + mState.getNotifTint(), interpolatedFract); + } else { + backTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getBehindTint(), + mState.getBehindTint(), interpolatedFract); + } if (mQsExpansion > 0) { - mBehindAlpha = MathUtils.lerp(mBehindAlpha, mDefaultScrimAlpha, mQsExpansion); - mBehindTint = ColorUtils.blendARGB(mBehindTint, - ScrimState.SHADE_LOCKED.getBehindTint(), mQsExpansion); + backAlpha = MathUtils.lerp(backAlpha, mDefaultScrimAlpha, mQsExpansion); + int stateTint = mClipsQsScrim ? ScrimState.SHADE_LOCKED.getNotifTint() + : ScrimState.SHADE_LOCKED.getBehindTint(); + backTint = ColorUtils.blendARGB(backTint, stateTint, mQsExpansion); + } + if (mClipsQsScrim) { + mNotificationsAlpha = backAlpha; + mNotificationsTint = backTint; + mBehindAlpha = 1; + mBehindTint = Color.BLACK; + } else { + mBehindAlpha = backAlpha; + mNotificationsAlpha = Math.max(1.0f - getInterpolatedFraction(), mQsExpansion); + mBehindTint = backTint; } } - if (isNaN(mBehindAlpha) || isNaN(mInFrontAlpha)) { + if (isNaN(mBehindAlpha) || isNaN(mInFrontAlpha) || isNaN(mNotificationsAlpha)) { throw new IllegalStateException("Scrim opacity is NaN for state: " + mState - + ", front: " + mInFrontAlpha + ", back: " + mBehindAlpha); + + ", front: " + mInFrontAlpha + ", back: " + mBehindAlpha + ", notif: " + + mNotificationsAlpha); } } @@ -606,7 +652,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump setOrAdaptCurrentAnimation(mNotificationsScrim); setOrAdaptCurrentAnimation(mScrimInFront); setOrAdaptCurrentAnimation(mScrimForBubble); - dispatchScrimState(mScrimBehind.getViewAlpha()); + dispatchBackScrimState(mScrimBehind.getViewAlpha()); // Reset wallpaper timeout if it's already timeout like expanding panel while PULSING // and docking. @@ -693,10 +739,10 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump && !mBlankScreen; mScrimInFront.setColors(mColors, animateScrimInFront); - mScrimBehind.setColors(mColors, animateScrimNotifications); + mScrimBehind.setColors(mColors, animateBehindScrim); mNotificationsScrim.setColors(mColors, animateScrimNotifications); - dispatchScrimState(mScrimBehind.getViewAlpha()); + dispatchBackScrimState(mScrimBehind.getViewAlpha()); } // We want to override the back scrim opacity for the AOD state @@ -723,15 +769,21 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump dispatchScrimsVisible(); } - private void dispatchScrimState(float alpha) { + private void dispatchBackScrimState(float alpha) { + // When clipping QS, the notification scrim is the one that feels behind. + // mScrimBehind will be drawing black and its opacity will always be 1. + if (mClipsQsScrim && mQsBottomVisible) { + alpha = mNotificationsAlpha; + } mScrimStateListener.accept(mState, alpha, mScrimInFront.getColors()); } private void dispatchScrimsVisible() { + final ScrimView backScrim = mClipsQsScrim ? mNotificationsScrim : mScrimBehind; final int currentScrimVisibility; - if (mScrimInFront.getViewAlpha() == 1 || mScrimBehind.getViewAlpha() == 1) { + if (mScrimInFront.getViewAlpha() == 1 || backScrim.getViewAlpha() == 1) { currentScrimVisibility = OPAQUE; - } else if (mScrimInFront.getViewAlpha() == 0 && mScrimBehind.getViewAlpha() == 0) { + } else if (mScrimInFront.getViewAlpha() == 0 && backScrim.getViewAlpha() == 0) { currentScrimVisibility = TRANSPARENT; } else { currentScrimVisibility = SEMI_TRANSPARENT; @@ -859,7 +911,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump } else if (scrim == mScrimBehind) { return mBehindTint; } else if (scrim == mNotificationsScrim) { - return Color.TRANSPARENT; + return mNotificationsTint; } else if (scrim == mScrimForBubble) { return mBubbleTint; } else { @@ -917,9 +969,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump if (mState == ScrimState.UNLOCKED) { mInFrontTint = Color.TRANSPARENT; mBehindTint = mState.getBehindTint(); + mNotificationsTint = mState.getNotifTint(); mBubbleTint = Color.TRANSPARENT; updateScrimColor(mScrimInFront, mInFrontAlpha, mInFrontTint); updateScrimColor(mScrimBehind, mBehindAlpha, mBehindTint); + updateScrimColor(mNotificationsScrim, mNotificationsAlpha, mNotificationsTint); if (mScrimForBubble != null) { updateScrimColor(mScrimForBubble, mBubbleAlpha, mBubbleTint); } @@ -964,7 +1018,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump } if (scrim == mScrimBehind) { - dispatchScrimState(alpha); + dispatchBackScrimState(alpha); } final boolean wantsAlphaUpdate = alpha != currentAlpha; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java index a9774d850fd9..66cc26f5d295 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java @@ -22,7 +22,7 @@ import android.os.Trace; import androidx.annotation.Nullable; import com.android.systemui.dock.DockManager; -import com.android.systemui.statusbar.ScrimView; +import com.android.systemui.scrim.ScrimView; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; /** @@ -80,11 +80,16 @@ public enum ScrimState { } mFrontTint = Color.BLACK; mBehindTint = Color.BLACK; + mNotifTint = mClipQsScrim ? Color.BLACK : Color.TRANSPARENT; mBubbleTint = Color.TRANSPARENT; mFrontAlpha = 0; - mBehindAlpha = mScrimBehindAlphaKeyguard; + mBehindAlpha = mClipQsScrim ? 1 : mScrimBehindAlphaKeyguard; + mNotifAlpha = mClipQsScrim ? mScrimBehindAlphaKeyguard : 0; mBubbleAlpha = 0; + if (mClipQsScrim) { + updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK); + } } }, @@ -105,7 +110,10 @@ public enum ScrimState { BOUNCER { @Override public void prepare(ScrimState previousState) { - mBehindAlpha = mDefaultScrimAlpha; + mBehindAlpha = mClipQsScrim ? 1 : mDefaultScrimAlpha; + mBehindTint = mClipQsScrim ? Color.BLACK : Color.TRANSPARENT; + mNotifAlpha = mClipQsScrim ? mDefaultScrimAlpha : 0; + mNotifTint = Color.TRANSPARENT; mFrontAlpha = 0f; mBubbleAlpha = 0f; } @@ -126,10 +134,15 @@ public enum ScrimState { SHADE_LOCKED { @Override public void prepare(ScrimState previousState) { - mBehindAlpha = mDefaultScrimAlpha; + mBehindAlpha = mClipQsScrim ? 1 : mDefaultScrimAlpha; + mNotifAlpha = 1f; mBubbleAlpha = 0f; mFrontAlpha = 0f; mBehindTint = Color.BLACK; + + if (mClipQsScrim) { + updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK); + } } // to make sure correct color is returned before "prepare" is called @@ -224,7 +237,8 @@ public enum ScrimState { @Override public void prepare(ScrimState previousState) { // State that UI will sync to. - mBehindAlpha = 0; + mBehindAlpha = mClipQsScrim ? 1 : 0; + mNotifAlpha = 0; mFrontAlpha = 0; mBubbleAlpha = 0; @@ -253,6 +267,10 @@ public enum ScrimState { mBubbleTint = Color.BLACK; mBlankScreen = true; } + + if (mClipQsScrim) { + updateScrimColor(mScrimBehind, 1f /* alpha */, Color.BLACK); + } } }, @@ -279,12 +297,14 @@ public enum ScrimState { int mFrontTint = Color.TRANSPARENT; int mBehindTint = Color.TRANSPARENT; int mBubbleTint = Color.TRANSPARENT; + int mNotifTint = Color.TRANSPARENT; boolean mAnimateChange = true; float mAodFrontScrimAlpha; float mFrontAlpha; float mBehindAlpha; float mBubbleAlpha; + float mNotifAlpha; float mScrimBehindAlphaKeyguard; float mDefaultScrimAlpha; @@ -301,6 +321,7 @@ public enum ScrimState { boolean mWakeLockScreenSensorActive; boolean mKeyguardFadingAway; long mKeyguardFadingAwayDuration; + boolean mClipQsScrim; public void init(ScrimView scrimInFront, ScrimView scrimBehind, ScrimView scrimForBubble, DozeParameters dozeParameters, DockManager dockManager) { @@ -325,6 +346,10 @@ public enum ScrimState { return mBehindAlpha; } + public float getNotifAlpha() { + return mNotifAlpha; + } + public float getBubbleAlpha() { return mBubbleAlpha; } @@ -337,6 +362,10 @@ public enum ScrimState { return mBehindTint; } + public int getNotifTint() { + return mNotifTint; + } + public int getBubbleTint() { return mBubbleTint; } @@ -406,4 +435,8 @@ public enum ScrimState { mKeyguardFadingAway = fadingAway; mKeyguardFadingAwayDuration = duration; } + + public void setClipQsScrim(boolean clipsQsScrim) { + mClipQsScrim = clipsQsScrim; + } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 026072bf0d6a..de448100d33f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -21,7 +21,6 @@ import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; import static android.app.StatusBarManager.WindowType; import static android.app.StatusBarManager.WindowVisibleState; import static android.app.StatusBarManager.windowStateToString; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY; import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT; import static android.view.InsetsState.ITYPE_STATUS_BAR; import static android.view.InsetsState.containsType; @@ -180,6 +179,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSFragment; import com.android.systemui.qs.QSPanelController; import com.android.systemui.recents.ScreenPinningRequest; +import com.android.systemui.scrim.ScrimView; import com.android.systemui.settings.brightness.BrightnessSlider; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.AutoHideUiElement; @@ -202,7 +202,6 @@ import com.android.systemui.statusbar.NotificationShelfController; import com.android.systemui.statusbar.NotificationViewHierarchyManager; import com.android.systemui.statusbar.PowerButtonReveal; import com.android.systemui.statusbar.PulseExpansionHandler; -import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SuperStatusBarViewFactory; import com.android.systemui.statusbar.SysuiStatusBarStateController; diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java index b823534c2813..b95545576e56 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java @@ -63,11 +63,11 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.scrim.ScrimView; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.FeatureFlags; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationShadeWindowController; -import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.notification.NotificationChannelHelper; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ScrimViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/scrim/ScrimViewTest.java index c2e58efe1328..a345f7804ae9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ScrimViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/scrim/ScrimViewTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2021 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. @@ -11,10 +11,10 @@ * 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 + * limitations under the License. */ -package com.android.systemui.statusbar; +package com.android.systemui.scrim; import static junit.framework.Assert.assertEquals; @@ -31,7 +31,6 @@ import android.view.View; import androidx.test.filters.SmallTest; import com.android.internal.colorextraction.ColorExtractor; -import com.android.internal.colorextraction.drawable.ScrimDrawable; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.utils.leaks.LeakCheckedTest; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index 8633eb466b6c..559ee6f29bb6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -49,8 +49,8 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.DejankUtils; import com.android.systemui.SysuiTestCase; import com.android.systemui.dock.DockManager; +import com.android.systemui.scrim.ScrimView; import com.android.systemui.statusbar.FeatureFlags; -import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.FakeExecutor; @@ -69,6 +69,7 @@ import org.mockito.stubbing.Answer; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -91,7 +92,7 @@ public class ScrimControllerTest extends SysuiTestCase { @Mock private AlarmManager mAlarmManager; @Mock - private DozeParameters mDozeParamenters; + private DozeParameters mDozeParameters; @Mock LightBarController mLightBarController; @Mock @@ -200,8 +201,8 @@ public class ScrimControllerTest extends SysuiTestCase { return null; }).when(mScrimBehind).postOnAnimationDelayed(any(Runnable.class), anyLong()); - when(mDozeParamenters.getAlwaysOn()).thenAnswer(invocation -> mAlwaysOnEnabled); - when(mDozeParamenters.getDisplayNeedsBlanking()).thenReturn(true); + when(mDozeParameters.getAlwaysOn()).thenAnswer(invocation -> mAlwaysOnEnabled); + when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true); doAnswer((Answer<Void>) invocation -> { mScrimState = invocation.getArgument(0); @@ -220,7 +221,7 @@ public class ScrimControllerTest extends SysuiTestCase { when(mDockManager.isDocked()).thenReturn(false); mScrimController = new ScrimController(mLightBarController, - mDozeParamenters, mAlarmManager, mKeyguardStateController, mDelayedWakeLockBuilder, + mDozeParameters, mAlarmManager, mKeyguardStateController, mDelayedWakeLockBuilder, new FakeHandler(mLooper.getLooper()), mKeyguardUpdateMonitor, mDockManager, mConfigurationController, mFeatureFlags, new FakeExecutor(new FakeSystemClock())); @@ -238,6 +239,10 @@ public class ScrimControllerTest extends SysuiTestCase { @After public void tearDown() { finishAnimationsImmediately(); + Arrays.stream(ScrimState.values()).forEach((scrim) -> { + scrim.setAodFrontScrimAlpha(0f); + scrim.setClipQsScrim(false); + }); DejankUtils.setImmediate(false); } @@ -259,9 +264,30 @@ public class ScrimControllerTest extends SysuiTestCase { @Test public void transitionToShadeLocked() { mScrimController.transitionTo(ScrimState.SHADE_LOCKED); + mScrimController.setQsPosition(1f, 0); + finishAnimationsImmediately(); + + assertScrimAlpha(Map.of( + mNotificationsScrim, OPAQUE, + mScrimInFront, TRANSPARENT, + mScrimBehind, OPAQUE)); + + assertScrimTinted(Map.of( + mScrimInFront, false, + mScrimBehind, true, + mScrimForBubble, false + )); + } + + @Test + public void transitionToShadeLocked_clippingQs() { + mScrimController.setClipsQsScrim(true); + mScrimController.transitionTo(ScrimState.SHADE_LOCKED); + mScrimController.setQsPosition(1f, 0); finishAnimationsImmediately(); assertScrimAlpha(Map.of( + mNotificationsScrim, OPAQUE, mScrimInFront, TRANSPARENT, mScrimBehind, OPAQUE)); @@ -403,9 +429,6 @@ public class ScrimControllerTest extends SysuiTestCase { mScrimController.setAodFrontScrimAlpha(0.3f); Assert.assertEquals(ScrimState.AOD.getFrontAlpha(), mScrimInFront.getViewAlpha(), 0.001f); Assert.assertNotEquals(0.3f, mScrimInFront.getViewAlpha(), 0.001f); - - // Reset value since enums are static. - mScrimController.setAodFrontScrimAlpha(0f); } @Test @@ -500,11 +523,34 @@ public class ScrimControllerTest extends SysuiTestCase { // Back scrim should be visible without tint assertScrimAlpha(Map.of( mScrimInFront, TRANSPARENT, + mNotificationsScrim, TRANSPARENT, mScrimBehind, OPAQUE)); assertScrimTinted(Map.of( mScrimInFront, false, mScrimBehind, false, + mNotificationsScrim, false, + mScrimForBubble, false + )); + } + + @Test + public void transitionToKeyguardBouncer_clippingQs() { + mScrimController.setClipsQsScrim(true); + mScrimController.transitionTo(ScrimState.BOUNCER); + finishAnimationsImmediately(); + // Front scrim should be transparent + // Back scrim should be clipping QS + // Notif scrim should be visible without tint + assertScrimAlpha(Map.of( + mScrimInFront, TRANSPARENT, + mNotificationsScrim, OPAQUE, + mScrimBehind, OPAQUE)); + + assertScrimTinted(Map.of( + mScrimInFront, false, + mScrimBehind, true, + mNotificationsScrim, false, mScrimForBubble, false )); } @@ -530,9 +576,11 @@ public class ScrimControllerTest extends SysuiTestCase { finishAnimationsImmediately(); assertScrimAlpha(Map.of( mScrimInFront, TRANSPARENT, + mNotificationsScrim, TRANSPARENT, mScrimBehind, TRANSPARENT)); assertScrimTinted(Map.of( + mNotificationsScrim, false, mScrimInFront, false, mScrimBehind, true, mScrimForBubble, false @@ -542,6 +590,7 @@ public class ScrimControllerTest extends SysuiTestCase { mScrimController.setPanelExpansion(0.5f); assertScrimAlpha(Map.of( mScrimInFront, TRANSPARENT, + mNotificationsScrim, SEMI_TRANSPARENT, mScrimBehind, SEMI_TRANSPARENT)); } @@ -617,6 +666,32 @@ public class ScrimControllerTest extends SysuiTestCase { } @Test + public void qsExpansion_clippingQs() { + reset(mScrimBehind); + mScrimController.setClipsQsScrim(true); + mScrimController.setQsPosition(1f, 999 /* value doesn't matter */); + finishAnimationsImmediately(); + + assertScrimAlpha(Map.of( + mScrimInFront, TRANSPARENT, + mScrimBehind, OPAQUE, + mNotificationsScrim, OPAQUE)); + } + + @Test + public void qsExpansion_half_clippingQs() { + reset(mScrimBehind); + mScrimController.setClipsQsScrim(true); + mScrimController.setQsPosition(0.5f, 999 /* value doesn't matter */); + finishAnimationsImmediately(); + + assertScrimAlpha(Map.of( + mScrimInFront, TRANSPARENT, + mScrimBehind, OPAQUE, + mNotificationsScrim, SEMI_TRANSPARENT)); + } + + @Test public void panelExpansionAffectsAlpha() { mScrimController.setPanelExpansion(0f); mScrimController.setPanelExpansion(0.5f); @@ -919,13 +994,13 @@ public class ScrimControllerTest extends SysuiTestCase { @Test public void testAnimatesTransitionToAod() { - when(mDozeParamenters.shouldControlScreenOff()).thenReturn(false); + when(mDozeParameters.shouldControlScreenOff()).thenReturn(false); ScrimState.AOD.prepare(ScrimState.KEYGUARD); Assert.assertFalse("No animation when ColorFade kicks in", ScrimState.AOD.getAnimateChange()); - reset(mDozeParamenters); - when(mDozeParamenters.shouldControlScreenOff()).thenReturn(true); + reset(mDozeParameters); + when(mDozeParameters.shouldControlScreenOff()).thenReturn(true); ScrimState.AOD.prepare(ScrimState.KEYGUARD); Assert.assertTrue("Animate scrims when ColorFade won't be triggered", ScrimState.AOD.getAnimateChange()); @@ -977,6 +1052,7 @@ public class ScrimControllerTest extends SysuiTestCase { mScrimController.setPanelExpansion(0.5f); // notifications scrim alpha change require calling setQsPosition mScrimController.setQsPosition(0, 300); + finishAnimationsImmediately(); assertScrimAlpha(Map.of( mScrimBehind, SEMI_TRANSPARENT, @@ -985,6 +1061,21 @@ public class ScrimControllerTest extends SysuiTestCase { } @Test + public void testScrimsVisible_whenShadeVisible_clippingQs() { + mScrimController.setClipsQsScrim(true); + mScrimController.transitionTo(ScrimState.UNLOCKED); + mScrimController.setPanelExpansion(0.5f); + // notifications scrim alpha change require calling setQsPosition + mScrimController.setQsPosition(0.5f, 300); + finishAnimationsImmediately(); + + assertScrimAlpha(Map.of( + mScrimBehind, OPAQUE, + mNotificationsScrim, SEMI_TRANSPARENT, + mScrimInFront, TRANSPARENT)); + } + + @Test public void testScrimsVisible_whenShadeVisibleOnLockscreen() { mScrimController.transitionTo(ScrimState.KEYGUARD); mScrimController.setQsPosition(0.5f, 300); @@ -1008,8 +1099,6 @@ public class ScrimControllerTest extends SysuiTestCase { } private void assertScrimTinted(Map<ScrimView, Boolean> scrimToTint) { - // notifications scrim should have always transparent tint - assertScrimTint(mNotificationsScrim, false); scrimToTint.forEach((scrim, hasTint) -> assertScrimTint(scrim, hasTint)); } @@ -1047,6 +1136,12 @@ public class ScrimControllerTest extends SysuiTestCase { } scrimToAlpha.forEach((scrimView, alpha) -> assertScrimAlpha(scrimView, alpha)); + // When clipping, QS scrim should not affect combined visibility. + if (mScrimController.getClipQsScrim() && scrimToAlpha.get(mScrimBehind) == OPAQUE) { + scrimToAlpha = new HashMap<>(scrimToAlpha); + scrimToAlpha.remove(mScrimBehind); + } + // Check combined scrim visibility. final int visibility; if (scrimToAlpha.values().contains(OPAQUE)) { |