Modified the way the notifications clip to the boundary
Previously the notification shade didn't clip at all to the qs - notification boundary, which could lead to content peaking outside of the boundary during dismissal or other situations. We now clip to the boundary using rounded rect clipping. This also allows us to remove various complex clipping logic that was updating the clipping on the top for individual notification views. This also fixes some bugs on the split shade where the notifications were wrongly clipped previously. This is change 2/2 to improve the notification dismissal. Fixes: 181887261 Test: atest SystemUITests Change-Id: I1ebe777c50b85a500ecce22823d547c6d9c3c6d5
This commit is contained in:
@@ -330,30 +330,6 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setDistanceToTopRoundness(float distanceToTopRoundness) {
|
|
||||||
super.setDistanceToTopRoundness(distanceToTopRoundness);
|
|
||||||
mBackgroundNormal.setDistanceToTopRoundness(distanceToTopRoundness);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Sets whether this view is the last notification in a section. */
|
|
||||||
@Override
|
|
||||||
public void setLastInSection(boolean lastInSection) {
|
|
||||||
if (lastInSection != mLastInSection) {
|
|
||||||
super.setLastInSection(lastInSection);
|
|
||||||
mBackgroundNormal.setLastInSection(lastInSection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Sets whether this view is the first notification in a section. */
|
|
||||||
@Override
|
|
||||||
public void setFirstInSection(boolean firstInSection) {
|
|
||||||
if (firstInSection != mFirstInSection) {
|
|
||||||
super.setFirstInSection(firstInSection);
|
|
||||||
mBackgroundNormal.setFirstInSection(firstInSection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set an override tint color that is used for the background.
|
* Set an override tint color that is used for the background.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -846,8 +846,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
updateClickAndFocus();
|
updateClickAndFocus();
|
||||||
if (mNotificationParent != null) {
|
if (mNotificationParent != null) {
|
||||||
setOverrideTintColor(NO_COLOR, 0.0f);
|
setOverrideTintColor(NO_COLOR, 0.0f);
|
||||||
// Let's reset the distance to top roundness, as this isn't applied to group children
|
|
||||||
setDistanceToTopRoundness(NO_ROUNDNESS);
|
|
||||||
mNotificationParent.updateBackgroundForGroupState();
|
mNotificationParent.updateBackgroundForGroupState();
|
||||||
}
|
}
|
||||||
updateBackgroundClipping();
|
updateBackgroundClipping();
|
||||||
@@ -3036,24 +3034,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
|||||||
|| mExpandAnimationRunning || mChildIsExpanding));
|
|| mExpandAnimationRunning || mChildIsExpanding));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean topAmountNeedsClipping() {
|
|
||||||
if (isGroupExpanded()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (isGroupExpansionChanging()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (getShowingLayout().shouldClipToRounding(true /* topRounded */,
|
|
||||||
false /* bottomRounded */)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (mGuts != null && mGuts.getAlpha() != 0.0f) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean childNeedsClipping(View child) {
|
protected boolean childNeedsClipping(View child) {
|
||||||
if (child instanceof NotificationContentView) {
|
if (child instanceof NotificationContentView) {
|
||||||
|
|||||||
@@ -75,16 +75,13 @@ public abstract class ExpandableOutlineView extends ExpandableView {
|
|||||||
* it is moved. Otherwise, the translation is set on the {@code ExpandableOutlineView} itself.
|
* it is moved. Otherwise, the translation is set on the {@code ExpandableOutlineView} itself.
|
||||||
*/
|
*/
|
||||||
protected boolean mShouldTranslateContents;
|
protected boolean mShouldTranslateContents;
|
||||||
private boolean mTopAmountRounded;
|
|
||||||
private float mDistanceToTopRoundness = -1;
|
|
||||||
private float[] mTmpCornerRadii = new float[8];
|
private float[] mTmpCornerRadii = new float[8];
|
||||||
|
|
||||||
private final ViewOutlineProvider mProvider = new ViewOutlineProvider() {
|
private final ViewOutlineProvider mProvider = new ViewOutlineProvider() {
|
||||||
@Override
|
@Override
|
||||||
public void getOutline(View view, Outline outline) {
|
public void getOutline(View view, Outline outline) {
|
||||||
if (!mCustomOutline && getCurrentTopRoundness() == 0.0f
|
if (!mCustomOutline && getCurrentTopRoundness() == 0.0f
|
||||||
&& getCurrentBottomRoundness() == 0.0f && !mAlwaysRoundBothCorners
|
&& getCurrentBottomRoundness() == 0.0f && !mAlwaysRoundBothCorners) {
|
||||||
&& !mTopAmountRounded) {
|
|
||||||
int translation = mShouldTranslateContents ? (int) getTranslation() : 0;
|
int translation = mShouldTranslateContents ? (int) getTranslation() : 0;
|
||||||
int left = Math.max(translation, 0);
|
int left = Math.max(translation, 0);
|
||||||
int top = mClipTopAmount + mBackgroundTop;
|
int top = mClipTopAmount + mBackgroundTop;
|
||||||
@@ -168,33 +165,15 @@ public abstract class ExpandableOutlineView extends ExpandableView {
|
|||||||
@Override
|
@Override
|
||||||
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
|
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
|
||||||
canvas.save();
|
canvas.save();
|
||||||
Path intersectPath = null;
|
|
||||||
if (mTopAmountRounded && topAmountNeedsClipping()) {
|
|
||||||
int left = (int) (- mExtraWidthForClipping / 2.0f);
|
|
||||||
int top = (int) (mClipTopAmount - mDistanceToTopRoundness);
|
|
||||||
int right = getWidth() + (int) (mExtraWidthForClipping + left);
|
|
||||||
int bottom = (int) Math.max(mMinimumHeightForClipping,
|
|
||||||
Math.max(getActualHeight() - mClipBottomAmount, top + mOutlineRadius));
|
|
||||||
getRoundedRectPath(left, top, right, bottom, mOutlineRadius, 0.0f, mClipPath);
|
|
||||||
intersectPath = mClipPath;
|
|
||||||
}
|
|
||||||
boolean clipped = false;
|
|
||||||
if (childNeedsClipping(child)) {
|
if (childNeedsClipping(child)) {
|
||||||
Path clipPath = getCustomClipPath(child);
|
Path clipPath = getCustomClipPath(child);
|
||||||
if (clipPath == null) {
|
if (clipPath == null) {
|
||||||
clipPath = getClipPath(false /* ignoreTranslation */);
|
clipPath = getClipPath(false /* ignoreTranslation */);
|
||||||
}
|
}
|
||||||
if (clipPath != null) {
|
if (clipPath != null) {
|
||||||
if (intersectPath != null) {
|
|
||||||
clipPath.op(intersectPath, Path.Op.INTERSECT);
|
|
||||||
}
|
|
||||||
canvas.clipPath(clipPath);
|
canvas.clipPath(clipPath);
|
||||||
clipped = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!clipped && intersectPath != null) {
|
|
||||||
canvas.clipPath(intersectPath);
|
|
||||||
}
|
|
||||||
boolean result = super.drawChild(canvas, child, drawingTime);
|
boolean result = super.drawChild(canvas, child, drawingTime);
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
return result;
|
return result;
|
||||||
@@ -212,24 +191,10 @@ public abstract class ExpandableOutlineView extends ExpandableView {
|
|||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setDistanceToTopRoundness(float distanceToTopRoundness) {
|
|
||||||
super.setDistanceToTopRoundness(distanceToTopRoundness);
|
|
||||||
if (distanceToTopRoundness != mDistanceToTopRoundness) {
|
|
||||||
mTopAmountRounded = distanceToTopRoundness >= 0;
|
|
||||||
mDistanceToTopRoundness = distanceToTopRoundness;
|
|
||||||
applyRoundness();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean childNeedsClipping(View child) {
|
protected boolean childNeedsClipping(View child) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean topAmountNeedsClipping() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean isClippingNeeded() {
|
protected boolean isClippingNeeded() {
|
||||||
return mAlwaysRoundBothCorners || mCustomOutline || getTranslation() != 0 ;
|
return mAlwaysRoundBothCorners || mCustomOutline || getTranslation() != 0 ;
|
||||||
}
|
}
|
||||||
@@ -272,11 +237,6 @@ public abstract class ExpandableOutlineView extends ExpandableView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public float getCurrentBackgroundRadiusTop() {
|
public float getCurrentBackgroundRadiusTop() {
|
||||||
// If this view is top amount notification view, it should always has round corners on top.
|
|
||||||
// It will be applied with applyRoundness()
|
|
||||||
if (mTopAmountRounded) {
|
|
||||||
return mOutlineRadius;
|
|
||||||
}
|
|
||||||
return getCurrentTopRoundness() * mOutlineRadius;
|
return getCurrentTopRoundness() * mOutlineRadius;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ import java.util.List;
|
|||||||
public abstract class ExpandableView extends FrameLayout implements Dumpable {
|
public abstract class ExpandableView extends FrameLayout implements Dumpable {
|
||||||
private static final String TAG = "ExpandableView";
|
private static final String TAG = "ExpandableView";
|
||||||
|
|
||||||
public static final float NO_ROUNDNESS = -1;
|
|
||||||
protected OnHeightChangedListener mOnHeightChangedListener;
|
protected OnHeightChangedListener mOnHeightChangedListener;
|
||||||
private int mActualHeight;
|
private int mActualHeight;
|
||||||
protected int mClipTopAmount;
|
protected int mClipTopAmount;
|
||||||
@@ -192,14 +191,6 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the distance to the top roundness, from where we should start clipping a value above
|
|
||||||
* or equal to 0 is the effective distance, and if a value below 0 is received, there should
|
|
||||||
* be no clipping.
|
|
||||||
*/
|
|
||||||
public void setDistanceToTopRoundness(float distanceToTopRoundness) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setActualHeight(int actualHeight) {
|
public void setActualHeight(int actualHeight) {
|
||||||
setActualHeight(actualHeight, true /* notifyListeners */);
|
setActualHeight(actualHeight, true /* notifyListeners */);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,10 +42,8 @@ public class NotificationBackgroundView extends View {
|
|||||||
private int mActualHeight;
|
private int mActualHeight;
|
||||||
private int mClipBottomAmount;
|
private int mClipBottomAmount;
|
||||||
private int mTintColor;
|
private int mTintColor;
|
||||||
private float[] mCornerRadii = new float[8];
|
private final float[] mCornerRadii = new float[8];
|
||||||
private boolean mBottomIsRounded;
|
private boolean mBottomIsRounded;
|
||||||
private boolean mLastInSection;
|
|
||||||
private boolean mFirstInSection;
|
|
||||||
private int mBackgroundTop;
|
private int mBackgroundTop;
|
||||||
private boolean mBottomAmountClips = true;
|
private boolean mBottomAmountClips = true;
|
||||||
private boolean mExpandAnimationRunning;
|
private boolean mExpandAnimationRunning;
|
||||||
@@ -53,9 +51,6 @@ public class NotificationBackgroundView extends View {
|
|||||||
private int mDrawableAlpha = 255;
|
private int mDrawableAlpha = 255;
|
||||||
private boolean mIsPressedAllowed;
|
private boolean mIsPressedAllowed;
|
||||||
|
|
||||||
private boolean mTopAmountRounded;
|
|
||||||
private float mDistanceToTopRoundness;
|
|
||||||
|
|
||||||
public NotificationBackgroundView(Context context, AttributeSet attrs) {
|
public NotificationBackgroundView(Context context, AttributeSet attrs) {
|
||||||
super(context, attrs);
|
super(context, attrs);
|
||||||
mDontModifyCorners = getResources().getBoolean(
|
mDontModifyCorners = getResources().getBoolean(
|
||||||
@@ -90,15 +85,6 @@ public class NotificationBackgroundView extends View {
|
|||||||
left = (int) ((getWidth() - mActualWidth) / 2.0f);
|
left = (int) ((getWidth() - mActualWidth) / 2.0f);
|
||||||
right = (int) (left + mActualWidth);
|
right = (int) (left + mActualWidth);
|
||||||
}
|
}
|
||||||
if (mTopAmountRounded) {
|
|
||||||
int clipTop = (int) (mClipTopAmount - mDistanceToTopRoundness);
|
|
||||||
if (clipTop >= 0 || !mFirstInSection) {
|
|
||||||
top += clipTop;
|
|
||||||
}
|
|
||||||
if (clipTop >= 0 && !mLastInSection) {
|
|
||||||
bottom += clipTop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
drawable.setBounds(left, top, right, bottom);
|
drawable.setBounds(left, top, right, bottom);
|
||||||
drawable.draw(canvas);
|
drawable.draw(canvas);
|
||||||
}
|
}
|
||||||
@@ -180,14 +166,6 @@ public class NotificationBackgroundView extends View {
|
|||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDistanceToTopRoundness(float distanceToTopRoundness) {
|
|
||||||
if (distanceToTopRoundness != mDistanceToTopRoundness) {
|
|
||||||
mTopAmountRounded = distanceToTopRoundness >= 0;
|
|
||||||
mDistanceToTopRoundness = distanceToTopRoundness;
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasOverlappingRendering() {
|
public boolean hasOverlappingRendering() {
|
||||||
|
|
||||||
@@ -246,18 +224,6 @@ public class NotificationBackgroundView extends View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets whether this background belongs to the last notification in a section. */
|
|
||||||
public void setLastInSection(boolean lastInSection) {
|
|
||||||
mLastInSection = lastInSection;
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Sets whether this background belongs to the first notification in a section. */
|
|
||||||
public void setFirstInSection(boolean firstInSection) {
|
|
||||||
mFirstInSection = firstInSection;
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateBackgroundRadii() {
|
private void updateBackgroundRadii() {
|
||||||
if (mDontModifyCorners) {
|
if (mDontModifyCorners) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -75,7 +75,6 @@ public class AmbientState {
|
|||||||
private int mExpandAnimationTopChange;
|
private int mExpandAnimationTopChange;
|
||||||
private ExpandableNotificationRow mExpandingNotification;
|
private ExpandableNotificationRow mExpandingNotification;
|
||||||
private float mHideAmount;
|
private float mHideAmount;
|
||||||
private float mNotificationScrimTop;
|
|
||||||
private boolean mAppearing;
|
private boolean mAppearing;
|
||||||
private float mPulseHeight = MAX_PULSE_HEIGHT;
|
private float mPulseHeight = MAX_PULSE_HEIGHT;
|
||||||
private float mDozeAmount = 0.0f;
|
private float mDozeAmount = 0.0f;
|
||||||
@@ -256,20 +255,6 @@ public class AmbientState {
|
|||||||
return mHideAmount;
|
return mHideAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set y position of top of notifications background scrim, relative to top of screen.
|
|
||||||
*/
|
|
||||||
public void setNotificationScrimTop(float notificationScrimTop) {
|
|
||||||
mNotificationScrimTop = notificationScrimTop;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Y position of top of notifications background scrim, relative to top of screen.
|
|
||||||
*/
|
|
||||||
public float getNotificationScrimTop() {
|
|
||||||
return mNotificationScrimTop;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setHideSensitive(boolean hideSensitive) {
|
public void setHideSensitive(boolean hideSensitive) {
|
||||||
mHideSensitive = hideSensitive;
|
mHideSensitive = hideSensitive;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import android.graphics.Canvas;
|
|||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Outline;
|
import android.graphics.Outline;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Path;
|
||||||
import android.graphics.PointF;
|
import android.graphics.PointF;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@@ -453,6 +454,27 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
|
|||||||
private NotificationStackScrollLayoutController mController;
|
private NotificationStackScrollLayoutController mController;
|
||||||
|
|
||||||
private boolean mKeyguardMediaControllorVisible;
|
private boolean mKeyguardMediaControllorVisible;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The clip path used to clip the view in a rounded way.
|
||||||
|
*/
|
||||||
|
private final Path mRoundedClipPath = new Path();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should we use rounded rect clipping right now
|
||||||
|
*/
|
||||||
|
private boolean mShouldUseRoundedRectClipping = false;
|
||||||
|
|
||||||
|
private int mRoundedRectClippingLeft;
|
||||||
|
private int mRoundedRectClippingTop;
|
||||||
|
private int mRoundedRectClippingBottom;
|
||||||
|
private int mRoundedRectClippingRight;
|
||||||
|
private float[] mBgCornerRadii = new float[8];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Are we launching a notification right now
|
||||||
|
*/
|
||||||
|
private boolean mLaunchingNotification;
|
||||||
private NotificationEntry mTopHeadsUpEntry;
|
private NotificationEntry mTopHeadsUpEntry;
|
||||||
private long mNumHeadsUp;
|
private long mNumHeadsUp;
|
||||||
private NotificationStackScrollLayoutController.TouchHandler mTouchHandler;
|
private NotificationStackScrollLayoutController.TouchHandler mTouchHandler;
|
||||||
@@ -1017,33 +1039,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
|
|||||||
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
|
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
|
||||||
private void onPreDrawDuringAnimation() {
|
private void onPreDrawDuringAnimation() {
|
||||||
mShelf.updateAppearance();
|
mShelf.updateAppearance();
|
||||||
updateClippingToTopRoundedCorner();
|
|
||||||
if (!mNeedsAnimation && !mChildrenUpdateRequested) {
|
if (!mNeedsAnimation && !mChildrenUpdateRequested) {
|
||||||
updateBackground();
|
updateBackground();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
|
|
||||||
private void updateClippingToTopRoundedCorner() {
|
|
||||||
Float clipStart = mAmbientState.getNotificationScrimTop();
|
|
||||||
Float clipEnd = clipStart + mCornerRadius;
|
|
||||||
boolean first = true;
|
|
||||||
for (int i = 0; i < getChildCount(); i++) {
|
|
||||||
ExpandableView child = (ExpandableView) getChildAt(i);
|
|
||||||
if (child.getVisibility() == GONE) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
float start = child.getTranslationY();
|
|
||||||
float end = start + child.getActualHeight();
|
|
||||||
boolean clip = clipStart > start && clipStart < end
|
|
||||||
|| clipEnd >= start && clipEnd <= end;
|
|
||||||
clip &= !(first && mScrollAdapter.isScrolledToTop());
|
|
||||||
child.setDistanceToTopRoundness(clip ? Math.max(start - clipStart, 0)
|
|
||||||
: ExpandableView.NO_ROUNDNESS);
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
|
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
|
||||||
private void updateScrollStateForAddedChildren() {
|
private void updateScrollStateForAddedChildren() {
|
||||||
if (mChildrenToAddAnimated.isEmpty()) {
|
if (mChildrenToAddAnimated.isEmpty()) {
|
||||||
@@ -1601,6 +1601,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
|
|||||||
super.onConfigurationChanged(newConfig);
|
super.onConfigurationChanged(newConfig);
|
||||||
Resources res = getResources();
|
Resources res = getResources();
|
||||||
mShouldUseSplitNotificationShade = shouldUseSplitNotificationShade(mFeatureFlags, res);
|
mShouldUseSplitNotificationShade = shouldUseSplitNotificationShade(mFeatureFlags, res);
|
||||||
|
updateUseRoundedRectClipping();
|
||||||
mStatusBarHeight = res.getDimensionPixelOffset(R.dimen.status_bar_height);
|
mStatusBarHeight = res.getDimensionPixelOffset(R.dimen.status_bar_height);
|
||||||
float densityScale = res.getDisplayMetrics().density;
|
float densityScale = res.getDisplayMetrics().density;
|
||||||
mSwipeHelper.setDensityScale(densityScale);
|
mSwipeHelper.setDensityScale(densityScale);
|
||||||
@@ -2818,6 +2819,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
|
|||||||
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
|
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
|
||||||
public void applyExpandAnimationParams(ExpandAnimationParameters params) {
|
public void applyExpandAnimationParams(ExpandAnimationParameters params) {
|
||||||
mAmbientState.setExpandAnimationTopChange(params == null ? 0 : params.getTopChange());
|
mAmbientState.setExpandAnimationTopChange(params == null ? 0 : params.getTopChange());
|
||||||
|
|
||||||
|
// Disable clipping for launches
|
||||||
|
setLaunchingNotification(params != null);
|
||||||
requestChildrenUpdate();
|
requestChildrenUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2901,7 +2905,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
|
|||||||
mAnimationEvents.clear();
|
mAnimationEvents.clear();
|
||||||
updateBackground();
|
updateBackground();
|
||||||
updateViewShadows();
|
updateViewShadows();
|
||||||
updateClippingToTopRoundedCorner();
|
|
||||||
} else {
|
} else {
|
||||||
applyCurrentState();
|
applyCurrentState();
|
||||||
}
|
}
|
||||||
@@ -3795,6 +3798,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
|
|||||||
updateNotificationAnimationStates();
|
updateNotificationAnimationStates();
|
||||||
updateChronometers();
|
updateChronometers();
|
||||||
requestChildrenUpdate();
|
requestChildrenUpdate();
|
||||||
|
updateUseRoundedRectClipping();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4015,7 +4019,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
|
|||||||
setAnimationRunning(false);
|
setAnimationRunning(false);
|
||||||
updateBackground();
|
updateBackground();
|
||||||
updateViewShadows();
|
updateViewShadows();
|
||||||
updateClippingToTopRoundedCorner();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
|
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
|
||||||
@@ -4550,6 +4553,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
|
|||||||
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
|
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
|
||||||
public void setQsExpansionFraction(float qsExpansionFraction) {
|
public void setQsExpansionFraction(float qsExpansionFraction) {
|
||||||
mQsExpansionFraction = qsExpansionFraction;
|
mQsExpansionFraction = qsExpansionFraction;
|
||||||
|
updateUseRoundedRectClipping();
|
||||||
|
|
||||||
// If notifications are scrolled,
|
// If notifications are scrolled,
|
||||||
// clear out scrollY by the time we push notifications offscreen
|
// clear out scrollY by the time we push notifications offscreen
|
||||||
@@ -5191,6 +5195,69 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
|
|||||||
mScrollListener = listener;
|
mScrollListener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set rounded rect clipping bounds on this view.
|
||||||
|
*/
|
||||||
|
public void setRoundedClippingBounds(int left, int top, int right, int bottom, int topRadius,
|
||||||
|
int bottomRadius) {
|
||||||
|
if (mRoundedRectClippingLeft == left && mRoundedRectClippingRight == right
|
||||||
|
&& mRoundedRectClippingBottom == bottom && mRoundedRectClippingTop == top
|
||||||
|
&& mBgCornerRadii[0] == topRadius && mBgCornerRadii[5] == bottomRadius) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mRoundedRectClippingLeft = left;
|
||||||
|
mRoundedRectClippingTop = top;
|
||||||
|
mRoundedRectClippingBottom = bottom;
|
||||||
|
mRoundedRectClippingRight = right;
|
||||||
|
mBgCornerRadii[0] = topRadius;
|
||||||
|
mBgCornerRadii[1] = topRadius;
|
||||||
|
mBgCornerRadii[2] = topRadius;
|
||||||
|
mBgCornerRadii[3] = topRadius;
|
||||||
|
mBgCornerRadii[4] = bottomRadius;
|
||||||
|
mBgCornerRadii[5] = bottomRadius;
|
||||||
|
mBgCornerRadii[6] = bottomRadius;
|
||||||
|
mBgCornerRadii[7] = bottomRadius;
|
||||||
|
mRoundedClipPath.reset();
|
||||||
|
mRoundedClipPath.addRoundRect(left, top, right, bottom, mBgCornerRadii, Path.Direction.CW);
|
||||||
|
if (mShouldUseRoundedRectClipping) {
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set if we're launching a notification right now.
|
||||||
|
*/
|
||||||
|
private void setLaunchingNotification(boolean launching) {
|
||||||
|
if (launching == mLaunchingNotification) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mLaunchingNotification = launching;
|
||||||
|
updateUseRoundedRectClipping();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should we use rounded rect clipping
|
||||||
|
*/
|
||||||
|
private void updateUseRoundedRectClipping() {
|
||||||
|
// We don't want to clip notifications when QS is expanded, because incoming heads up on
|
||||||
|
// the bottom would be clipped otherwise
|
||||||
|
boolean qsAllowsClipping = mQsExpansionFraction < 0.5f || mShouldUseSplitNotificationShade;
|
||||||
|
boolean clip = !mLaunchingNotification && mIsExpanded && qsAllowsClipping;
|
||||||
|
if (clip != mShouldUseRoundedRectClipping) {
|
||||||
|
mShouldUseRoundedRectClipping = clip;
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void dispatchDraw(Canvas canvas) {
|
||||||
|
if (mShouldUseRoundedRectClipping) {
|
||||||
|
// Let's clip rounded.
|
||||||
|
canvas.clipPath(mRoundedClipPath);
|
||||||
|
}
|
||||||
|
super.dispatchDraw(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A listener that is notified when the empty space below the notifications is clicked on
|
* A listener that is notified when the empty space below the notifications is clicked on
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -822,8 +822,18 @@ public class NotificationStackScrollLayoutController {
|
|||||||
return mView.isLayoutRtl();
|
return mView.isLayoutRtl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the left of the view.
|
||||||
|
*/
|
||||||
public int getLeft() {
|
public int getLeft() {
|
||||||
return mView.getLeft();
|
return mView.getLeft();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the top of the view.
|
||||||
|
*/
|
||||||
|
public int getTop() {
|
||||||
|
return mView.getTop();
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getTranslationX() {
|
public float getTranslationX() {
|
||||||
@@ -1444,6 +1454,14 @@ public class NotificationStackScrollLayoutController {
|
|||||||
mView.setOnScrollListener(listener);
|
mView.setOnScrollListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set rounded rect clipping bounds on this view.
|
||||||
|
*/
|
||||||
|
public void setRoundedClippingBounds(int left, int top, int right, int bottom, int topRadius,
|
||||||
|
int bottomRadius) {
|
||||||
|
mView.setRoundedClippingBounds(left, top, right, bottom, topRadius, bottomRadius);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enum for UiEvent logged from this class
|
* Enum for UiEvent logged from this class
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ public class StackScrollAlgorithm {
|
|||||||
AmbientState ambientState) {
|
AmbientState ambientState) {
|
||||||
float drawStart = ambientState.isOnKeyguard() ? 0
|
float drawStart = ambientState.isOnKeyguard() ? 0
|
||||||
: ambientState.getStackY() - ambientState.getScrollY();
|
: ambientState.getStackY() - ambientState.getScrollY();
|
||||||
float clipStart = ambientState.getNotificationScrimTop();
|
float clipStart = 0;
|
||||||
int childCount = algorithmState.visibleChildren.size();
|
int childCount = algorithmState.visibleChildren.size();
|
||||||
boolean firstHeadsUp = true;
|
boolean firstHeadsUp = true;
|
||||||
for (int i = 0; i < childCount; i++) {
|
for (int i = 0; i < childCount; i++) {
|
||||||
|
|||||||
@@ -2241,29 +2241,21 @@ public class NotificationPanelViewController extends PanelViewController {
|
|||||||
* and QS state.
|
* and QS state.
|
||||||
*/
|
*/
|
||||||
private void setQSClippingBounds() {
|
private void setQSClippingBounds() {
|
||||||
int top = 0;
|
int top;
|
||||||
int bottom = 0;
|
int bottom;
|
||||||
int left = 0;
|
int left;
|
||||||
int right = 0;
|
int right;
|
||||||
|
|
||||||
final int qsPanelBottomY = calculateQsBottomPosition(computeQsExpansionFraction());
|
final int qsPanelBottomY = calculateQsBottomPosition(computeQsExpansionFraction());
|
||||||
final boolean quickSettingsVisible = computeQsExpansionFraction() > 0 || qsPanelBottomY > 0;
|
final boolean qsVisible = (computeQsExpansionFraction() > 0 || qsPanelBottomY > 0);
|
||||||
|
|
||||||
if (mShouldUseSplitNotificationShade && quickSettingsVisible) {
|
if (!mShouldUseSplitNotificationShade) {
|
||||||
mAmbientState.setNotificationScrimTop(mSplitShadeNotificationsTopPadding);
|
|
||||||
|
|
||||||
top = Math.max(0, Math.min(qsPanelBottomY, mSplitShadeNotificationsTopPadding));
|
|
||||||
bottom = mNotificationStackScrollLayoutController.getHeight();
|
|
||||||
left = mNotificationStackScrollLayoutController.getLeft();
|
|
||||||
right = mNotificationStackScrollLayoutController.getRight();
|
|
||||||
} else {
|
|
||||||
if (mTransitioningToFullShadeProgress > 0.0f) {
|
if (mTransitioningToFullShadeProgress > 0.0f) {
|
||||||
// If we're transitioning, let's use the actual value. The else case
|
// If we're transitioning, let's use the actual value. The else case
|
||||||
// can be wrong during transitions when waiting for the keyguard to unlock
|
// can be wrong during transitions when waiting for the keyguard to unlock
|
||||||
top = mTransitionToFullShadeQSPosition;
|
top = mTransitionToFullShadeQSPosition;
|
||||||
} else {
|
} else {
|
||||||
final float notificationTop = getQSEdgePosition();
|
final float notificationTop = getQSEdgePosition();
|
||||||
mAmbientState.setNotificationScrimTop(notificationTop);
|
|
||||||
top = (int) (isOnKeyguard() ? Math.min(qsPanelBottomY, notificationTop)
|
top = (int) (isOnKeyguard() ? Math.min(qsPanelBottomY, notificationTop)
|
||||||
: notificationTop);
|
: notificationTop);
|
||||||
}
|
}
|
||||||
@@ -2271,21 +2263,25 @@ public class NotificationPanelViewController extends PanelViewController {
|
|||||||
// notification bounds should take full screen width regardless of insets
|
// notification bounds should take full screen width regardless of insets
|
||||||
left = 0;
|
left = 0;
|
||||||
right = getView().getRight() + mDisplayRightInset;
|
right = getView().getRight() + mDisplayRightInset;
|
||||||
|
} else {
|
||||||
|
top = Math.min(qsPanelBottomY, mSplitShadeNotificationsTopPadding);
|
||||||
|
bottom = mNotificationStackScrollLayoutController.getHeight();
|
||||||
|
left = mNotificationStackScrollLayoutController.getLeft();
|
||||||
|
right = mNotificationStackScrollLayoutController.getRight();
|
||||||
}
|
}
|
||||||
// top should never be lower than bottom, otherwise it will be invisible.
|
// top should never be lower than bottom, otherwise it will be invisible.
|
||||||
top = Math.min(top, bottom);
|
top = Math.min(top, bottom);
|
||||||
applyQSClippingBounds(left, top, right, bottom,
|
applyQSClippingBounds(left, top, right, bottom, qsVisible);
|
||||||
quickSettingsVisible && !mShouldUseSplitNotificationShade);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyQSClippingBounds(int left, int top, int right, int bottom,
|
private void applyQSClippingBounds(int left, int top, int right, int bottom,
|
||||||
boolean visible) {
|
boolean qsVisible) {
|
||||||
if (!mAnimateNextNotificationBounds || mKeyguardStatusAreaClipBounds.isEmpty()) {
|
if (!mAnimateNextNotificationBounds || mKeyguardStatusAreaClipBounds.isEmpty()) {
|
||||||
if (mQsClippingAnimation != null) {
|
if (mQsClippingAnimation != null) {
|
||||||
// update the end position of the animator
|
// update the end position of the animator
|
||||||
mQsClippingAnimationEndBounds.set(left, top, right, bottom);
|
mQsClippingAnimationEndBounds.set(left, top, right, bottom);
|
||||||
} else {
|
} else {
|
||||||
applyQSClippingImmediately(left, top, right, bottom, visible);
|
applyQSClippingImmediately(left, top, right, bottom, qsVisible);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mQsClippingAnimationEndBounds.set(left, top, right, bottom);
|
mQsClippingAnimationEndBounds.set(left, top, right, bottom);
|
||||||
@@ -2309,7 +2305,7 @@ public class NotificationPanelViewController extends PanelViewController {
|
|||||||
int animBottom = (int) MathUtils.lerp(startBottom,
|
int animBottom = (int) MathUtils.lerp(startBottom,
|
||||||
mQsClippingAnimationEndBounds.bottom, fraction);
|
mQsClippingAnimationEndBounds.bottom, fraction);
|
||||||
applyQSClippingImmediately(animLeft, animTop, animRight, animBottom,
|
applyQSClippingImmediately(animLeft, animTop, animRight, animBottom,
|
||||||
visible /* visible */);
|
qsVisible /* qsVisible */);
|
||||||
});
|
});
|
||||||
mQsClippingAnimation.addListener(new AnimatorListenerAdapter() {
|
mQsClippingAnimation.addListener(new AnimatorListenerAdapter() {
|
||||||
@Override
|
@Override
|
||||||
@@ -2324,7 +2320,7 @@ public class NotificationPanelViewController extends PanelViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void applyQSClippingImmediately(int left, int top, int right, int bottom,
|
private void applyQSClippingImmediately(int left, int top, int right, int bottom,
|
||||||
boolean visible) {
|
boolean qsVisible) {
|
||||||
// Fancy clipping for quick settings
|
// Fancy clipping for quick settings
|
||||||
int radius = mScrimCornerRadius;
|
int radius = mScrimCornerRadius;
|
||||||
int statusBarClipTop = 0;
|
int statusBarClipTop = 0;
|
||||||
@@ -2332,19 +2328,34 @@ public class NotificationPanelViewController extends PanelViewController {
|
|||||||
if (!mShouldUseSplitNotificationShade) {
|
if (!mShouldUseSplitNotificationShade) {
|
||||||
// The padding on this area is large enough that we can use a cheaper clipping strategy
|
// The padding on this area is large enough that we can use a cheaper clipping strategy
|
||||||
mKeyguardStatusAreaClipBounds.set(left, top, right, bottom);
|
mKeyguardStatusAreaClipBounds.set(left, top, right, bottom);
|
||||||
clipStatusView = visible;
|
clipStatusView = qsVisible;
|
||||||
radius = (int) MathUtils.lerp(mScreenCornerRadius, mScrimCornerRadius,
|
radius = (int) MathUtils.lerp(mScreenCornerRadius, mScrimCornerRadius,
|
||||||
Math.min(top / (float) mScrimCornerRadius, 1f));
|
Math.min(top / (float) mScrimCornerRadius, 1f));
|
||||||
statusBarClipTop = top - mKeyguardStatusBar.getTop();
|
statusBarClipTop = top - mKeyguardStatusBar.getTop();
|
||||||
}
|
}
|
||||||
if (mQs != null) {
|
if (mQs != null) {
|
||||||
mQs.setFancyClipping(top, bottom, radius, visible);
|
mQs.setFancyClipping(top, bottom, radius, qsVisible
|
||||||
|
&& !mShouldUseSplitNotificationShade);
|
||||||
}
|
}
|
||||||
mKeyguardStatusViewController.setClipBounds(
|
mKeyguardStatusViewController.setClipBounds(
|
||||||
clipStatusView ? mKeyguardStatusAreaClipBounds : null);
|
clipStatusView ? mKeyguardStatusAreaClipBounds : null);
|
||||||
mScrimController.setNotificationsBounds(left, top, right, bottom);
|
if (!qsVisible && mShouldUseSplitNotificationShade) {
|
||||||
|
// On the lockscreen when qs isn't visible, we don't want the bounds of the shade to
|
||||||
|
// be visible, otherwise you can see the bounds once swiping up to see bouncer
|
||||||
|
mScrimController.setNotificationsBounds(0, 0, 0, 0);
|
||||||
|
} else {
|
||||||
|
mScrimController.setNotificationsBounds(left, top, right, bottom);
|
||||||
|
}
|
||||||
|
|
||||||
mScrimController.setScrimCornerRadius(radius);
|
mScrimController.setScrimCornerRadius(radius);
|
||||||
mKeyguardStatusBar.setTopClipping(statusBarClipTop);
|
mKeyguardStatusBar.setTopClipping(statusBarClipTop);
|
||||||
|
int nsslLeft = left - mNotificationStackScrollLayoutController.getLeft();
|
||||||
|
int nsslRight = right - mNotificationStackScrollLayoutController.getLeft();
|
||||||
|
int nsslTop = top - mNotificationStackScrollLayoutController.getTop();
|
||||||
|
int nsslBottom = bottom - mNotificationStackScrollLayoutController.getTop();
|
||||||
|
int bottomRadius = mShouldUseSplitNotificationShade ? radius : 0;
|
||||||
|
mNotificationStackScrollLayoutController.setRoundedClippingBounds(
|
||||||
|
nsslLeft, nsslTop, nsslRight, nsslBottom, radius, bottomRadius);
|
||||||
}
|
}
|
||||||
|
|
||||||
private float getQSEdgePosition() {
|
private float getQSEdgePosition() {
|
||||||
|
|||||||
@@ -87,7 +87,6 @@ import com.android.systemui.media.MediaDataManager;
|
|||||||
import com.android.systemui.media.MediaHierarchyManager;
|
import com.android.systemui.media.MediaHierarchyManager;
|
||||||
import com.android.systemui.navigationbar.NavigationModeController;
|
import com.android.systemui.navigationbar.NavigationModeController;
|
||||||
import com.android.systemui.plugins.FalsingManager;
|
import com.android.systemui.plugins.FalsingManager;
|
||||||
import com.android.systemui.plugins.qs.QS;
|
|
||||||
import com.android.systemui.qs.QSDetailDisplayer;
|
import com.android.systemui.qs.QSDetailDisplayer;
|
||||||
import com.android.systemui.statusbar.CommandQueue;
|
import com.android.systemui.statusbar.CommandQueue;
|
||||||
import com.android.systemui.statusbar.FeatureFlags;
|
import com.android.systemui.statusbar.FeatureFlags;
|
||||||
@@ -678,21 +677,6 @@ public class NotificationPanelViewTest extends SysuiTestCase {
|
|||||||
verify(mTapAgainViewController).show();
|
verify(mTapAgainViewController).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNotificationClipping_isAlignedWithNotificationScrimInSplitShade() {
|
|
||||||
mStatusBarStateController.setState(SHADE);
|
|
||||||
QS qs = mock(QS.class);
|
|
||||||
when(qs.getHeader()).thenReturn(mock(View.class));
|
|
||||||
mNotificationPanelViewController.mQs = qs;
|
|
||||||
enableSplitShade();
|
|
||||||
|
|
||||||
// hacky way to refresh notification scrim top with non-zero qsPanelBottom value
|
|
||||||
mNotificationPanelViewController.setTransitionToFullShadeAmount(200, false, 0);
|
|
||||||
|
|
||||||
verify(mAmbientState)
|
|
||||||
.setNotificationScrimTop(NOTIFICATION_SCRIM_TOP_PADDING_IN_SPLIT_SHADE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSwitchesToCorrectClockInSinglePaneShade() {
|
public void testSwitchesToCorrectClockInSinglePaneShade() {
|
||||||
mStatusBarStateController.setState(KEYGUARD);
|
mStatusBarStateController.setState(KEYGUARD);
|
||||||
|
|||||||
Reference in New Issue
Block a user