Merge "Fixed an issue where the notification clipping flickered" into sc-dev am: 1a10b25d48
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15027057 Change-Id: Iae8ba2c18e328e366744f916a2f5bf100ef2a95a
This commit is contained in:
@@ -22,11 +22,27 @@ class ExpandAnimationParameters(
|
||||
)
|
||||
|
||||
var startTranslationZ = 0f
|
||||
|
||||
/**
|
||||
* The top position of the notification at the start of the animation. This is needed in order
|
||||
* to keep the notification at its place when launching a notification that is clipped rounded.
|
||||
*/
|
||||
var startNotificationTop = 0f
|
||||
var startClipTopAmount = 0
|
||||
var parentStartClipTopAmount = 0
|
||||
var progress = 0f
|
||||
var linearProgress = 0f
|
||||
|
||||
/**
|
||||
* The rounded top clipping at the beginning.
|
||||
*/
|
||||
var startRoundedTopClipping = 0
|
||||
|
||||
/**
|
||||
* The rounded top clipping of the parent notification at the start.
|
||||
*/
|
||||
var parentStartRoundedTopClipping = 0
|
||||
|
||||
override val topChange: Int
|
||||
get() {
|
||||
// We need this compensation to ensure that the QS moves in sync.
|
||||
|
||||
@@ -40,6 +40,11 @@ class NotificationLaunchAnimatorController(
|
||||
private val headsUpManager: HeadsUpManagerPhone,
|
||||
private val notification: ExpandableNotificationRow
|
||||
) : ActivityLaunchAnimator.Controller {
|
||||
|
||||
companion object {
|
||||
const val ANIMATION_DURATION_TOP_ROUNDING = 100L
|
||||
}
|
||||
|
||||
private val notificationEntry = notification.entry
|
||||
private val notificationKey = notificationEntry.sbn.key
|
||||
|
||||
@@ -54,18 +59,37 @@ class NotificationLaunchAnimatorController(
|
||||
val height = max(0, notification.actualHeight - notification.clipBottomAmount)
|
||||
val location = notification.locationOnScreen
|
||||
|
||||
val clipStartLocation = notificationListContainer.getTopClippingStartLocation()
|
||||
val roundedTopClipping = Math.max(clipStartLocation - location[1], 0)
|
||||
val windowTop = location[1] + roundedTopClipping
|
||||
val topCornerRadius = if (roundedTopClipping > 0) {
|
||||
// Because the rounded Rect clipping is complex, we start the top rounding at
|
||||
// 0, which is pretty close to matching the real clipping.
|
||||
// We'd have to clipOut the overlaid drawable too with the outer rounded rect in case
|
||||
// if we'd like to have this perfect, but this is close enough.
|
||||
0f
|
||||
} else {
|
||||
notification.currentBackgroundRadiusTop
|
||||
}
|
||||
val params = ExpandAnimationParameters(
|
||||
top = location[1],
|
||||
top = windowTop,
|
||||
bottom = location[1] + height,
|
||||
left = location[0],
|
||||
right = location[0] + notification.width,
|
||||
topCornerRadius = notification.currentBackgroundRadiusTop,
|
||||
topCornerRadius = topCornerRadius,
|
||||
bottomCornerRadius = notification.currentBackgroundRadiusBottom
|
||||
)
|
||||
|
||||
params.startTranslationZ = notification.translationZ
|
||||
params.startNotificationTop = notification.translationY
|
||||
params.startRoundedTopClipping = roundedTopClipping
|
||||
params.startClipTopAmount = notification.clipTopAmount
|
||||
if (notification.isChildInGroup) {
|
||||
params.startNotificationTop += notification.notificationParent.translationY
|
||||
val parentRoundedClip = Math.max(clipStartLocation
|
||||
- notification.notificationParent.locationOnScreen[1], 0)
|
||||
params.parentStartRoundedTopClipping = parentRoundedClip
|
||||
|
||||
val parentClip = notification.notificationParent.clipTopAmount
|
||||
params.parentStartClipTopAmount = parentClip
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.drawable.AnimatedVectorDrawable;
|
||||
import android.graphics.drawable.AnimationDrawable;
|
||||
@@ -85,6 +86,7 @@ import com.android.systemui.statusbar.RemoteInputController;
|
||||
import com.android.systemui.statusbar.StatusBarIconView;
|
||||
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
|
||||
import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
|
||||
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorController;
|
||||
import com.android.systemui.statusbar.notification.NotificationUtils;
|
||||
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
|
||||
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
|
||||
@@ -252,6 +254,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
||||
private OnExpandClickListener mOnExpandClickListener;
|
||||
private View.OnClickListener mOnAppClickListener;
|
||||
private View.OnClickListener mOnFeedbackClickListener;
|
||||
private Path mExpandingClipPath;
|
||||
|
||||
// Listener will be called when receiving a long click event.
|
||||
// Use #setLongPressPosition to optionally assign positional data with the long press.
|
||||
@@ -836,6 +839,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
||||
public void setIsChildInGroup(boolean isChildInGroup, ExpandableNotificationRow parent) {
|
||||
if (mExpandAnimationRunning && !isChildInGroup && mNotificationParent != null) {
|
||||
mNotificationParent.setChildIsExpanding(false);
|
||||
mNotificationParent.setExpandingClipPath(null);
|
||||
mNotificationParent.setExtraWidthForClipping(0.0f);
|
||||
mNotificationParent.setMinimumHeightForClipping(0);
|
||||
}
|
||||
@@ -2032,7 +2036,22 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
||||
setTranslationZ(translationZ);
|
||||
float extraWidthForClipping = params.getWidth() - getWidth();
|
||||
setExtraWidthForClipping(extraWidthForClipping);
|
||||
int top = params.getTop();
|
||||
int top;
|
||||
if (params.getStartRoundedTopClipping() > 0) {
|
||||
// If we were clipping initially, let's interpolate from the start position to the
|
||||
// top. Otherwise, we just take the top directly.
|
||||
float expandProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
|
||||
params.getProgress(0,
|
||||
NotificationLaunchAnimatorController.ANIMATION_DURATION_TOP_ROUNDING));
|
||||
float startTop = params.getStartNotificationTop();
|
||||
top = (int) Math.min(MathUtils.lerp(startTop,
|
||||
params.getTop(), expandProgress),
|
||||
startTop);
|
||||
} else {
|
||||
top = params.getTop();
|
||||
}
|
||||
int actualHeight = params.getBottom() - top;
|
||||
setActualHeight(actualHeight);
|
||||
int startClipTopAmount = params.getStartClipTopAmount();
|
||||
int clipTopAmount = (int) MathUtils.lerp(startClipTopAmount, 0, params.getProgress());
|
||||
if (mNotificationParent != null) {
|
||||
@@ -2061,13 +2080,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
||||
setClipTopAmount(clipTopAmount);
|
||||
}
|
||||
setTranslationY(top);
|
||||
setActualHeight(params.getHeight());
|
||||
|
||||
mTopRoundnessDuringExpandAnimation = params.getTopCornerRadius() / mOutlineRadius;
|
||||
mBottomRoundnessDuringExpandAnimation = params.getBottomCornerRadius() / mOutlineRadius;
|
||||
invalidateOutline();
|
||||
|
||||
mBackgroundNormal.setExpandAnimationParams(params);
|
||||
mBackgroundNormal.setExpandAnimationSize(params.getWidth(), actualHeight);
|
||||
}
|
||||
|
||||
public void setExpandAnimationRunning(boolean expandAnimationRunning) {
|
||||
@@ -3081,6 +3099,26 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
|
||||
return super.childNeedsClipping(child);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a clip path to be set while expanding the notification. This is needed to nicely
|
||||
* clip ourselves during the launch if we were clipped rounded in the beginning
|
||||
*/
|
||||
public void setExpandingClipPath(Path path) {
|
||||
mExpandingClipPath = path;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dispatchDraw(Canvas canvas) {
|
||||
canvas.save();
|
||||
if (mExpandingClipPath != null && (mExpandAnimationRunning || mChildIsExpanding)) {
|
||||
// If we're launching a notification, let's clip if a clip rounded to the clipPath
|
||||
canvas.clipPath(mExpandingClipPath);
|
||||
}
|
||||
super.dispatchDraw(canvas);
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyRoundness() {
|
||||
super.applyRoundness();
|
||||
|
||||
@@ -240,10 +240,10 @@ public class NotificationBackgroundView extends View {
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/** Set the current expand animation parameters. */
|
||||
public void setExpandAnimationParams(ExpandAnimationParameters params) {
|
||||
mActualHeight = params.getHeight();
|
||||
mActualWidth = params.getWidth();
|
||||
/** Set the current expand animation size. */
|
||||
public void setExpandAnimationSize(int actualWidth, int actualHeight) {
|
||||
mActualHeight = actualHeight;
|
||||
mActualWidth = actualWidth;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
||||
@@ -72,8 +72,6 @@ public class AmbientState {
|
||||
private boolean mUnlockHintRunning;
|
||||
private boolean mQsCustomizerShowing;
|
||||
private int mIntrinsicPadding;
|
||||
private int mExpandAnimationTopChange;
|
||||
private ExpandableNotificationRow mExpandingNotification;
|
||||
private float mHideAmount;
|
||||
private boolean mAppearing;
|
||||
private float mPulseHeight = MAX_PULSE_HEIGHT;
|
||||
@@ -518,22 +516,6 @@ public class AmbientState {
|
||||
return isDozing() && !isPulsing(row.getEntry());
|
||||
}
|
||||
|
||||
public void setExpandAnimationTopChange(int expandAnimationTopChange) {
|
||||
mExpandAnimationTopChange = expandAnimationTopChange;
|
||||
}
|
||||
|
||||
public void setExpandingNotification(ExpandableNotificationRow row) {
|
||||
mExpandingNotification = row;
|
||||
}
|
||||
|
||||
public ExpandableNotificationRow getExpandingNotification() {
|
||||
return mExpandingNotification;
|
||||
}
|
||||
|
||||
public int getExpandAnimationTopChange() {
|
||||
return mExpandAnimationTopChange;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true } when shade is completely hidden: in AOD, ambient display or when
|
||||
* bypassing.
|
||||
|
||||
@@ -200,4 +200,11 @@ public interface NotificationListContainer extends
|
||||
default void setWillExpand(boolean willExpand) {}
|
||||
|
||||
void setNotificationActivityStarter(NotificationActivityStarter notificationActivityStarter);
|
||||
|
||||
/**
|
||||
* @return the start location where we start clipping notifications.
|
||||
*/
|
||||
default int getTopClippingStartLocation() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,6 +77,7 @@ import com.android.settingslib.Utils;
|
||||
import com.android.systemui.Dumpable;
|
||||
import com.android.systemui.ExpandHelper;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.animation.ActivityLaunchAnimator;
|
||||
import com.android.systemui.animation.Interpolators;
|
||||
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
|
||||
import com.android.systemui.statusbar.CommandQueue;
|
||||
@@ -90,6 +91,7 @@ import com.android.systemui.statusbar.StatusBarState;
|
||||
import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
|
||||
import com.android.systemui.statusbar.notification.FakeShadowView;
|
||||
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
|
||||
import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorController;
|
||||
import com.android.systemui.statusbar.notification.NotificationUtils;
|
||||
import com.android.systemui.statusbar.notification.ShadeViewRefactor;
|
||||
import com.android.systemui.statusbar.notification.ShadeViewRefactor.RefactorComponent;
|
||||
@@ -470,6 +472,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
|
||||
*/
|
||||
private final Path mRoundedClipPath = new Path();
|
||||
|
||||
/**
|
||||
* The clip Path used to clip the launching notification. This may be different
|
||||
* from the normal path, as the views launch animation could start clipped.
|
||||
*/
|
||||
private final Path mLaunchedNotificationClipPath = new Path();
|
||||
|
||||
/**
|
||||
* Should we use rounded rect clipping right now
|
||||
*/
|
||||
@@ -492,6 +500,26 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
|
||||
*/
|
||||
private boolean mLaunchingNotification;
|
||||
|
||||
/**
|
||||
* Does the launching notification need to be clipped
|
||||
*/
|
||||
private boolean mLaunchingNotificationNeedsToBeClipped;
|
||||
|
||||
/**
|
||||
* The current launch animation params when launching a notification
|
||||
*/
|
||||
private ExpandAnimationParameters mLaunchAnimationParams;
|
||||
|
||||
/**
|
||||
* Corner radii of the launched notification if it's clipped
|
||||
*/
|
||||
private float[] mLaunchedNotificationRadii = new float[8];
|
||||
|
||||
/**
|
||||
* The notification that is being launched currently.
|
||||
*/
|
||||
private ExpandableNotificationRow mExpandingNotificationRow;
|
||||
|
||||
/**
|
||||
* Do notifications dismiss with normal transitioning
|
||||
*/
|
||||
@@ -2749,6 +2777,9 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
|
||||
* @return the amount of scrolling needed to start clipping notifications.
|
||||
*/
|
||||
private int getScrollAmountToScrollBoundary() {
|
||||
if (mShouldUseSplitNotificationShade) {
|
||||
return mSidePaddings;
|
||||
}
|
||||
return mTopPadding - mQsScrollBoundaryPosition;
|
||||
}
|
||||
|
||||
@@ -2892,7 +2923,16 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
|
||||
|
||||
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
|
||||
void setExpandingNotification(ExpandableNotificationRow row) {
|
||||
mAmbientState.setExpandingNotification(row);
|
||||
if (mExpandingNotificationRow != null && row == null) {
|
||||
// Let's unset the clip path being set during launch
|
||||
mExpandingNotificationRow.setExpandingClipPath(null);
|
||||
ExpandableNotificationRow parent = mExpandingNotificationRow.getNotificationParent();
|
||||
if (parent != null) {
|
||||
parent.setExpandingClipPath(null);
|
||||
}
|
||||
}
|
||||
mExpandingNotificationRow = row;
|
||||
updateLaunchedNotificationClipPath();
|
||||
requestChildrenUpdate();
|
||||
}
|
||||
|
||||
@@ -2902,10 +2942,10 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
|
||||
|
||||
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
|
||||
public void applyExpandAnimationParams(ExpandAnimationParameters params) {
|
||||
mAmbientState.setExpandAnimationTopChange(params == null ? 0 : params.getTopChange());
|
||||
|
||||
// Disable clipping for launches
|
||||
// Modify the clipping for launching notifications
|
||||
mLaunchAnimationParams = params;
|
||||
setLaunchingNotification(params != null);
|
||||
updateLaunchedNotificationClipPath();
|
||||
requestChildrenUpdate();
|
||||
}
|
||||
|
||||
@@ -5353,7 +5393,15 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
|
||||
return;
|
||||
}
|
||||
mLaunchingNotification = launching;
|
||||
updateUseRoundedRectClipping();
|
||||
mLaunchingNotificationNeedsToBeClipped = mLaunchAnimationParams != null
|
||||
&& (mLaunchAnimationParams.getStartRoundedTopClipping() > 0
|
||||
|| mLaunchAnimationParams.getParentStartRoundedTopClipping() > 0);
|
||||
if (!mLaunchingNotificationNeedsToBeClipped || !mLaunchingNotification) {
|
||||
mLaunchedNotificationClipPath.reset();
|
||||
}
|
||||
// When launching notifications, we're clipping the children individually instead of in
|
||||
// dispatchDraw
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -5363,22 +5411,97 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
|
||||
// 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;
|
||||
boolean clip = mIsExpanded && qsAllowsClipping;
|
||||
if (clip != mShouldUseRoundedRectClipping) {
|
||||
mShouldUseRoundedRectClipping = clip;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the clip path for launched notifications in case they were originally clipped
|
||||
*/
|
||||
private void updateLaunchedNotificationClipPath() {
|
||||
if (!mLaunchingNotificationNeedsToBeClipped || !mLaunchingNotification
|
||||
|| mExpandingNotificationRow == null) {
|
||||
return;
|
||||
}
|
||||
int left = Math.min(mLaunchAnimationParams.getLeft(), mRoundedRectClippingLeft);
|
||||
int right = Math.max(mLaunchAnimationParams.getRight(), mRoundedRectClippingRight);
|
||||
int bottom = Math.max(mLaunchAnimationParams.getBottom(), mRoundedRectClippingBottom);
|
||||
float expandProgress = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
|
||||
mLaunchAnimationParams.getProgress(0,
|
||||
NotificationLaunchAnimatorController.ANIMATION_DURATION_TOP_ROUNDING));
|
||||
int top = (int) Math.min(MathUtils.lerp(mRoundedRectClippingTop,
|
||||
mLaunchAnimationParams.getTop(), expandProgress),
|
||||
mRoundedRectClippingTop);
|
||||
float topRadius = mLaunchAnimationParams.getTopCornerRadius();
|
||||
float bottomRadius = mLaunchAnimationParams.getBottomCornerRadius();
|
||||
mLaunchedNotificationRadii[0] = topRadius;
|
||||
mLaunchedNotificationRadii[1] = topRadius;
|
||||
mLaunchedNotificationRadii[2] = topRadius;
|
||||
mLaunchedNotificationRadii[3] = topRadius;
|
||||
mLaunchedNotificationRadii[4] = bottomRadius;
|
||||
mLaunchedNotificationRadii[5] = bottomRadius;
|
||||
mLaunchedNotificationRadii[6] = bottomRadius;
|
||||
mLaunchedNotificationRadii[7] = bottomRadius;
|
||||
mLaunchedNotificationClipPath.reset();
|
||||
mLaunchedNotificationClipPath.addRoundRect(left, top, right, bottom,
|
||||
mLaunchedNotificationRadii, Path.Direction.CW);
|
||||
// Offset into notification clip coordinates instead of parent ones.
|
||||
// This is needed since the notification changes in translationZ, where clipping via
|
||||
// canvas dispatching won't work.
|
||||
ExpandableNotificationRow expandingRow = mExpandingNotificationRow;
|
||||
if (expandingRow.getNotificationParent() != null) {
|
||||
expandingRow = expandingRow.getNotificationParent();
|
||||
}
|
||||
mLaunchedNotificationClipPath.offset(
|
||||
-expandingRow.getLeft() - expandingRow.getTranslationX(),
|
||||
-expandingRow.getTop() - expandingRow.getTranslationY());
|
||||
expandingRow.setExpandingClipPath(mLaunchedNotificationClipPath);
|
||||
if (mShouldUseRoundedRectClipping) {
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dispatchDraw(Canvas canvas) {
|
||||
if (mShouldUseRoundedRectClipping) {
|
||||
if (mShouldUseRoundedRectClipping && !mLaunchingNotification) {
|
||||
// When launching notifications, we're clipping the children individually instead of in
|
||||
// dispatchDraw
|
||||
// Let's clip rounded.
|
||||
canvas.clipPath(mRoundedClipPath);
|
||||
}
|
||||
super.dispatchDraw(canvas);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
|
||||
if (mShouldUseRoundedRectClipping && mLaunchingNotification) {
|
||||
// Let's clip children individually during notification launch
|
||||
canvas.save();
|
||||
ExpandableView expandableView = (ExpandableView) child;
|
||||
Path clipPath;
|
||||
if (expandableView.isExpandAnimationRunning()
|
||||
|| ((ExpandableView) child).hasExpandingChild()) {
|
||||
// When launching the notification, it is not clipped by this layout, but by the
|
||||
// view itself. This is because the view is Translating in Z, where this clipPath
|
||||
// wouldn't apply.
|
||||
clipPath = null;
|
||||
} else {
|
||||
clipPath = mRoundedClipPath;
|
||||
}
|
||||
if (clipPath != null) {
|
||||
canvas.clipPath(clipPath);
|
||||
}
|
||||
boolean result = super.drawChild(canvas, child, drawingTime);
|
||||
canvas.restore();
|
||||
return result;
|
||||
} else {
|
||||
return super.drawChild(canvas, child, drawingTime);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the total translation needed when dismissing.
|
||||
*/
|
||||
@@ -5392,6 +5515,13 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
|
||||
return containerWidth - padding;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the start location where we start clipping notifications.
|
||||
*/
|
||||
public int getTopClippingStartLocation() {
|
||||
return mIsExpanded ? mQsScrollBoundaryPosition : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* A listener that is notified when the empty space below the notifications is clicked on
|
||||
*/
|
||||
|
||||
@@ -1537,6 +1537,11 @@ public class NotificationStackScrollLayoutController {
|
||||
mView.setNotificationActivityStarter(notificationActivityStarter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTopClippingStartLocation() {
|
||||
return mView.getTopClippingStartLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getContainerChildAt(int i) {
|
||||
return mView.getContainerChildAt(i);
|
||||
|
||||
Reference in New Issue
Block a user