Added new appear and disappear animations for heads up
The heads up notifications now appear directly out of the statusbar icon in a smoother way. This also fixes the landscape presentation, which was completely wrong before. Test: runtest systemui Change-Id: I84e65d5216f74a9eb1717d3e7c111d66c0b43c65 Fixes: 72748440
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:visibility="invisible"
|
||||
android:id="@+id/heads_up_status_bar_view"
|
||||
android:alpha="0"
|
||||
>
|
||||
<!-- This is a space just used as a layout and it's not actually displaying anything. We're
|
||||
repositioning the statusbar icon to the position where this is laid out when showing this
|
||||
|
||||
@@ -26,6 +26,7 @@ import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.RectF;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.MathUtils;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewAnimationUtils;
|
||||
@@ -178,6 +179,10 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
|
||||
private boolean mNeedsDimming;
|
||||
private int mDimmedAlpha;
|
||||
private boolean mBlockNextTouch;
|
||||
private boolean mIsHeadsUpAnimation;
|
||||
private int mHeadsUpAddStartLocation;
|
||||
private float mHeadsUpLocation;
|
||||
private boolean mIsAppearing;
|
||||
|
||||
public ActivatableNotificationView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
@@ -204,6 +209,18 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
|
||||
makeInactive(true /* animate */);
|
||||
}
|
||||
}, super::performClick, this::handleSlideBack, mFalsingManager::onNotificationDoubleTap);
|
||||
initDimens();
|
||||
}
|
||||
|
||||
private void initDimens() {
|
||||
mHeadsUpAddStartLocation = getResources().getDimensionPixelSize(
|
||||
com.android.internal.R.dimen.notification_content_margin_start);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDensityOrFontScaleChanged() {
|
||||
super.onDensityOrFontScaleChanged();
|
||||
initDimens();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -745,27 +762,34 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performRemoveAnimation(long duration, float translationDirection,
|
||||
Runnable onFinishedRunnable) {
|
||||
public void performRemoveAnimation(long duration, long delay,
|
||||
float translationDirection, boolean isHeadsUpAnimation, float endLocation,
|
||||
Runnable onFinishedRunnable, AnimatorListenerAdapter animationListener) {
|
||||
enableAppearDrawing(true);
|
||||
mIsHeadsUpAnimation = isHeadsUpAnimation;
|
||||
mHeadsUpLocation = endLocation;
|
||||
if (mDrawingAppearAnimation) {
|
||||
startAppearAnimation(false /* isAppearing */, translationDirection,
|
||||
0, duration, onFinishedRunnable);
|
||||
delay, duration, onFinishedRunnable, animationListener);
|
||||
} else if (onFinishedRunnable != null) {
|
||||
onFinishedRunnable.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performAddAnimation(long delay, long duration) {
|
||||
public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear) {
|
||||
enableAppearDrawing(true);
|
||||
mIsHeadsUpAnimation = isHeadsUpAppear;
|
||||
mHeadsUpLocation = mHeadsUpAddStartLocation;
|
||||
if (mDrawingAppearAnimation) {
|
||||
startAppearAnimation(true /* isAppearing */, -1.0f, delay, duration, null);
|
||||
startAppearAnimation(true /* isAppearing */, isHeadsUpAppear ? 0.0f : -1.0f, delay,
|
||||
duration, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void startAppearAnimation(boolean isAppearing, float translationDirection, long delay,
|
||||
long duration, final Runnable onFinishedRunnable) {
|
||||
long duration, final Runnable onFinishedRunnable,
|
||||
AnimatorListenerAdapter animationListener) {
|
||||
cancelAppearAnimation();
|
||||
mAnimationTranslationY = translationDirection * getActualHeight();
|
||||
if (mAppearAnimationFraction == -1.0f) {
|
||||
@@ -778,6 +802,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
|
||||
mAppearAnimationTranslation = 0;
|
||||
}
|
||||
}
|
||||
mIsAppearing = isAppearing;
|
||||
|
||||
float targetValue;
|
||||
if (isAppearing) {
|
||||
@@ -803,6 +828,9 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
|
||||
invalidate();
|
||||
}
|
||||
});
|
||||
if (animationListener != null) {
|
||||
mAppearAnimator.addListener(animationListener);
|
||||
}
|
||||
if (delay > 0) {
|
||||
// we need to apply the initial state already to avoid drawn frames in the wrong state
|
||||
updateAppearAnimationAlpha();
|
||||
@@ -862,9 +890,21 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView
|
||||
/ (HORIZONTAL_ANIMATION_START - HORIZONTAL_ANIMATION_END);
|
||||
widthFraction = Math.min(1.0f, Math.max(0.0f, widthFraction));
|
||||
widthFraction = mCurrentAppearInterpolator.getInterpolation(widthFraction);
|
||||
float left = (getWidth() * (0.5f - HORIZONTAL_COLLAPSED_REST_PARTIAL / 2.0f) *
|
||||
widthFraction);
|
||||
float right = getWidth() - left;
|
||||
float startWidthFraction = HORIZONTAL_COLLAPSED_REST_PARTIAL;
|
||||
if (mIsHeadsUpAnimation && !mIsAppearing) {
|
||||
startWidthFraction = 0;
|
||||
}
|
||||
float width = MathUtils.lerp(startWidthFraction, 1.0f, 1.0f - widthFraction)
|
||||
* getWidth();
|
||||
float left;
|
||||
float right;
|
||||
if (mIsHeadsUpAnimation) {
|
||||
left = MathUtils.lerp(mHeadsUpLocation, 0, 1.0f - widthFraction);
|
||||
right = left + width;
|
||||
} else {
|
||||
left = getWidth() * 0.5f - width / 2.0f;
|
||||
right = getWidth() - left;
|
||||
}
|
||||
|
||||
// handle top animation
|
||||
float heightFraction = (inverseFraction - (1.0f - VERTICAL_ANIMATION_START)) /
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.systemui.statusbar;
|
||||
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.content.Context;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
@@ -296,19 +297,24 @@ public abstract class ExpandableView extends FrameLayout {
|
||||
|
||||
/**
|
||||
* Perform a remove animation on this view.
|
||||
*
|
||||
* @param duration The duration of the remove animation.
|
||||
* @param delay The delay of the animation
|
||||
* @param translationDirection The direction value from [-1 ... 1] indicating in which the
|
||||
* animation should be performed. A value of -1 means that The
|
||||
* remove animation should be performed upwards,
|
||||
* such that the child appears to be going away to the top. 1
|
||||
* Should mean the opposite.
|
||||
* animation should be performed. A value of -1 means that The
|
||||
* remove animation should be performed upwards,
|
||||
* such that the child appears to be going away to the top. 1
|
||||
* Should mean the opposite.
|
||||
* @param isHeadsUpAnimation Is this a headsUp animation.
|
||||
* @param endLocation The location where the horizonal heads up disappear animation should end.
|
||||
* @param onFinishedRunnable A runnable which should be run when the animation is finished.
|
||||
* @param animationListener An animation listener to add to the animation.
|
||||
*/
|
||||
public abstract void performRemoveAnimation(long duration, float translationDirection,
|
||||
Runnable onFinishedRunnable);
|
||||
public abstract void performRemoveAnimation(long duration,
|
||||
long delay, float translationDirection, boolean isHeadsUpAnimation, float endLocation,
|
||||
Runnable onFinishedRunnable,
|
||||
AnimatorListenerAdapter animationListener);
|
||||
|
||||
public abstract void performAddAnimation(long delay, long duration);
|
||||
public abstract void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear);
|
||||
|
||||
/**
|
||||
* Set the notification appearance to be below the speed bump.
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.systemui.statusbar;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
@@ -37,10 +38,15 @@ public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout {
|
||||
private View mIconPlaceholder;
|
||||
private TextView mTextView;
|
||||
private NotificationData.Entry mShowingEntry;
|
||||
private Rect mIconRect = new Rect();
|
||||
private Rect mLayoutedIconRect = new Rect();
|
||||
private int[] mTmpPosition = new int[2];
|
||||
private boolean mFirstLayout = true;
|
||||
private boolean mPublicMode;
|
||||
private int mMaxWidth;
|
||||
private View mRootView;
|
||||
private int mLeftInset;
|
||||
private Rect mIconDrawingRect = new Rect();
|
||||
private Runnable mOnDrawingRectChangedListener;
|
||||
|
||||
public HeadsUpStatusBarView(Context context) {
|
||||
this(context, null);
|
||||
@@ -64,6 +70,33 @@ public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout {
|
||||
mEndMargin = res.getDimensionPixelSize(
|
||||
com.android.internal.R.dimen.notification_content_margin_end);
|
||||
setPaddingRelative(mAbsoluteStartPadding, 0, mEndMargin, 0);
|
||||
updateMaxWidth();
|
||||
}
|
||||
|
||||
private void updateMaxWidth() {
|
||||
int maxWidth = getResources().getDimensionPixelSize(R.dimen.qs_panel_width);
|
||||
if (maxWidth != mMaxWidth) {
|
||||
// maxWidth doesn't work with fill_parent, let's manually make it at most as big as the
|
||||
// notification panel
|
||||
mMaxWidth = maxWidth;
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
if (mMaxWidth > 0) {
|
||||
int newSize = Math.min(MeasureSpec.getSize(widthMeasureSpec), mMaxWidth);
|
||||
widthMeasureSpec = MeasureSpec.makeMeasureSpec(newSize,
|
||||
MeasureSpec.getMode(widthMeasureSpec));
|
||||
}
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
updateMaxWidth();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -97,13 +130,15 @@ public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout {
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
super.onLayout(changed, l, t, r, b);
|
||||
mIconPlaceholder.getLocationOnScreen(mTmpPosition);
|
||||
int left = mTmpPosition[0];
|
||||
int left = (int) (mTmpPosition[0] - getTranslationX());
|
||||
int top = mTmpPosition[1];
|
||||
int right = left + mIconPlaceholder.getWidth();
|
||||
int bottom = top + mIconPlaceholder.getHeight();
|
||||
mIconRect.set(left, top, right, bottom);
|
||||
if (left != mAbsoluteStartPadding) {
|
||||
int newPadding = mAbsoluteStartPadding - left + getPaddingStart();
|
||||
mLayoutedIconRect.set(left, top, right, bottom);
|
||||
updateDrawingRect();
|
||||
int targetPadding = mAbsoluteStartPadding + mLeftInset;
|
||||
if (left != targetPadding) {
|
||||
int newPadding = targetPadding - left + getPaddingStart();
|
||||
setPaddingRelative(newPadding, 0, mEndMargin, 0);
|
||||
}
|
||||
if (mFirstLayout) {
|
||||
@@ -115,12 +150,33 @@ public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTranslationX(float translationX) {
|
||||
super.setTranslationX(translationX);
|
||||
updateDrawingRect();
|
||||
}
|
||||
|
||||
private void updateDrawingRect() {
|
||||
float oldLeft = mIconDrawingRect.left;
|
||||
mIconDrawingRect.set(mLayoutedIconRect);
|
||||
mIconDrawingRect.offset((int) getTranslationX(), 0);
|
||||
if (oldLeft != mIconDrawingRect.left && mOnDrawingRectChangedListener != null) {
|
||||
mOnDrawingRectChangedListener.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean fitSystemWindows(Rect insets) {
|
||||
mLeftInset = insets.left;
|
||||
return super.fitSystemWindows(insets);
|
||||
}
|
||||
|
||||
public NotificationData.Entry getShowingEntry() {
|
||||
return mShowingEntry;
|
||||
}
|
||||
|
||||
public Rect getIconDrawingRect() {
|
||||
return mIconRect;
|
||||
return mIconDrawingRect;
|
||||
}
|
||||
|
||||
public void onDarkChanged(Rect area, float darkIntensity, int tint) {
|
||||
@@ -130,4 +186,8 @@ public class HeadsUpStatusBarView extends AlphaOptimizedLinearLayout {
|
||||
public void setPublicMode(boolean publicMode) {
|
||||
mPublicMode = publicMode;
|
||||
}
|
||||
|
||||
public void setOnDrawingRectChangedListener(Runnable onDrawingRectChangedListener) {
|
||||
mOnDrawingRectChangedListener = onDrawingRectChangedListener;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.systemui.statusbar;
|
||||
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
@@ -112,14 +113,16 @@ public abstract class StackScrollerDecorView extends ExpandableView {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performRemoveAnimation(long duration, float translationDirection,
|
||||
Runnable onFinishedRunnable) {
|
||||
public void performRemoveAnimation(long duration, long delay,
|
||||
float translationDirection, boolean isHeadsUpAnimation, float endLocation,
|
||||
Runnable onFinishedRunnable,
|
||||
AnimatorListenerAdapter animationListener) {
|
||||
// TODO: Use duration
|
||||
performVisibilityAnimation(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performAddAnimation(long delay, long duration) {
|
||||
public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear) {
|
||||
// TODO: use delay and duration
|
||||
performVisibilityAnimation(true);
|
||||
}
|
||||
|
||||
@@ -72,20 +72,37 @@ class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
|
||||
mHeadsUpManager = headsUpManager;
|
||||
mHeadsUpManager.addListener(this);
|
||||
mHeadsUpStatusBarView = headsUpStatusBarView;
|
||||
headsUpStatusBarView.setOnDrawingRectChangedListener(
|
||||
() -> updateIsolatedIconLocation(true /* requireUpdate */));
|
||||
mStackScroller = stackScroller;
|
||||
panelView.addTrackingHeadsUpListener(this::setTrackingHeadsUp);
|
||||
panelView.setVerticalTranslationListener(this::updatePanelTranslation);
|
||||
panelView.setHeadsUpAppearanceController(this);
|
||||
mStackScroller.addOnExpandedHeightListener(this::setExpandedHeight);
|
||||
mStackScroller.addOnLayoutChangeListener(
|
||||
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom)
|
||||
-> updatePanelTranslation());
|
||||
mClockView = clockView;
|
||||
mDarkIconDispatcher = Dependency.get(DarkIconDispatcher.class);
|
||||
mDarkIconDispatcher.addDarkReceiver(this);
|
||||
}
|
||||
|
||||
private void updateIsolatedIconLocation(boolean requireStateUpdate) {
|
||||
mNotificationIconAreaController.setIsolatedIconLocation(
|
||||
mHeadsUpStatusBarView.getIconDrawingRect(), requireStateUpdate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHeadsUpPinned(ExpandableNotificationRow headsUp) {
|
||||
updateTopEntry();
|
||||
updateHeader(headsUp.getEntry());
|
||||
}
|
||||
|
||||
public void updatePanelTranslation() {
|
||||
float newTranslation = mStackScroller.getLeft() + mStackScroller.getTranslationX();
|
||||
mHeadsUpStatusBarView.setTranslationX(newTranslation);
|
||||
}
|
||||
|
||||
private void updateTopEntry() {
|
||||
NotificationData.Entry newEntry = null;
|
||||
if (!mIsExpanded && mHeadsUpManager.hasPinnedHeadsUp()) {
|
||||
@@ -104,9 +121,11 @@ class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
|
||||
// We now have a headsUp and didn't have one before. Let's start the disappear
|
||||
// animation
|
||||
setShown(true);
|
||||
animateIsolation = !mIsExpanded;
|
||||
}
|
||||
updateIsolatedIconLocation(false /* requireUpdate */);
|
||||
mNotificationIconAreaController.showIconIsolated(newEntry == null ? null
|
||||
: newEntry.icon, mHeadsUpStatusBarView.getIconDrawingRect(), animateIsolation);
|
||||
: newEntry.icon, animateIsolation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,6 +153,16 @@ class HeadsUpAppearanceController implements OnHeadsUpChangedListener,
|
||||
return mShown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the headsup status bar view be visible right now? This may be different from isShown,
|
||||
* since the headsUp manager might not have notified us yet of the state change.
|
||||
*
|
||||
* @return if the heads up status bar view should be shown
|
||||
*/
|
||||
public boolean shouldBeVisible() {
|
||||
return !mIsExpanded && mHeadsUpManager.hasPinnedHeadsUp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHeadsUpUnPinned(ExpandableNotificationRow headsUp) {
|
||||
updateTopEntry();
|
||||
|
||||
@@ -311,8 +311,11 @@ public class NotificationIconAreaController implements DarkReceiver {
|
||||
mShelfIcons.setDark(dark, false, 0);
|
||||
}
|
||||
|
||||
public void showIconIsolated(StatusBarIconView icon, Rect absoluteIconPosition,
|
||||
boolean animated) {
|
||||
mNotificationIcons.showIconIsolated(icon, absoluteIconPosition, animated);
|
||||
public void showIconIsolated(StatusBarIconView icon, boolean animated) {
|
||||
mNotificationIcons.showIconIsolated(icon, animated);
|
||||
}
|
||||
|
||||
public void setIsolatedIconLocation(Rect iconDrawingRect, boolean requireStateUpdate) {
|
||||
mNotificationIcons.setIsolatedIconLocation(iconDrawingRect, requireStateUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package com.android.systemui.statusbar.phone;
|
||||
|
||||
import static com.android.systemui.statusbar.phone.HeadsUpAppearanceController.CONTENT_FADE_DURATION;
|
||||
import static com.android.systemui.statusbar.phone.HeadsUpAppearanceController.CONTENT_FADE_DELAY;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Canvas;
|
||||
@@ -109,8 +112,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
|
||||
public AnimationFilter getAnimationFilter() {
|
||||
return mAnimationFilter;
|
||||
}
|
||||
}.setDuration(HeadsUpAppearanceController.CONTENT_FADE_DURATION).setDelay(
|
||||
HeadsUpAppearanceController.CONTENT_FADE_DURATION);
|
||||
}.setDuration(CONTENT_FADE_DURATION);
|
||||
|
||||
/**
|
||||
* The animation property used for the icon when its isolation ends.
|
||||
@@ -123,7 +125,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
|
||||
public AnimationFilter getAnimationFilter() {
|
||||
return mAnimationFilter;
|
||||
}
|
||||
}.setDuration(HeadsUpAppearanceController.CONTENT_FADE_DURATION);
|
||||
}.setDuration(CONTENT_FADE_DURATION);
|
||||
|
||||
public static final int MAX_VISIBLE_ICONS_WHEN_DARK = 5;
|
||||
public static final int MAX_STATIC_ICONS = 4;
|
||||
@@ -319,7 +321,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
|
||||
boolean isIsolatedIcon = child == mIsolatedIcon;
|
||||
icon.setVisibleState(StatusBarIconView.STATE_HIDDEN, true /* animate */,
|
||||
() -> removeTransientView(icon),
|
||||
isIsolatedIcon ? HeadsUpAppearanceController.CONTENT_FADE_DURATION : 0);
|
||||
isIsolatedIcon ? CONTENT_FADE_DURATION : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -620,16 +622,21 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
|
||||
mReplacingIcons = replacingIcons;
|
||||
}
|
||||
|
||||
public void showIconIsolated(StatusBarIconView icon, Rect absoluteIconPosition,
|
||||
boolean animated) {
|
||||
public void showIconIsolated(StatusBarIconView icon, boolean animated) {
|
||||
if (animated) {
|
||||
mIsolatedIconForAnimation = mIsolatedIcon;
|
||||
mIsolatedIconForAnimation = icon != null ? icon : mIsolatedIcon;
|
||||
}
|
||||
mIsolatedIcon = icon;
|
||||
mIsolatedIconLocation = absoluteIconPosition;
|
||||
updateState();
|
||||
}
|
||||
|
||||
public void setIsolatedIconLocation(Rect isolatedIconLocation, boolean requireUpdate) {
|
||||
mIsolatedIconLocation = isolatedIconLocation;
|
||||
if (requireUpdate) {
|
||||
updateState();
|
||||
}
|
||||
}
|
||||
|
||||
public class IconState extends ViewState {
|
||||
public static final int NO_VALUE = NotificationIconContainer.NO_VALUE;
|
||||
public float iconAppearAmount = 1.0f;
|
||||
@@ -706,8 +713,12 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
|
||||
if (mIsolatedIconForAnimation != null) {
|
||||
if (view == mIsolatedIconForAnimation) {
|
||||
animationProperties = UNISOLATION_PROPERTY;
|
||||
animationProperties.setDelay(
|
||||
mIsolatedIcon != null ? CONTENT_FADE_DELAY : 0);
|
||||
} else {
|
||||
animationProperties = UNISOLATION_PROPERTY_OTHERS;
|
||||
animationProperties.setDelay(
|
||||
mIsolatedIcon == null ? CONTENT_FADE_DELAY : 0);
|
||||
}
|
||||
animate = true;
|
||||
}
|
||||
|
||||
@@ -247,6 +247,8 @@ public class NotificationPanelView extends PanelView implements
|
||||
private int mStackScrollerMeasuringPass;
|
||||
private ArrayList<Consumer<ExpandableNotificationRow>> mTrackingHeadsUpListeners
|
||||
= new ArrayList<>();
|
||||
private Runnable mVerticalTranslationListener;
|
||||
private HeadsUpAppearanceController mHeadsUpAppearanceController;
|
||||
|
||||
public NotificationPanelView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
@@ -2413,6 +2415,9 @@ public class NotificationPanelView extends PanelView implements
|
||||
protected void setVerticalPanelTranslation(float translation) {
|
||||
mNotificationStackScroller.setTranslationX(translation);
|
||||
mQsFrame.setTranslationX(translation);
|
||||
if (mVerticalTranslationListener != null) {
|
||||
mVerticalTranslationListener.run();
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateExpandedHeight(float expandedHeight) {
|
||||
@@ -2568,6 +2573,10 @@ public class NotificationPanelView extends PanelView implements
|
||||
if (mLaunchingNotification) {
|
||||
return mHideIconsDuringNotificationLaunch;
|
||||
}
|
||||
if (mHeadsUpAppearanceController != null
|
||||
&& mHeadsUpAppearanceController.shouldBeVisible()) {
|
||||
return false;
|
||||
}
|
||||
return !isFullWidth() || !mShowIconsWhenExpanded;
|
||||
}
|
||||
|
||||
@@ -2713,4 +2722,13 @@ public class NotificationPanelView extends PanelView implements
|
||||
public void addTrackingHeadsUpListener(Consumer<ExpandableNotificationRow> listener) {
|
||||
mTrackingHeadsUpListeners.add(listener);
|
||||
}
|
||||
|
||||
public void setVerticalTranslationListener(Runnable verticalTranslationListener) {
|
||||
mVerticalTranslationListener = verticalTranslationListener;
|
||||
}
|
||||
|
||||
public void setHeadsUpAppearanceController(
|
||||
HeadsUpAppearanceController headsUpAppearanceController) {
|
||||
mHeadsUpAppearanceController = headsUpAppearanceController;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,12 +21,12 @@ import android.util.Property;
|
||||
import android.view.View;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Filters the animations for only a certain type of properties.
|
||||
*/
|
||||
public class AnimationFilter {
|
||||
public static final int NO_DELAY = -1;
|
||||
boolean animateAlpha;
|
||||
boolean animateX;
|
||||
boolean animateY;
|
||||
@@ -40,7 +40,7 @@ public class AnimationFilter {
|
||||
public boolean animateShadowAlpha;
|
||||
boolean hasDelays;
|
||||
boolean hasGoToFullShadeEvent;
|
||||
boolean hasHeadsUpDisappearClickEvent;
|
||||
long customDelay;
|
||||
private ArraySet<Property> mAnimatedProperties = new ArraySet<>();
|
||||
|
||||
public AnimationFilter animateAlpha() {
|
||||
@@ -129,8 +129,14 @@ public class AnimationFilter {
|
||||
hasGoToFullShadeEvent = true;
|
||||
}
|
||||
if (ev.animationType == NotificationStackScrollLayout.AnimationEvent
|
||||
.ANIMATION_TYPE_HEADS_UP_DISAPPEAR) {
|
||||
customDelay = StackStateAnimator.ANIMATION_DELAY_HEADS_UP;
|
||||
} else if (ev.animationType == NotificationStackScrollLayout.AnimationEvent
|
||||
.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK) {
|
||||
hasHeadsUpDisappearClickEvent = true;
|
||||
// We need both timeouts when clicking, one to delay it and one for the animation
|
||||
// to look nice
|
||||
customDelay = StackStateAnimator.ANIMATION_DELAY_HEADS_UP_CLICKED
|
||||
+ StackStateAnimator.ANIMATION_DELAY_HEADS_UP;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -165,7 +171,7 @@ public class AnimationFilter {
|
||||
animateHideSensitive = false;
|
||||
hasDelays = false;
|
||||
hasGoToFullShadeEvent = false;
|
||||
hasHeadsUpDisappearClickEvent = false;
|
||||
customDelay = NO_DELAY;
|
||||
mAnimatedProperties.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -233,7 +233,8 @@ public class ExpandableViewState extends ViewState {
|
||||
expandableView.setDark(this.dark, animationFilter.animateDark, properties.delay);
|
||||
|
||||
if (properties.wasAdded(child) && !hidden) {
|
||||
expandableView.performAddAnimation(properties.delay, properties.duration);
|
||||
expandableView.performAddAnimation(properties.delay, properties.duration,
|
||||
false /* isHeadsUpAppear */);
|
||||
}
|
||||
|
||||
if (!expandableView.isInShelf() && this.inShelf) {
|
||||
|
||||
@@ -23,6 +23,11 @@ import android.view.animation.PathInterpolator;
|
||||
* An interpolator specifically designed for the appear animation of heads up notifications.
|
||||
*/
|
||||
public class HeadsUpAppearInterpolator extends PathInterpolator {
|
||||
|
||||
private static float X1 = 250f;
|
||||
private static float X2 = 200f;
|
||||
private static float XTOT = (X1 + X2);;
|
||||
|
||||
public HeadsUpAppearInterpolator() {
|
||||
super(getAppearPath());
|
||||
}
|
||||
@@ -30,22 +35,18 @@ public class HeadsUpAppearInterpolator extends PathInterpolator {
|
||||
private static Path getAppearPath() {
|
||||
Path path = new Path();
|
||||
path.moveTo(0, 0);
|
||||
float x1 = 250f;
|
||||
float x2 = 150f;
|
||||
float x3 = 100f;
|
||||
float y1 = 90f;
|
||||
float y2 = 78f;
|
||||
float y3 = 80f;
|
||||
float xTot = (x1 + x2 + x3);
|
||||
path.cubicTo(x1 * 0.9f / xTot, 0f,
|
||||
x1 * 0.8f / xTot, y1 / y3,
|
||||
x1 / xTot , y1 / y3);
|
||||
path.cubicTo((x1 + x2 * 0.4f) / xTot, y1 / y3,
|
||||
(x1 + x2 * 0.2f) / xTot, y2 / y3,
|
||||
(x1 + x2) / xTot, y2 / y3);
|
||||
path.cubicTo((x1 + x2 + x3 * 0.4f) / xTot, y2 / y3,
|
||||
(x1 + x2 + x3 * 0.2f) / xTot, 1f,
|
||||
1f, 1f);
|
||||
float y2 = 80f;
|
||||
path.cubicTo(X1 * 0.8f / XTOT, y1 / y2,
|
||||
X1 * 0.8f / XTOT, y1 / y2,
|
||||
X1 / XTOT, y1 / y2);
|
||||
path.cubicTo((X1 + X2 * 0.4f) / XTOT, y1 / y2,
|
||||
(X1 + X2 * 0.2f) / XTOT, 1.0f,
|
||||
1.0f , 1.0f);
|
||||
return path;
|
||||
}
|
||||
|
||||
public static float getFractionUntilOvershoot() {
|
||||
return X1 / XTOT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4885,7 +4885,8 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
.animateHeight()
|
||||
.animateTopInset()
|
||||
.animateY()
|
||||
.animateZ(),
|
||||
.animateZ()
|
||||
.hasDelays(),
|
||||
|
||||
// ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
|
||||
new AnimationFilter()
|
||||
|
||||
@@ -528,9 +528,6 @@ public class StackScrollAlgorithm {
|
||||
childViewState.inShelf = true;
|
||||
childViewState.headsUpIsVisible = false;
|
||||
}
|
||||
if (!ambientState.isShadeExpanded()) {
|
||||
childViewState.height = (int) (mStatusBarHeight - childViewState.yTranslation);
|
||||
}
|
||||
}
|
||||
|
||||
protected int getMaxAllowedChildHeight(View child) {
|
||||
|
||||
@@ -29,6 +29,7 @@ import com.android.systemui.R;
|
||||
import com.android.systemui.statusbar.ExpandableNotificationRow;
|
||||
import com.android.systemui.statusbar.ExpandableView;
|
||||
import com.android.systemui.statusbar.NotificationShelf;
|
||||
import com.android.systemui.statusbar.StatusBarIconView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
@@ -45,13 +46,17 @@ public class StackStateAnimator {
|
||||
public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464;
|
||||
public static final int ANIMATION_DURATION_DIMMED_ACTIVATED = 220;
|
||||
public static final int ANIMATION_DURATION_CLOSE_REMOTE_INPUT = 150;
|
||||
public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 650;
|
||||
public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 230;
|
||||
public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 550;
|
||||
public static final int ANIMATION_DURATION_HEADS_UP_APPEAR_CLOSED
|
||||
= (int) (ANIMATION_DURATION_HEADS_UP_APPEAR
|
||||
* HeadsUpAppearInterpolator.getFractionUntilOvershoot());
|
||||
public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 300;
|
||||
public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80;
|
||||
public static final int ANIMATION_DELAY_PER_ELEMENT_MANUAL = 32;
|
||||
public static final int ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE = 48;
|
||||
public static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2;
|
||||
public static final int ANIMATION_DELAY_HEADS_UP = 120;
|
||||
public static final int ANIMATION_DELAY_HEADS_UP_CLICKED= 120;
|
||||
|
||||
private final int mGoToFullShadeAppearingTranslation;
|
||||
private final ExpandableViewState mTmpState = new ExpandableViewState();
|
||||
@@ -74,8 +79,9 @@ public class StackStateAnimator {
|
||||
private ValueAnimator mBottomOverScrollAnimator;
|
||||
private int mHeadsUpAppearHeightBottom;
|
||||
private boolean mShadeExpanded;
|
||||
private ArrayList<View> mChildrenToClearFromOverlay = new ArrayList<>();
|
||||
private NotificationShelf mShelf;
|
||||
private float mStatusBarIconLocation;
|
||||
private int[] mTmpLocation = new int[2];
|
||||
|
||||
public StackStateAnimator(NotificationStackScrollLayout hostLayout) {
|
||||
mHostLayout = hostLayout;
|
||||
@@ -222,8 +228,8 @@ public class StackStateAnimator {
|
||||
if (mAnimationFilter.hasGoToFullShadeEvent) {
|
||||
return calculateDelayGoToFullShade(viewState);
|
||||
}
|
||||
if (mAnimationFilter.hasHeadsUpDisappearClickEvent) {
|
||||
return ANIMATION_DELAY_HEADS_UP;
|
||||
if (mAnimationFilter.customDelay != AnimationFilter.NO_DELAY) {
|
||||
return mAnimationFilter.customDelay;
|
||||
}
|
||||
long minDelay = 0;
|
||||
for (NotificationStackScrollLayout.AnimationEvent event : mNewEvents) {
|
||||
@@ -327,10 +333,6 @@ public class StackStateAnimator {
|
||||
|
||||
private void onAnimationFinished() {
|
||||
mHostLayout.onChildAnimationFinished();
|
||||
for (View v : mChildrenToClearFromOverlay) {
|
||||
removeFromOverlay(v);
|
||||
}
|
||||
mChildrenToClearFromOverlay.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -396,13 +398,14 @@ public class StackStateAnimator {
|
||||
|
||||
}
|
||||
changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR,
|
||||
translationDirection, new Runnable() {
|
||||
0 /* delay */, translationDirection, false /* isHeadsUpAppear */,
|
||||
0, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// remove the temporary overlay
|
||||
removeFromOverlay(changingView);
|
||||
}
|
||||
});
|
||||
}, null);
|
||||
} else if (event.animationType ==
|
||||
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
|
||||
// A race condition can trigger the view to be added to the overlay even though
|
||||
@@ -424,7 +427,9 @@ public class StackStateAnimator {
|
||||
if (event.headsUpFromBottom) {
|
||||
mTmpState.yTranslation = mHeadsUpAppearHeightBottom;
|
||||
} else {
|
||||
mTmpState.yTranslation = -mTmpState.height;
|
||||
mTmpState.yTranslation = 0;
|
||||
changingView.performAddAnimation(0, ANIMATION_DURATION_HEADS_UP_APPEAR_CLOSED,
|
||||
true /* isHeadsUpAppear */);
|
||||
}
|
||||
mHeadsUpAppearChildren.add(changingView);
|
||||
mTmpState.applyToView(changingView);
|
||||
@@ -433,22 +438,56 @@ public class StackStateAnimator {
|
||||
event.animationType == NotificationStackScrollLayout
|
||||
.AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK) {
|
||||
mHeadsUpDisappearChildren.add(changingView);
|
||||
Runnable endRunnable = null;
|
||||
// We need some additional delay in case we were removed to make sure we're not
|
||||
// lagging
|
||||
int extraDelay = event.animationType == NotificationStackScrollLayout
|
||||
.AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
|
||||
? ANIMATION_DELAY_HEADS_UP_CLICKED
|
||||
: 0;
|
||||
if (changingView.getParent() == null) {
|
||||
// This notification was actually removed, so we need to add it to the overlay
|
||||
mHostLayout.getOverlay().add(changingView);
|
||||
mTmpState.initFrom(changingView);
|
||||
mTmpState.yTranslation = -changingView.getActualHeight();
|
||||
mTmpState.yTranslation = 0;
|
||||
// We temporarily enable Y animations, the real filter will be combined
|
||||
// afterwards anyway
|
||||
mAnimationFilter.animateY = true;
|
||||
mAnimationProperties.delay =
|
||||
event.animationType == NotificationStackScrollLayout
|
||||
.AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
|
||||
? ANIMATION_DELAY_HEADS_UP
|
||||
: 0;
|
||||
mAnimationProperties.delay = extraDelay + ANIMATION_DELAY_HEADS_UP;
|
||||
mAnimationProperties.duration = ANIMATION_DURATION_HEADS_UP_DISAPPEAR;
|
||||
mTmpState.animateTo(changingView, mAnimationProperties);
|
||||
mChildrenToClearFromOverlay.add(changingView);
|
||||
endRunnable = () -> {
|
||||
// remove the temporary overlay
|
||||
removeFromOverlay(changingView);
|
||||
};
|
||||
}
|
||||
float targetLocation = 0;
|
||||
boolean needsAnimation = true;
|
||||
if (changingView instanceof ExpandableNotificationRow) {
|
||||
ExpandableNotificationRow row = (ExpandableNotificationRow) changingView;
|
||||
if (row.isDismissed()) {
|
||||
needsAnimation = false;
|
||||
}
|
||||
StatusBarIconView icon = row.getEntry().icon;
|
||||
if (icon.getParent() != null) {
|
||||
icon.getLocationOnScreen(mTmpLocation);
|
||||
float iconPosition = mTmpLocation[0] - icon.getTranslationX()
|
||||
+ ViewState.getFinalTranslationX(icon) + icon.getWidth() * 0.25f;
|
||||
mHostLayout.getLocationOnScreen(mTmpLocation);
|
||||
targetLocation = iconPosition - mTmpLocation[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (needsAnimation) {
|
||||
// We need to add the global animation listener, since once no animations are
|
||||
// running anymore, the panel will instantly hide itself. We need to wait until
|
||||
// the animation is fully finished for this though.
|
||||
changingView.performRemoveAnimation(ANIMATION_DURATION_HEADS_UP_DISAPPEAR
|
||||
+ ANIMATION_DELAY_HEADS_UP, extraDelay, 0.0f,
|
||||
true /* isHeadsUpAppear */, targetLocation, endRunnable,
|
||||
getGlobalAnimationFinishedListener());
|
||||
} else if (endRunnable != null) {
|
||||
endRunnable.run();
|
||||
}
|
||||
}
|
||||
mNewEvents.add(event);
|
||||
|
||||
@@ -640,6 +640,22 @@ public class ViewState {
|
||||
return newDuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the end value of the xTranslation animation running on a view or the xTranslation
|
||||
* if no animation is running.
|
||||
*/
|
||||
public static float getFinalTranslationX(View view) {
|
||||
if (view == null) {
|
||||
return 0;
|
||||
}
|
||||
ValueAnimator xAnimator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_X);
|
||||
if (xAnimator == null) {
|
||||
return view.getTranslationX();
|
||||
} else {
|
||||
return getChildTag(view, TAG_END_TRANSLATION_X);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the end value of the yTranslation animation running on a view or the yTranslation
|
||||
* if no animation is running.
|
||||
|
||||
Reference in New Issue
Block a user