From 5b5beb01dc65c210b27bbb770cd0d7e052d8c743 Mon Sep 17 00:00:00 2001 From: Selim Cinek Date: Tue, 8 Nov 2016 18:11:58 -0800 Subject: [PATCH] Added appear and disappear animations for the shelf icons The icons now animate in and out of the shelf nicer. Also fixed that the regular animation was played when in the shelf. Test: Add notifications, observe animation in statusbar Bug: 32437839 Change-Id: Id003fee1508b8c18a933d38faf93541be21baffd --- .../systemui/statusbar/NotificationShelf.java | 12 +- .../systemui/statusbar/StatusBarIconView.java | 104 +++++++++++------- .../phone/NotificationIconAreaController.java | 11 +- .../phone/NotificationIconContainer.java | 91 +++++++++++++-- .../statusbar/stack/ExpandableViewState.java | 2 +- .../statusbar/stack/StackStateAnimator.java | 2 +- .../systemui/statusbar/stack/ViewState.java | 2 +- 7 files changed, 161 insertions(+), 63 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index de88e6650245f..ada24f37e6d5e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -16,8 +16,6 @@ package com.android.systemui.statusbar; -import static com.android.systemui.statusbar.phone.NotificationIconContainer.IconState; - import android.content.Context; import android.content.res.Configuration; import android.util.AttributeSet; @@ -157,6 +155,7 @@ public class NotificationShelf extends ActivatableNotificationView { mShelfState.shadowAlpha = 1.0f; mShelfState.isBottomClipped = false; mShelfState.hideSensitive = false; + mShelfState.xTranslation = getTranslationX(); if (mNotGoneIndex != -1) { mShelfState.notGoneIndex = Math.min(mShelfState.notGoneIndex, mNotGoneIndex); } @@ -173,7 +172,7 @@ public class NotificationShelf extends ActivatableNotificationView { * the icons from the notification area into the shelf. */ public void updateAppearance() { - WeakHashMap iconStates = + WeakHashMap iconStates = mShelfIcons.resetViewStates(); float numIconsInShelf = 0.0f; int shelfIndex = mAmbientState.getShelfIndex(); @@ -196,7 +195,7 @@ public class NotificationShelf extends ActivatableNotificationView { } ExpandableNotificationRow row = (ExpandableNotificationRow) child; StatusBarIconView icon = row.getEntry().expandedIcon; - IconState iconState = iconStates.get(icon); + NotificationIconContainer.IconState iconState = iconStates.get(icon); float notificationClipEnd; float shelfStart = getTranslationY(); boolean aboveShelf = row.getTranslationZ() > mAmbientState.getBaseZHeight(); @@ -255,8 +254,9 @@ public class NotificationShelf extends ActivatableNotificationView { } } - private void updateIconAppearance(ExpandableNotificationRow row, IconState iconState, - StatusBarIconView icon, float expandAmount) { + private void updateIconAppearance(ExpandableNotificationRow row, + NotificationIconContainer.IconState iconState, StatusBarIconView icon, + float expandAmount) { // Let calculate how much the view is in the shelf float viewStart = row.getTranslationY(); int transformHeight = row.getActualHeight() + mPaddingBetweenElements; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index 543f899283d4b..d635bb06d318c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -19,7 +19,6 @@ package com.android.systemui.statusbar; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; import android.app.Notification; import android.content.Context; import android.content.pm.ApplicationInfo; @@ -68,7 +67,7 @@ public class StatusBarIconView extends AnimatedImageView { return object.getIconAppearAmount(); } }; - private static final Property DOT_APPEAR_AMOUNG + private static final Property DOT_APPEAR_AMOUNT = new FloatProperty("dot_appear_amount") { @Override @@ -440,54 +439,75 @@ public class StatusBarIconView extends AnimatedImageView { mDotPaint.setColor(iconTint); } - public void setVisibleState(int visibleState) { + public void setVisibleState(int state) { + setVisibleState(state, true /* animate */, null /* endRunnable */); + } + + public void setVisibleState(int state, boolean animate) { + setVisibleState(state, animate, null); + } + + @Override + public boolean hasOverlappingRendering() { + return false; + } + + public void setVisibleState(int visibleState, boolean animate, Runnable endRunnable) { if (visibleState != mVisibleState) { mVisibleState = visibleState; - if (mIconAppearAnimator != null) { - mIconAppearAnimator.cancel(); - } - float targetAmount = 0.0f; - Interpolator interpolator = Interpolators.FAST_OUT_LINEAR_IN; - if (visibleState == STATE_ICON) { - targetAmount = 1.0f; - interpolator = Interpolators.LINEAR_OUT_SLOW_IN; - } - mIconAppearAnimator = ObjectAnimator.ofFloat(this, ICON_APPEAR_AMOUNT, - targetAmount); - mIconAppearAnimator.setInterpolator(interpolator); - mIconAppearAnimator.setDuration(100); - mIconAppearAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mIconAppearAnimator = null; + if (animate) { + if (mIconAppearAnimator != null) { + mIconAppearAnimator.cancel(); } - }); - mIconAppearAnimator.start(); + float targetAmount = 0.0f; + Interpolator interpolator = Interpolators.FAST_OUT_LINEAR_IN; + if (visibleState == STATE_ICON) { + targetAmount = 1.0f; + interpolator = Interpolators.LINEAR_OUT_SLOW_IN; + } + mIconAppearAnimator = ObjectAnimator.ofFloat(this, ICON_APPEAR_AMOUNT, + targetAmount); + mIconAppearAnimator.setInterpolator(interpolator); + mIconAppearAnimator.setDuration(100); + mIconAppearAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mIconAppearAnimator = null; + if (endRunnable != null) { + endRunnable.run(); + } + } + }); + mIconAppearAnimator.start(); - if (mDotAnimator != null) { - mDotAnimator.cancel(); - } - targetAmount = visibleState == STATE_ICON ? 2.0f : 0.0f; - interpolator = Interpolators.FAST_OUT_LINEAR_IN; - if (visibleState == STATE_DOT) { - targetAmount = 1.0f; - interpolator = Interpolators.LINEAR_OUT_SLOW_IN; - } - mDotAnimator = ObjectAnimator.ofFloat(this, DOT_APPEAR_AMOUNG, - targetAmount); - mDotAnimator.setInterpolator(interpolator); - mDotAnimator.setDuration(100); - mDotAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - mDotAnimator = null; + if (mDotAnimator != null) { + mDotAnimator.cancel(); } - }); - mDotAnimator.start(); + targetAmount = visibleState == STATE_ICON ? 2.0f : 0.0f; + interpolator = Interpolators.FAST_OUT_LINEAR_IN; + if (visibleState == STATE_DOT) { + targetAmount = 1.0f; + interpolator = Interpolators.LINEAR_OUT_SLOW_IN; + } + mDotAnimator = ObjectAnimator.ofFloat(this, DOT_APPEAR_AMOUNT, + targetAmount); + mDotAnimator.setInterpolator(interpolator); + mDotAnimator.setDuration(100); + mDotAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mDotAnimator = null; + } + }); + mDotAnimator.start(); + } else { + setIconAppearAmount(visibleState == STATE_ICON ? 1.0f : 0.0f); + setDotAppearAmount(visibleState == STATE_DOT ? 1.0f : 0.0f); + } } } - public void setIconAppearAmount(Float iconAppearAmount) { + public void setIconAppearAmount(float iconAppearAmount) { mIconAppearAmount = iconAppearAmount; invalidate(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java index 7b67f31b7a4a5..0dbff19b6bcf4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java @@ -36,7 +36,7 @@ public class NotificationIconAreaController { private PhoneStatusBar mPhoneStatusBar; protected View mNotificationIconArea; private NotificationIconContainer mNotificationIcons; - private NotificationIconContainer mNotificationIconsScroller; + private NotificationIconContainer mShelfIcons; private final Rect mTintArea = new Rect(); private NotificationStackScrollLayout mNotificationScrollLayout; private Context mContext; @@ -65,7 +65,7 @@ public class NotificationIconAreaController { R.id.notificationIcons); NotificationShelf shelf = mPhoneStatusBar.getNotificationShelf(); - mNotificationIconsScroller = shelf.getShelfIcons(); + mShelfIcons = shelf.getShelfIcons(); shelf.setCollapsedIcons(mNotificationIcons); mNotificationScrollLayout = mPhoneStatusBar.getNotificationScrollLayout(); @@ -147,8 +147,7 @@ public class NotificationIconAreaController { public void updateNotificationIcons(NotificationData notificationData) { updateIconsForLayout(notificationData, entry -> entry.icon, mNotificationIcons); - updateIconsForLayout(notificationData, entry -> entry.expandedIcon, - mNotificationIconsScroller); + updateIconsForLayout(notificationData, entry -> entry.expandedIcon, mShelfIcons); applyNotificationIconsTint(); ArrayList activeNotifications @@ -207,11 +206,14 @@ public class NotificationIconAreaController { final LinearLayout.LayoutParams params = generateIconLayoutParams(); for (int i = 0; i < toShow.size(); i++) { View v = toShow.get(i); + // The view might still be transiently added if it was just removed and added again + hostLayout.removeTransientView(v); if (v.getParent() == null) { hostLayout.addView(v, i, params); } } + hostLayout.setChangingViewPositions(true); // Re-sort notification icons final int childCount = hostLayout.getChildCount(); for (int i = 0; i < childCount; i++) { @@ -223,6 +225,7 @@ public class NotificationIconAreaController { hostLayout.removeView(expected); hostLayout.addView(expected, i); } + hostLayout.setChangingViewPositions(false); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java index 0f41e31d9b366..28958908f81bc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -40,13 +40,23 @@ import java.util.WeakHashMap; public class NotificationIconContainer extends AlphaOptimizedFrameLayout { private static final String TAG = "NotificationIconContainer"; private static final boolean DEBUG = false; - private static final AnimationFilter DOT_ANIMATION_FILTER = new AnimationFilter().animateX(); private static final AnimationProperties DOT_ANIMATION_PROPERTIES = new AnimationProperties() { + private AnimationFilter mAnimationFilter = new AnimationFilter().animateX(); + @Override public AnimationFilter getAnimationFilter() { - return DOT_ANIMATION_FILTER; + return mAnimationFilter; } }.setDuration(200); + + private static final AnimationProperties ADD_ICON_PROPERTIES = new AnimationProperties() { + private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha(); + + @Override + public AnimationFilter getAnimationFilter() { + return mAnimationFilter; + } + }.setDuration(200).setDelay(50); private boolean mShowAllIcons = true; private WeakHashMap mIconStates = new WeakHashMap<>(); @@ -55,6 +65,8 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { private int mActualLayoutWidth = -1; private float mActualPaddingEnd = -1; private float mActualPaddingStart = -1; + private boolean mChangingViewPositions; + private int mAnimationStartIndex = -1; public NotificationIconContainer(Context context, AttributeSet attrs) { super(context, attrs); @@ -109,18 +121,60 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { childState.applyToView(child); } } + mAnimationStartIndex = -1; } @Override public void onViewAdded(View child) { super.onViewAdded(child); - mIconStates.put(child, new IconState()); + if (!mChangingViewPositions) { + mIconStates.put(child, new IconState()); + } + int childIndex = indexOfChild(child); + if (childIndex < getChildCount() - 1 + && mIconStates.get(getChildAt(childIndex + 1)).iconAppearAmount > 0.0f) { + if (mAnimationStartIndex < 0) { + mAnimationStartIndex = childIndex; + } else { + mAnimationStartIndex = Math.min(mAnimationStartIndex, childIndex); + } + } } @Override public void onViewRemoved(View child) { super.onViewRemoved(child); - mIconStates.remove(child); + if (child instanceof StatusBarIconView) { + final StatusBarIconView icon = (StatusBarIconView) child; + if (icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN + && child.getVisibility() == VISIBLE) { + int animationStartIndex = findFirstViewIndexAfter(icon.getTranslationX()); + if (mAnimationStartIndex < 0) { + mAnimationStartIndex = animationStartIndex; + } else { + mAnimationStartIndex = Math.min(mAnimationStartIndex, animationStartIndex); + } + } + if (!mChangingViewPositions) { + mIconStates.remove(child); + addTransientView(icon, 0); + icon.setVisibleState(StatusBarIconView.STATE_HIDDEN, true /* animate */, + () -> removeTransientView(icon)); + } + } + } + + /** + * Finds the first view with a translation bigger then a given value + */ + private int findFirstViewIndexAfter(float translationX) { + for (int i = 0; i < getChildCount(); i++) { + View view = getChildAt(i); + if (view.getTranslationX() > translationX) { + return i; + } + } + return getChildCount(); } public WeakHashMap resetViewStates() { @@ -128,6 +182,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { View view = getChildAt(i); ViewState iconState = mIconStates.get(view); iconState.initFrom(view); + iconState.alpha = 1.0f; } return mIconStates; } @@ -246,21 +301,41 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { return mActualLayoutWidth; } - public static class IconState extends ViewState { + public void setChangingViewPositions(boolean changingViewPositions) { + mChangingViewPositions = changingViewPositions; + } + + public class IconState extends ViewState { public float iconAppearAmount = 1.0f; public int visibleState; + public boolean justAdded = true; @Override public void applyToView(View view) { if (view instanceof StatusBarIconView) { StatusBarIconView icon = (StatusBarIconView) view; - if (visibleState != icon.getVisibleState()) { - icon.setVisibleState(visibleState); - animateTo(icon, DOT_ANIMATION_PROPERTIES); + AnimationProperties animationProperties = DOT_ANIMATION_PROPERTIES; + if (justAdded) { + super.applyToView(icon); + icon.setAlpha(0.0f); + icon.setVisibleState(StatusBarIconView.STATE_HIDDEN, false /* animate */); + animationProperties = ADD_ICON_PROPERTIES; + } + boolean animate = visibleState != icon.getVisibleState() || justAdded; + if (!animate && mAnimationStartIndex >= 0 + && (icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN + || visibleState != StatusBarIconView.STATE_HIDDEN)) { + int viewIndex = indexOfChild(view); + animate = viewIndex >= mAnimationStartIndex; + } + icon.setVisibleState(visibleState); + if (animate) { + animateTo(icon, animationProperties); } else { super.applyToView(view); } } + justAdded = false; } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java index 7c719d8f3fd94..bcd278d762b24 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ExpandableViewState.java @@ -221,7 +221,7 @@ public class ExpandableViewState extends ViewState { // start dark animation expandableView.setDark(this.dark, animationFilter.animateDark, properties.delay); - if (properties.wasAdded(child)) { + if (properties.wasAdded(child) && !hidden) { expandableView.performAddAnimation(properties.delay, properties.duration); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java index 342d307954549..1f29b4fd7d917 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java @@ -372,7 +372,7 @@ public class StackStateAnimator { } else if (event.animationType == NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE) { - if (changingView.getVisibility() == View.GONE) { + if (changingView.getVisibility() != View.VISIBLE) { removeFromOverlay(changingView); continue; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java index 124cabb0770f9..8a5ddd43b24cd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java @@ -210,7 +210,7 @@ public class ViewState { } // start alpha animation - if (alphaChanging && child.getTranslationX() == 0) { + if (alphaChanging) { startAlphaAnimation(child, animationProperties); } else { abortAnimation(child, TAG_ANIMATOR_ALPHA);