From 2b549f4bd436787bd2543812b24add2d8c5b53ee Mon Sep 17 00:00:00 2001 From: Selim Cinek Date: Tue, 22 Nov 2016 16:38:51 -0800 Subject: [PATCH 01/11] Added possibility to use canned animation for icons Icons now move in a canned animation from the shelf position to the icon. Test: adb shell setprop debug.icon_opening_animations true && adb shell killall com.android.systemui Bug: 32437839 Change-Id: I82b6de37ac7a179aeb5d16bd663d566c2f338b1a --- .../statusbar/ExpandableNotificationRow.java | 36 +++-- .../systemui/statusbar/NotificationData.java | 6 + .../systemui/statusbar/NotificationShelf.java | 150 ++++++++++++------ .../systemui/statusbar/StatusBarIconView.java | 18 +++ .../phone/NotificationIconContainer.java | 92 +++++++++-- .../statusbar/stack/AnimationFilter.java | 4 +- .../stack/NotificationChildrenContainer.java | 2 +- .../systemui/statusbar/stack/ViewState.java | 84 ++++++---- 8 files changed, 280 insertions(+), 112 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index 661cc3ca2b4ae..e4654e608d092 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -193,7 +193,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { private View mChildAfterViewWhenDismissed; private View mGroupParentWhenDismissed; private boolean mRefocusOnDismiss; - private float mIconTransformationAmount; + private float mContentTransformationAmount; private boolean mIconsVisible = true; private boolean mAboveShelf; private boolean mIsLastChild; @@ -837,23 +837,29 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { /** * Set how much this notification is transformed into an icon. * - * @param iconTransformationAmount A value from 0 to 1 indicating how much we are transformed - * to an icon + * @param contentTransformationAmount A value from 0 to 1 indicating how much we are transformed + * to the content away * @param isLastChild is this the last child in the list. If true, then the transformation is * different since it's content fades out. */ - public void setIconTransformationAmount(float iconTransformationAmount, boolean isLastChild) { + public void setContentTransformationAmount(float contentTransformationAmount, + boolean isLastChild) { boolean changeTransformation = isLastChild != mIsLastChild; - changeTransformation |= mIconTransformationAmount != iconTransformationAmount; + changeTransformation |= mContentTransformationAmount != contentTransformationAmount; mIsLastChild = isLastChild; - mIconTransformationAmount = iconTransformationAmount; + mContentTransformationAmount = contentTransformationAmount; if (changeTransformation) { updateContentTransformation(); - boolean iconsVisible = mIconTransformationAmount == 0.0f; - if (iconsVisible != mIconsVisible) { - mIconsVisible = iconsVisible; - updateIconVisibilities(); - } + } + } + + /** + * Set the icons to be visible of this notification. + */ + public void setIconsVisible(boolean iconsVisible) { + if (iconsVisible != mIconsVisible) { + mIconsVisible = iconsVisible; + updateIconVisibilities(); } } @@ -864,9 +870,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { private void updateContentTransformation() { float contentAlpha; - float translationY = - mIconTransformationAmount * mIconTransformContentShift; + float translationY = -mContentTransformationAmount * mIconTransformContentShift; if (mIsLastChild) { - contentAlpha = 1.0f - mIconTransformationAmount; + contentAlpha = 1.0f - mContentTransformationAmount; contentAlpha = Math.min(contentAlpha / 0.5f, 1.0f); contentAlpha = Interpolators.ALPHA_OUT.getInterpolation(contentAlpha); translationY *= 0.4f; @@ -1871,8 +1877,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { } @Override - protected void onYTranslationAnimationFinished() { - super.onYTranslationAnimationFinished(); + protected void onYTranslationAnimationFinished(View view) { + super.onYTranslationAnimationFinished(view); if (mHeadsupDisappearRunning) { setHeadsUpAnimatingAway(false); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java index 3687f6d3d84e6..32f8ebbbd5393 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java @@ -208,6 +208,12 @@ public class NotificationData { expandedIcon = null; throw new IconException("Couldn't create icon: " + ic); } + expandedIcon.setOnVisibilityChangedListener( + newVisibility -> { + if (row != null) { + row.setIconsVisible(newVisibility != View.VISIBLE); + } + }); } public void setIconTag(int key, Object tag) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 680562aa670b0..3423e3bcd7aca 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -18,7 +18,10 @@ package com.android.systemui.statusbar; import android.content.Context; import android.content.res.Configuration; +import android.os.SystemProperties; import android.util.AttributeSet; +import android.util.FloatProperty; +import android.util.Property; import android.view.View; import android.view.ViewGroup; @@ -34,7 +37,6 @@ import com.android.systemui.statusbar.stack.ExpandableViewState; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.stack.StackScrollState; -import java.util.ArrayList; import java.util.WeakHashMap; /** @@ -43,10 +45,11 @@ import java.util.WeakHashMap; */ public class NotificationShelf extends ActivatableNotificationView { + private static final boolean USE_ANIMATIONS_WHEN_OPENING = + SystemProperties.getBoolean("debug.icon_opening_animations", true); private ViewInvertHelper mViewInvertHelper; private boolean mDark; private NotificationIconContainer mShelfIcons; - private ArrayList mIcons = new ArrayList<>(); private ShelfState mShelfState; private int[] mTmp = new int[2]; private boolean mHideBackground; @@ -204,8 +207,6 @@ public class NotificationShelf extends ActivatableNotificationView { continue; } ExpandableNotificationRow row = (ExpandableNotificationRow) child; - StatusBarIconView icon = row.getEntry().expandedIcon; - NotificationIconContainer.IconState iconState = iconStates.get(icon); float notificationClipEnd; float shelfStart = getTranslationY(); boolean aboveShelf = row.getTranslationZ() > mAmbientState.getBaseZHeight(); @@ -223,8 +224,7 @@ public class NotificationShelf extends ActivatableNotificationView { } } updateNotificationClipHeight(row, notificationClipEnd); - float inShelfAmount = updateIconAppearance(row, iconState, icon, expandAmount, - isLastChild); + float inShelfAmount = updateIconAppearance(row, expandAmount, isLastChild); numViewsInShelf += inShelfAmount; int ownColorUntinted = row.getBackgroundColorWithoutTint(); if (row.getTranslationY() >= getTranslationY() && mNotGoneIndex == -1) { @@ -250,7 +250,7 @@ public class NotificationShelf extends ActivatableNotificationView { } mShelfIcons.calculateIconTranslations(); mShelfIcons.applyIconStates(); - setVisibility(numViewsInShelf != 0.0f && mAmbientState.isShadeExpanded() + setVisibility(mAmbientState.isShadeExpanded() ? VISIBLE : INVISIBLE); boolean hideBackground = numViewsInShelf < 1.0f; @@ -275,41 +275,103 @@ public class NotificationShelf extends ActivatableNotificationView { /** * @return the icon amount how much this notification is in the shelf; */ - private float updateIconAppearance(ExpandableNotificationRow row, - NotificationIconContainer.IconState iconState, StatusBarIconView icon, - float expandAmount, boolean isLastChild) { + private float updateIconAppearance(ExpandableNotificationRow row, float expandAmount, + boolean isLastChild) { // Let calculate how much the view is in the shelf float viewStart = row.getTranslationY(); - int transformHeight = row.getActualHeight() + mPaddingBetweenElements; + int fullHeight = row.getActualHeight() + mPaddingBetweenElements; + float iconTransformDistance = getIntrinsicHeight() * 1.5f; if (isLastChild) { - transformHeight = - Math.min(transformHeight, row.getMinHeight() - getIntrinsicHeight()); + fullHeight = Math.min(fullHeight, row.getMinHeight() - getIntrinsicHeight()); + iconTransformDistance = Math.min(iconTransformDistance, row.getMinHeight() + - getIntrinsicHeight()); } - float viewEnd = viewStart + transformHeight; - float iconAppearAmount; - float yTranslation; - float alpha = 1.0f; + float viewEnd = viewStart + fullHeight; + float fullTransitionAmount; + float iconTransitonAmount; if (viewEnd >= getTranslationY() && (mAmbientState.isShadeExpanded() || (!row.isPinned() && !row.isHeadsUpAnimatingAway()))) { if (viewStart < getTranslationY()) { - float linearAmount = (getTranslationY() - viewStart) / transformHeight; + + float fullAmount = (getTranslationY() - viewStart) / fullHeight; float interpolatedAmount = Interpolators.ACCELERATE_DECELERATE.getInterpolation( - linearAmount); + fullAmount); interpolatedAmount = NotificationUtils.interpolate( - interpolatedAmount, linearAmount, expandAmount); - iconAppearAmount = 1.0f - interpolatedAmount; + interpolatedAmount, fullAmount, expandAmount); + fullTransitionAmount = 1.0f - interpolatedAmount; + + iconTransitonAmount = (getTranslationY() - viewStart) / iconTransformDistance; + iconTransitonAmount = Math.min(1.0f, iconTransitonAmount); + iconTransitonAmount = 1.0f - iconTransitonAmount; + } else { - iconAppearAmount = 1.0f; + fullTransitionAmount = 1.0f; + iconTransitonAmount = 1.0f; } } else { - iconAppearAmount = 0.0f; + fullTransitionAmount = 0.0f; + iconTransitonAmount = 0.0f; } + row.setContentTransformationAmount(iconTransitonAmount, isLastChild); + updateIconPositioning(row, iconTransitonAmount, fullTransitionAmount); + return fullTransitionAmount; + } + + private void updateIconPositioning(ExpandableNotificationRow row, float iconTransitionAmount, + float fullTransitionAmount) { + StatusBarIconView icon = row.getEntry().expandedIcon; + NotificationIconContainer.IconState iconState = getIconState(icon); + if (iconState == null) { + return; + } + float clampedAmount = iconTransitionAmount > 0.5f ? 1.0f : 0.0f; + boolean isLastChild = isLastChild(row); + if (clampedAmount == iconTransitionAmount) { + iconState.keepClampedPosition = false; + } + if (clampedAmount == fullTransitionAmount) { + iconState.useFullTransitionAmount = fullTransitionAmount == 0.0f; + } + float transitionAmount; + boolean needCannedAnimation = iconState.clampedAppearAmount == 1.0f + && clampedAmount == 0.0f; + if (isLastChild || !USE_ANIMATIONS_WHEN_OPENING || iconState.useFullTransitionAmount) { + transitionAmount = iconTransitionAmount; + } else if (iconState.keepClampedPosition + && iconState.clampedAppearAmount != clampedAmount) { + // We animated to the clamped amount but then decided to go the other way. Let's + // animate it to the new position + transitionAmount = iconTransitionAmount; + iconState.needsCannedAnimation = true; + iconState.keepClampedPosition = false; + } else if (needCannedAnimation || iconState.keepClampedPosition + || iconState.iconAppearAmount == 1.0f) { + // We need to perform a canned animation since we crossed the treshhold + transitionAmount = clampedAmount; + iconState.keepClampedPosition = iconState.keepClampedPosition || needCannedAnimation; + iconState.needsCannedAnimation = needCannedAnimation; + } else { + transitionAmount = iconTransitionAmount; + } + iconState.iconAppearAmount = !USE_ANIMATIONS_WHEN_OPENING + || iconState.useFullTransitionAmount + ? fullTransitionAmount + : transitionAmount; + iconState.clampedAppearAmount = clampedAmount; + setIconTransformationAmount(row, transitionAmount); + } + + private boolean isLastChild(ExpandableNotificationRow row) { + return row == mAmbientState.getLastVisibleBackgroundChild(); + } + + private void setIconTransformationAmount(ExpandableNotificationRow row, + float transitionAmount) { + StatusBarIconView icon = row.getEntry().expandedIcon; + NotificationIconContainer.IconState iconState = getIconState(icon); - // Lets now calculate how much of the transformation has already happened. This is different - // from the above, since we only start transforming when the view is already quite a bit - // pushed in. View rowIcon = row.getNotificationIcon(); - float notificationIconPosition = viewStart; + float notificationIconPosition = row.getTranslationY(); float notificationIconSize = 0.0f; int iconTopPadding; if (rowIcon != null) { @@ -322,28 +384,18 @@ public class NotificationShelf extends ActivatableNotificationView { float shelfIconPosition = getTranslationY() + icon.getTop(); shelfIconPosition += ((1.0f - icon.getIconScale()) * icon.getHeight()) / 2.0f; float transitionDistance = getIntrinsicHeight() * 1.5f; - if (isLastChild) { + if (row == mAmbientState.getLastVisibleBackgroundChild()) { transitionDistance = Math.min(transitionDistance, row.getMinHeight() - getIntrinsicHeight()); } float transformationStartPosition = getTranslationY() - transitionDistance; - float transitionAmount = 0.0f; - if (viewStart < transformationStartPosition - || (!mAmbientState.isShadeExpanded() - && (row.isPinned() || row.isHeadsUpAnimatingAway()))) { - // We simply place it on the icon of the notification - yTranslation = notificationIconPosition - shelfIconPosition; - } else { - transitionAmount = (viewStart - transformationStartPosition) - / transitionDistance; - float startPosition = transformationStartPosition + iconTopPadding; - yTranslation = NotificationUtils.interpolate( - startPosition - shelfIconPosition, 0, transitionAmount); - // If we are merging into the shelf, lets make sure the shelf is at least on our height, - // otherwise the icons won't be visible. - setTranslationZ(Math.max(getTranslationZ(), row.getTranslationZ())); - } + float iconYTranslation = NotificationUtils.interpolate( + Math.min(notificationIconPosition, transformationStartPosition + iconTopPadding) + - shelfIconPosition, + 0, + transitionAmount); float shelfIconSize = icon.getHeight() * icon.getIconScale(); + float alpha = 1.0f; if (!row.isShowingIcon()) { // The view currently doesn't have an icon, lets transform it in! alpha = transitionAmount; @@ -352,15 +404,12 @@ public class NotificationShelf extends ActivatableNotificationView { // The notification size is different from the size in the shelf / statusbar float newSize = NotificationUtils.interpolate(notificationIconSize, shelfIconSize, transitionAmount); - row.setIconTransformationAmount(transitionAmount, isLastChild); if (iconState != null) { iconState.scaleX = newSize / icon.getHeight() / icon.getIconScale(); iconState.scaleY = iconState.scaleX; iconState.hidden = transitionAmount == 0.0f; - iconState.iconAppearAmount = iconAppearAmount; iconState.alpha = alpha; - iconState.yTranslation = yTranslation; - icon.setVisibility(transitionAmount == 0.0f ? INVISIBLE : VISIBLE); + iconState.yTranslation = iconYTranslation; if (row.isInShelf() && !row.isTransformingIntoShelf()) { iconState.iconAppearAmount = 1.0f; iconState.alpha = 1.0f; @@ -369,7 +418,10 @@ public class NotificationShelf extends ActivatableNotificationView { iconState.hidden = false; } } - return iconAppearAmount; + } + + private NotificationIconContainer.IconState getIconState(StatusBarIconView icon) { + return mShelfIcons.getIconState(icon); } private float getFullyClosedTranslation() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index d635bb06d318c..2621e4a50931f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -37,6 +37,7 @@ import android.util.FloatProperty; import android.util.Log; import android.util.Property; import android.util.TypedValue; +import android.view.View; import android.view.ViewDebug; import android.view.accessibility.AccessibilityEvent; import android.view.animation.Interpolator; @@ -102,6 +103,7 @@ public class StatusBarIconView extends AnimatedImageView { private ObjectAnimator mIconAppearAnimator; private ObjectAnimator mDotAnimator; private float mDotAppearAmount; + private OnVisibilityChangedListener mOnVisibilityChangedListener; public StatusBarIconView(Context context, String slot, Notification notification) { this(context, slot, notification, false); @@ -525,7 +527,23 @@ public class StatusBarIconView extends AnimatedImageView { invalidate(); } + @Override + public void setVisibility(int visibility) { + super.setVisibility(visibility); + if (mOnVisibilityChangedListener != null) { + mOnVisibilityChangedListener.onVisibilityChanged(visibility); + } + } + public float getDotAppearAmount() { return mDotAppearAmount; } + + public void setOnVisibilityChangedListener(OnVisibilityChangedListener listener) { + mOnVisibilityChangedListener = listener; + } + + public interface OnVisibilityChangedListener { + void onVisibilityChanged(int newVisibility); + } } 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 03697b86e31e0..160b233847402 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -40,6 +40,7 @@ import java.util.WeakHashMap; public class NotificationIconContainer extends AlphaOptimizedFrameLayout { private static final String TAG = "NotificationIconContainer"; private static final boolean DEBUG = false; + private static final int CANNED_ANIMATION_DURATION = 100; private static final AnimationProperties DOT_ANIMATION_PROPERTIES = new AnimationProperties() { private AnimationFilter mAnimationFilter = new AnimationFilter().animateX(); @@ -49,6 +50,26 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { } }.setDuration(200); + private static final AnimationProperties ICON_ANIMATION_PROPERTIES = new AnimationProperties() { + private AnimationFilter mAnimationFilter = new AnimationFilter().animateY().animateAlpha(); + // TODO: add scale + + @Override + public AnimationFilter getAnimationFilter() { + return mAnimationFilter; + } + }.setDuration(CANNED_ANIMATION_DURATION); + + private static final AnimationProperties mTempProperties = new AnimationProperties() { + private AnimationFilter mAnimationFilter = new AnimationFilter(); + // TODO: add scale + + @Override + public AnimationFilter getAnimationFilter() { + return mAnimationFilter; + } + }.setDuration(CANNED_ANIMATION_DURATION); + private static final AnimationProperties ADD_ICON_PROPERTIES = new AnimationProperties() { private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha(); @@ -66,7 +87,8 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { private float mActualPaddingEnd = -1; private float mActualPaddingStart = -1; private boolean mChangingViewPositions; - private int mAnimationStartIndex = -1; + private int mAddAnimationStartIndex = -1; + private int mCannedAnimationStartIndex = -1; public NotificationIconContainer(Context context, AttributeSet attrs) { super(context, attrs); @@ -121,7 +143,8 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { childState.applyToView(child); } } - mAnimationStartIndex = -1; + mAddAnimationStartIndex = -1; + mCannedAnimationStartIndex = -1; } @Override @@ -133,10 +156,10 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { int childIndex = indexOfChild(child); if (childIndex < getChildCount() - 1 && mIconStates.get(getChildAt(childIndex + 1)).iconAppearAmount > 0.0f) { - if (mAnimationStartIndex < 0) { - mAnimationStartIndex = childIndex; + if (mAddAnimationStartIndex < 0) { + mAddAnimationStartIndex = childIndex; } else { - mAnimationStartIndex = Math.min(mAnimationStartIndex, childIndex); + mAddAnimationStartIndex = Math.min(mAddAnimationStartIndex, childIndex); } } } @@ -149,10 +172,10 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { if (icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN && child.getVisibility() == VISIBLE) { int animationStartIndex = findFirstViewIndexAfter(icon.getTranslationX()); - if (mAnimationStartIndex < 0) { - mAnimationStartIndex = animationStartIndex; + if (mAddAnimationStartIndex < 0) { + mAddAnimationStartIndex = animationStartIndex; } else { - mAnimationStartIndex = Math.min(mAnimationStartIndex, animationStartIndex); + mAddAnimationStartIndex = Math.min(mAddAnimationStartIndex, animationStartIndex); } } if (!mChangingViewPositions) { @@ -305,28 +328,64 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { mChangingViewPositions = changingViewPositions; } + public IconState getIconState(StatusBarIconView icon) { + return mIconStates.get(icon); + } + public class IconState extends ViewState { public float iconAppearAmount = 1.0f; + public float clampedAppearAmount = 1.0f; public int visibleState; public boolean justAdded = true; + public boolean needsCannedAnimation; + public boolean keepClampedPosition; + public boolean useFullTransitionAmount; @Override public void applyToView(View view) { if (view instanceof StatusBarIconView) { StatusBarIconView icon = (StatusBarIconView) view; - AnimationProperties animationProperties = DOT_ANIMATION_PROPERTIES; + boolean animate = false; + AnimationProperties animationProperties = null; if (justAdded) { super.applyToView(icon); icon.setAlpha(0.0f); icon.setVisibleState(StatusBarIconView.STATE_HIDDEN, false /* animate */); animationProperties = ADD_ICON_PROPERTIES; + animate = true; + } else if (visibleState != icon.getVisibleState()) { + animationProperties = DOT_ANIMATION_PROPERTIES; + animate = true; } - boolean animate = visibleState != icon.getVisibleState() || justAdded; - if (!animate && mAnimationStartIndex >= 0 + if (!animate && mAddAnimationStartIndex >= 0 + && indexOfChild(view) >= mAddAnimationStartIndex && (icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN || visibleState != StatusBarIconView.STATE_HIDDEN)) { - int viewIndex = indexOfChild(view); - animate = viewIndex >= mAnimationStartIndex; + animationProperties = DOT_ANIMATION_PROPERTIES; + animate = true; + } + if (needsCannedAnimation) { + AnimationFilter animationFilter = mTempProperties.getAnimationFilter(); + animationFilter.reset(); + animationFilter.combineFilter(ICON_ANIMATION_PROPERTIES.getAnimationFilter()); + if (animationProperties != null) { + animationFilter.combineFilter(animationProperties.getAnimationFilter()); + } + animationProperties = mTempProperties; + animationProperties.setDuration(CANNED_ANIMATION_DURATION); + animate = true; + mCannedAnimationStartIndex = indexOfChild(view); + } + if (!animate && mCannedAnimationStartIndex >= 0 + && indexOfChild(view) > mCannedAnimationStartIndex + && (icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN + || visibleState != StatusBarIconView.STATE_HIDDEN)) { + AnimationFilter animationFilter = mTempProperties.getAnimationFilter(); + animationFilter.reset(); + animationFilter.animateX(); + animationProperties = mTempProperties; + animationProperties.setDuration(CANNED_ANIMATION_DURATION); + animate = true; } icon.setVisibleState(visibleState); if (animate) { @@ -336,6 +395,13 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { } } justAdded = false; + needsCannedAnimation = false; + } + + protected void onYTranslationAnimationFinished(View view) { + if (hidden) { + view.setVisibility(INVISIBLE); + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java index d3d58f90c9e68..38bb40eb4a10c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java @@ -120,7 +120,7 @@ public class AnimationFilter { } } - private void combineFilter(AnimationFilter filter) { + public void combineFilter(AnimationFilter filter) { animateAlpha |= filter.animateAlpha; animateX |= filter.animateX; animateY |= filter.animateY; @@ -134,7 +134,7 @@ public class AnimationFilter { hasDelays |= filter.hasDelays; } - private void reset() { + public void reset() { animateAlpha = false; animateX = false; animateY = false; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java index b8f8cb2b5bc91..22709f645abbf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java @@ -213,7 +213,7 @@ public class NotificationChildrenContainer extends ViewGroup { mDividers.add(newIndex, divider); updateGroupOverflow(); - row.setIconTransformationAmount(0, false /* isLastChild */); + row.setContentTransformationAmount(0, false /* isLastChild */); } public void removeNotification(ExpandableNotificationRow row) { 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 8a5ddd43b24cd..a8e5ac63392ed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java @@ -99,36 +99,6 @@ public class ViewState { // don't do anything with it return; } - boolean becomesInvisible = this.alpha == 0.0f || this.hidden; - boolean animatingAlpha = isAnimating(view, TAG_ANIMATOR_ALPHA); - if (animatingAlpha) { - updateAlphaAnimation(view); - } else if (view.getAlpha() != this.alpha) { - // apply layer type - boolean becomesFullyVisible = this.alpha == 1.0f; - boolean newLayerTypeIsHardware = !becomesInvisible && !becomesFullyVisible - && view.hasOverlappingRendering(); - int layerType = view.getLayerType(); - int newLayerType = newLayerTypeIsHardware - ? View.LAYER_TYPE_HARDWARE - : View.LAYER_TYPE_NONE; - if (layerType != newLayerType) { - view.setLayerType(newLayerType, null); - } - - // apply alpha - view.setAlpha(this.alpha); - } - - // apply visibility - int oldVisibility = view.getVisibility(); - int newVisibility = becomesInvisible ? View.INVISIBLE : View.VISIBLE; - if (newVisibility != oldVisibility) { - if (!(view instanceof ExpandableView) || !((ExpandableView) view).willBeGone()) { - // We don't want views to change visibility when they are animating to GONE - view.setVisibility(newVisibility); - } - } // apply xTranslation boolean animatingX = isAnimating(view, TAG_ANIMATOR_TRANSLATION_X); @@ -163,6 +133,53 @@ public class ViewState { if (view.getScaleY() != this.scaleY) { view.setScaleY(this.scaleY); } + + boolean becomesInvisible = this.alpha == 0.0f || (this.hidden && !isAnimating(view)); + boolean animatingAlpha = isAnimating(view, TAG_ANIMATOR_ALPHA); + if (animatingAlpha) { + updateAlphaAnimation(view); + } else if (view.getAlpha() != this.alpha) { + // apply layer type + boolean becomesFullyVisible = this.alpha == 1.0f; + boolean newLayerTypeIsHardware = !becomesInvisible && !becomesFullyVisible + && view.hasOverlappingRendering(); + int layerType = view.getLayerType(); + int newLayerType = newLayerTypeIsHardware + ? View.LAYER_TYPE_HARDWARE + : View.LAYER_TYPE_NONE; + if (layerType != newLayerType) { + view.setLayerType(newLayerType, null); + } + + // apply alpha + view.setAlpha(this.alpha); + } + + // apply visibility + int oldVisibility = view.getVisibility(); + int newVisibility = becomesInvisible ? View.INVISIBLE : View.VISIBLE; + if (newVisibility != oldVisibility) { + if (!(view instanceof ExpandableView) || !((ExpandableView) view).willBeGone()) { + // We don't want views to change visibility when they are animating to GONE + view.setVisibility(newVisibility); + } + } + } + + protected boolean isAnimating(View view) { + if (isAnimating(view, TAG_ANIMATOR_TRANSLATION_X)) { + return true; + } + if (isAnimating(view, TAG_ANIMATOR_TRANSLATION_Y)) { + return true; + } + if (isAnimating(view, TAG_ANIMATOR_TRANSLATION_Z)) { + return true; + } + if (isAnimating(view, TAG_ANIMATOR_ALPHA)) { + return true; + } + return false; } private boolean isAnimating(View view, int tag) { @@ -482,7 +499,7 @@ public class ViewState { child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null); child.setTag(TAG_START_TRANSLATION_Y, null); child.setTag(TAG_END_TRANSLATION_Y, null); - onYTranslationAnimationFinished(); + onYTranslationAnimationFinished(child); } }); startAnimator(animator, listener); @@ -491,7 +508,10 @@ public class ViewState { child.setTag(TAG_END_TRANSLATION_Y, newEndValue); } - protected void onYTranslationAnimationFinished() { + protected void onYTranslationAnimationFinished(View view) { + if (hidden) { + view.setVisibility(View.INVISIBLE); + } } protected void startAnimator(Animator animator, AnimatorListenerAdapter listener) { From 5ea19572afe9955555f73a24f0b18f7eb5727bdf Mon Sep 17 00:00:00 2001 From: Selim Cinek Date: Tue, 29 Nov 2016 15:34:48 -0800 Subject: [PATCH 02/11] Fixed a bug where heads up notifications had no icon Test: add heads-up notification Bug: 32437839 Change-Id: I44b81a5f16d7d9d12d51b94f7fc4b000878acbd7 --- .../src/com/android/systemui/statusbar/NotificationShelf.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 3423e3bcd7aca..0138ca86c3d3f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -417,6 +417,9 @@ public class NotificationShelf extends ActivatableNotificationView { iconState.scaleY = 1.0f; iconState.hidden = false; } + if (row.isAboveShelf()) { + iconState.hidden = true; + } } } From 65d418ec8957fb1af03a8cc68aed90744a5848ff Mon Sep 17 00:00:00 2001 From: Selim Cinek Date: Tue, 29 Nov 2016 15:42:34 -0800 Subject: [PATCH 03/11] Improved the performance of the notification shelf The shelf had a few inefficiencies that were adding up when calculating the positions. Test: runtest systemui-jank -c android.platform.systemui.tests.jank.SystemUiJankTests -m testNotificationListPull_manyNotifications Bug: 32437839 Change-Id: Iac08a7c364a924f1d0c14258461383b431f0542b --- .../ActivatableNotificationView.java | 22 +++--- .../statusbar/ExpandableNotificationRow.java | 14 ++-- .../systemui/statusbar/NotificationShelf.java | 33 +++++---- .../systemui/statusbar/StatusBarIconView.java | 68 ++++++++++++------- .../phone/NotificationIconContainer.java | 7 +- .../stack/NotificationStackScrollLayout.java | 1 - 6 files changed, 85 insertions(+), 60 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java index 173f160a70d71..d4b478a874516 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java @@ -103,7 +103,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView private boolean mDimmed; private boolean mDark; - private int mBgTint = 0; + private int mBgTint = NO_COLOR; private float mBgAlpha = 1f; /** @@ -481,8 +481,10 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView * Sets the tint color of the background */ public void setTintColor(int color, boolean animated) { - mBgTint = color; - updateBackgroundTint(animated); + if (color != mBgTint) { + mBgTint = color; + updateBackgroundTint(animated); + } } /** @@ -541,13 +543,15 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView } private void setBackgroundTintColor(int color) { - mCurrentBackgroundTint = color; - if (color == mNormalColor) { - // We don't need to tint a normal notification - color = 0; + if (color != mCurrentBackgroundTint) { + mCurrentBackgroundTint = color; + if (color == mNormalColor) { + // We don't need to tint a normal notification + color = 0; + } + mBackgroundDimmed.setTint(color); + mBackgroundNormal.setTint(color); } - mBackgroundDimmed.setTint(color); - mBackgroundNormal.setTint(color); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index e4654e608d092..996e2ef587f05 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -1685,13 +1685,17 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { @Override public void setClipBottomAmount(int clipBottomAmount) { - super.setClipBottomAmount(clipBottomAmount); - mPrivateLayout.setClipBottomAmount(clipBottomAmount); - mPublicLayout.setClipBottomAmount(clipBottomAmount); - if (mGuts != null) { - mGuts.setClipBottomAmount(clipBottomAmount); + if (clipBottomAmount != mClipBottomAmount) { + super.setClipBottomAmount(clipBottomAmount); + mPrivateLayout.setClipBottomAmount(clipBottomAmount); + mPublicLayout.setClipBottomAmount(clipBottomAmount); + if (mGuts != null) { + mGuts.setClipBottomAmount(clipBottomAmount); + } } if (mChildrenContainer != null) { + // We have to update this even if it hasn't changed, since the children locations can + // have changed mChildrenContainer.setClipBottomAmount(clipBottomAmount); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 0138ca86c3d3f..80d41883066ec 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -37,8 +37,6 @@ import com.android.systemui.statusbar.stack.ExpandableViewState; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.stack.StackScrollState; -import java.util.WeakHashMap; - /** * A notification shelf view that is placed inside the notification scroller. It manages the * overflow icons that don't fit into the regular list anymore. @@ -165,6 +163,7 @@ public class NotificationShelf extends ActivatableNotificationView { mShelfState.notGoneIndex = Math.min(mShelfState.notGoneIndex, mNotGoneIndex); } mShelfState.hasItemsInStableShelf = lastViewState.inShelf; + mShelfState.hidden = !mAmbientState.isShadeExpanded(); } else { mShelfState.hidden = true; mShelfState.location = ExpandableViewState.LOCATION_GONE; @@ -177,15 +176,15 @@ public class NotificationShelf extends ActivatableNotificationView { * the icons from the notification area into the shelf. */ public void updateAppearance() { - WeakHashMap iconStates = - mShelfIcons.resetViewStates(); + mShelfIcons.resetViewStates(); + float shelfStart = getTranslationY(); float numViewsInShelf = 0.0f; View lastChild = mAmbientState.getLastVisibleBackgroundChild(); mNotGoneIndex = -1; float interpolationStart = mMaxLayoutHeight - getIntrinsicHeight() * 2; float expandAmount = 0.0f; - if (getTranslationY() >= interpolationStart) { - expandAmount = (getTranslationY() - interpolationStart) / getIntrinsicHeight(); + if (shelfStart >= interpolationStart) { + expandAmount = (shelfStart - interpolationStart) / getIntrinsicHeight(); expandAmount = Math.min(1.0f, expandAmount); } // find the first view that doesn't overlap with the shelf @@ -199,6 +198,7 @@ public class NotificationShelf extends ActivatableNotificationView { int colorTwoBefore = NO_COLOR; int previousColor = NO_COLOR; float transitionAmount = 0.0f; + int baseZHeight = mAmbientState.getBaseZHeight(); while (notificationIndex < mHostLayout.getChildCount()) { ExpandableView child = (ExpandableView) mHostLayout.getChildAt(notificationIndex); notificationIndex++; @@ -208,26 +208,26 @@ public class NotificationShelf extends ActivatableNotificationView { } ExpandableNotificationRow row = (ExpandableNotificationRow) child; float notificationClipEnd; - float shelfStart = getTranslationY(); - boolean aboveShelf = row.getTranslationZ() > mAmbientState.getBaseZHeight(); + boolean aboveShelf = row.getTranslationZ() > baseZHeight; boolean isLastChild = child == lastChild; + float rowTranslationY = row.getTranslationY(); if (isLastChild || aboveShelf || backgroundForceHidden) { notificationClipEnd = shelfStart + getIntrinsicHeight(); } else { notificationClipEnd = shelfStart - mPaddingBetweenElements; - float height = notificationClipEnd - row.getTranslationY(); + float height = notificationClipEnd - rowTranslationY; if (!row.isBelowSpeedBump() && height <= getNotificationMergeSize()) { // We want the gap to close when we reached the minimum size and only shrink // before notificationClipEnd = Math.min(shelfStart, - row.getTranslationY() + getNotificationMergeSize()); + rowTranslationY + getNotificationMergeSize()); } } updateNotificationClipHeight(row, notificationClipEnd); float inShelfAmount = updateIconAppearance(row, expandAmount, isLastChild); numViewsInShelf += inShelfAmount; int ownColorUntinted = row.getBackgroundColorWithoutTint(); - if (row.getTranslationY() >= getTranslationY() && mNotGoneIndex == -1) { + if (rowTranslationY >= shelfStart && mNotGoneIndex == -1) { mNotGoneIndex = notGoneIndex; setTintColor(previousColor); setOverrideTintColor(colorTwoBefore, transitionAmount); @@ -250,9 +250,6 @@ public class NotificationShelf extends ActivatableNotificationView { } mShelfIcons.calculateIconTranslations(); mShelfIcons.applyIconStates(); - setVisibility(mAmbientState.isShadeExpanded() - ? VISIBLE - : INVISIBLE); boolean hideBackground = numViewsInShelf < 1.0f; setHideBackground(hideBackground || backgroundForceHidden); if (mNotGoneIndex == -1) { @@ -441,9 +438,11 @@ public class NotificationShelf extends ActivatableNotificationView { } private void setHideBackground(boolean hideBackground) { - mHideBackground = hideBackground; - updateBackground(); - updateOutline(); + if (mHideBackground != hideBackground) { + mHideBackground = hideBackground; + updateBackground(); + updateOutline(); + } } public boolean hidesBackground() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index 2621e4a50931f..6650e7271b282 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -455,6 +455,7 @@ public class StatusBarIconView extends AnimatedImageView { } public void setVisibleState(int visibleState, boolean animate, Runnable endRunnable) { + boolean runnableAdded = false; if (visibleState != mVisibleState) { mVisibleState = visibleState; if (animate) { @@ -467,20 +468,22 @@ public class StatusBarIconView extends AnimatedImageView { 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(); + float currentAmount = getIconAppearAmount(); + if (targetAmount != currentAmount) { + mIconAppearAnimator = ObjectAnimator.ofFloat(this, ICON_APPEAR_AMOUNT, + currentAmount, targetAmount); + mIconAppearAnimator.setInterpolator(interpolator); + mIconAppearAnimator.setDuration(100); + mIconAppearAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mIconAppearAnimator = null; + runRunnable(endRunnable); } - } - }); - mIconAppearAnimator.start(); + }); + mIconAppearAnimator.start(); + runnableAdded = true; + } if (mDotAnimator != null) { mDotAnimator.cancel(); @@ -491,22 +494,39 @@ public class StatusBarIconView extends AnimatedImageView { 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(); + currentAmount = getDotAppearAmount(); + if (targetAmount != currentAmount) { + mDotAnimator = ObjectAnimator.ofFloat(this, DOT_APPEAR_AMOUNT, + currentAmount, targetAmount); + mDotAnimator.setInterpolator(interpolator); + mDotAnimator.setDuration(100); + final boolean runRunnable = !runnableAdded; + mDotAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mDotAnimator = null; + if (runRunnable) { + runRunnable(endRunnable); + } + } + }); + mDotAnimator.start(); + runnableAdded = true; + } } else { setIconAppearAmount(visibleState == STATE_ICON ? 1.0f : 0.0f); setDotAppearAmount(visibleState == STATE_DOT ? 1.0f : 0.0f); } } + if (!runnableAdded) { + runRunnable(endRunnable); + } + } + + private void runRunnable(Runnable runnable) { + if (runnable != null) { + runnable.run(); + } } public void setIconAppearAmount(float iconAppearAmount) { 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 160b233847402..294058474d15e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -31,7 +31,7 @@ import com.android.systemui.statusbar.stack.AnimationFilter; import com.android.systemui.statusbar.stack.AnimationProperties; import com.android.systemui.statusbar.stack.ViewState; -import java.util.WeakHashMap; +import java.util.HashMap; /** * A container for notification icons. It handles overflowing icons properly and positions them @@ -80,7 +80,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { }.setDuration(200).setDelay(50); private boolean mShowAllIcons = true; - private WeakHashMap mIconStates = new WeakHashMap<>(); + private final HashMap mIconStates = new HashMap<>(); private int mDotPadding; private int mStaticDotRadius; private int mActualLayoutWidth = -1; @@ -200,14 +200,13 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { return getChildCount(); } - public WeakHashMap resetViewStates() { + public void resetViewStates() { for (int i = 0; i < getChildCount(); i++) { View view = getChildAt(i); ViewState iconState = mIconStates.get(view); iconState.initFrom(view); iconState.alpha = 1.0f; } - return mIconStates; } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index 10d995ccadb52..4fb982b1f26b9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -715,7 +715,6 @@ public class NotificationStackScrollLayout extends ViewGroup requestChildrenUpdate(); } setStackTranslation(translationY); - requestChildrenUpdate(); } private void setRequestedClipBounds(Rect clipRect) { From 17e1b69523086a98189178fc7f2b23d74860c738 Mon Sep 17 00:00:00 2001 From: Selim Cinek Date: Fri, 2 Dec 2016 18:19:11 -0800 Subject: [PATCH 04/11] Improved the low priority behavior with the shelf Low priority notification now don't just disappear, but transform into an overflow such that there's still some object permanence. Test: add low-priority notifications, see dot transformation Bug: 32437839 Change-Id: I90c03c7d88c01c199062bd5f7f0151629654a3a1 --- .../statusbar/ExpandableNotificationRow.java | 4 +- .../systemui/statusbar/NotificationShelf.java | 3 + .../phone/NotificationIconAreaController.java | 17 ++-- .../phone/NotificationIconContainer.java | 77 +++++++++++++------ 4 files changed, 69 insertions(+), 32 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index 996e2ef587f05..e89fe55e07123 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -891,7 +891,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { } private void updateIconVisibilities() { - boolean visible = isChildInGroup() || isBelowSpeedBump() || mIconsVisible; + boolean visible = isChildInGroup() + || (isBelowSpeedBump() && !NotificationShelf.SHOW_AMBIENT_ICONS) + || mIconsVisible; mPublicLayout.setIconsVisible(visible); mPrivateLayout.setIconsVisible(visible); if (mChildrenContainer != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 80d41883066ec..8ede78b1d9e56 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -43,6 +43,7 @@ import com.android.systemui.statusbar.stack.StackScrollState; */ public class NotificationShelf extends ActivatableNotificationView { + public static final boolean SHOW_AMBIENT_ICONS = true; private static final boolean USE_ANIMATIONS_WHEN_OPENING = SystemProperties.getBoolean("debug.icon_opening_animations", true); private ViewInvertHelper mViewInvertHelper; @@ -248,6 +249,7 @@ public class NotificationShelf extends ActivatableNotificationView { notGoneIndex++; previousColor = ownColorUntinted; } + mShelfIcons.setSpeedBumpIndex(mAmbientState.getSpeedBumpIndex()); mShelfIcons.calculateIconTranslations(); mShelfIcons.applyIconStates(); boolean hideBackground = numViewsInShelf < 1.0f; @@ -476,6 +478,7 @@ public class NotificationShelf extends ActivatableNotificationView { float paddingStart = NotificationUtils.interpolate(start, mShelfIcons.getPaddingStart(), openedAmount); mShelfIcons.setActualPaddingStart(paddingStart); + mShelfIcons.setOpenedAmount(openedAmount); } public void setMaxLayoutHeight(int maxLayoutHeight) { 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 d543f49b9d026..345dcbdc61c4c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java @@ -127,9 +127,9 @@ public class NotificationIconAreaController { return mPhoneStatusBar.getStatusBarHeight(); } - protected boolean shouldShowNotification(NotificationData.Entry entry, - NotificationData notificationData) { - if (notificationData.isAmbient(entry.key) + protected boolean shouldShowNotificationIcon(NotificationData.Entry entry, + NotificationData notificationData, boolean showAmbient) { + if (notificationData.isAmbient(entry.key) && !showAmbient && !NotificationData.showNotificationEvenIfUnprovisioned(entry.notification)) { return false; } @@ -148,8 +148,10 @@ public class NotificationIconAreaController { */ public void updateNotificationIcons(NotificationData notificationData) { - updateIconsForLayout(notificationData, entry -> entry.icon, mNotificationIcons); - updateIconsForLayout(notificationData, entry -> entry.expandedIcon, mShelfIcons); + updateIconsForLayout(notificationData, entry -> entry.icon, mNotificationIcons, + false /* showAmbient */); + updateIconsForLayout(notificationData, entry -> entry.expandedIcon, mShelfIcons, + NotificationShelf.SHOW_AMBIENT_ICONS); applyNotificationIconsTint(); ArrayList activeNotifications @@ -173,10 +175,11 @@ public class NotificationIconAreaController { * @param notificationData the notification data to look up which notifications are relevant * @param function A function to look up an icon view based on an entry * @param hostLayout which layout should be updated + * @param showAmbient should ambient notification icons be shown */ private void updateIconsForLayout(NotificationData notificationData, Function function, - NotificationIconContainer hostLayout) { + NotificationIconContainer hostLayout, boolean showAmbient) { ArrayList toShow = new ArrayList<>( mNotificationScrollLayout.getChildCount()); @@ -185,7 +188,7 @@ public class NotificationIconAreaController { View view = mNotificationScrollLayout.getChildAt(i); if (view instanceof ExpandableNotificationRow) { NotificationData.Entry ent = ((ExpandableNotificationRow) view).getEntry(); - if (shouldShowNotification(ent, notificationData)) { + if (shouldShowNotificationIcon(ent, notificationData, showAmbient)) { toShow.add(function.apply(ent)); } } 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 294058474d15e..a1a8575d733b0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -89,6 +89,9 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { private boolean mChangingViewPositions; private int mAddAnimationStartIndex = -1; private int mCannedAnimationStartIndex = -1; + private int mSpeedBumpIndex = -1; + private int mIconSize; + private float mOpenedAmount = 0.0f; public NotificationIconContainer(Context context, AttributeSet attrs) { super(context, attrs); @@ -119,6 +122,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { protected void onLayout(boolean changed, int l, int t, int r, int b) { float centerY = getHeight() / 2.0f; // we layout all our children on the left at the top + mIconSize = 0; for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); // We need to layout all children even the GONE ones, such that the heights are @@ -127,6 +131,9 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { int height = child.getMeasuredHeight(); int top = (int) (centerY - height / 2.0f); child.layout(0, top, width, top + height); + if (i == 0) { + mIconSize = child.getWidth(); + } } if (mShowAllIcons) { resetViewStates(); @@ -216,42 +223,56 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { */ public void calculateIconTranslations() { float translationX = getActualPaddingStart(); - int overflowingIconIndex = -1; - int lastTwoIconWidth = 0; + int firstOverflowIndex = -1; int childCount = getChildCount(); + float layoutEnd = getLayoutEnd(); + float overflowStart = layoutEnd - mIconSize * 2.2f; + boolean hasAmbient = mSpeedBumpIndex != -1 && mSpeedBumpIndex < getChildCount(); + float visualOverflowStart = 0; for (int i = 0; i < childCount; i++) { View view = getChildAt(i); IconState iconState = mIconStates.get(view); iconState.xTranslation = translationX; - iconState.visibleState = StatusBarIconView.STATE_ICON; - translationX += iconState.iconAppearAmount * view.getWidth(); - if (translationX > getLayoutEnd()) { - // we are overflowing it with this icon - overflowingIconIndex = i - 1; - lastTwoIconWidth = view.getWidth(); - break; + boolean isAmbient = mSpeedBumpIndex != -1 && i >= mSpeedBumpIndex + && iconState.iconAppearAmount > 0.0f; + boolean noOverflowAfter = i == childCount - 1; + if (mOpenedAmount != 0.0f) { + noOverflowAfter = noOverflowAfter && !hasAmbient; } + iconState.visibleState = StatusBarIconView.STATE_ICON; + if (firstOverflowIndex == -1 && (isAmbient + || (translationX >= (noOverflowAfter ? layoutEnd - mIconSize : overflowStart)))) { + firstOverflowIndex = noOverflowAfter ? i - 1 : i; + int totalDotLength = mStaticDotRadius * 6 + 2 * mDotPadding; + visualOverflowStart = overflowStart + mIconSize * 1.2f - totalDotLength / 2 + - mIconSize * 0.5f + mStaticDotRadius; + if (isAmbient) { + visualOverflowStart = Math.min(translationX, visualOverflowStart + + mStaticDotRadius * 2 + mDotPadding); + } else { + visualOverflowStart += (translationX - overflowStart) / mIconSize + * (mStaticDotRadius * 2 + mDotPadding); + } + } + translationX += iconState.iconAppearAmount * view.getWidth(); } - if (overflowingIconIndex != -1) { + if (firstOverflowIndex != -1) { int numDots = 1; - View overflowIcon = getChildAt(overflowingIconIndex); - IconState overflowState = mIconStates.get(overflowIcon); - lastTwoIconWidth += overflowIcon.getWidth(); - int dotWidth = mStaticDotRadius * 2 + mDotPadding; - int totalDotLength = mStaticDotRadius * 6 + 2 * mDotPadding; - translationX = (getLayoutEnd() - lastTwoIconWidth / 2 - totalDotLength / 2) - - overflowIcon.getWidth() * 0.3f + mStaticDotRadius; - float overflowStart = getLayoutEnd() - lastTwoIconWidth; - float overlapAmount = (overflowState.xTranslation - overflowStart) - / overflowIcon.getWidth(); - translationX += overlapAmount * dotWidth; - for (int i = overflowingIconIndex; i < childCount; i++) { + translationX = visualOverflowStart; + for (int i = firstOverflowIndex; i < childCount; i++) { View view = getChildAt(i); IconState iconState = mIconStates.get(view); + int dotWidth = mStaticDotRadius * 2 + mDotPadding; iconState.xTranslation = translationX; if (numDots <= 3) { - iconState.visibleState = StatusBarIconView.STATE_DOT; - translationX += numDots == 3 ? 3 * dotWidth : dotWidth; + if (numDots == 1 && iconState.iconAppearAmount < 0.8f) { + iconState.visibleState = StatusBarIconView.STATE_ICON; + numDots--; + } else { + iconState.visibleState = StatusBarIconView.STATE_DOT; + } + translationX += (numDots == 3 ? 3 * dotWidth : dotWidth) + * iconState.iconAppearAmount; } else { iconState.visibleState = StatusBarIconView.STATE_HIDDEN; } @@ -331,6 +352,14 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { return mIconStates.get(icon); } + public void setSpeedBumpIndex(int speedBumpIndex) { + mSpeedBumpIndex = speedBumpIndex; + } + + public void setOpenedAmount(float expandAmount) { + mOpenedAmount = expandAmount; + } + public class IconState extends ViewState { public float iconAppearAmount = 1.0f; public float clampedAppearAmount = 1.0f; From 932005db01cb8ac8506b5b766a9f80faa2c99e41 Mon Sep 17 00:00:00 2001 From: Selim Cinek Date: Mon, 5 Dec 2016 17:12:09 -0800 Subject: [PATCH 05/11] Placed the overflow indicator perfectly in the collapsed center Previously, the state could be abitrary based on the width of the layout this is now fixed. Test: add high, low-priority notifications, observe perfect centering Bug: 32437839 Change-Id: Id82d4aa87fe07f53bfe1b891a1ce2caa88d686dc --- .../systemui/statusbar/NotificationShelf.java | 11 +++- .../phone/NotificationIconContainer.java | 59 ++++++++++++++++--- 2 files changed, 61 insertions(+), 9 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 8ede78b1d9e56..86c674fec43bf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -471,7 +471,15 @@ public class NotificationShelf extends ActivatableNotificationView { mShelfIcons.getWidth(), openedAmount); mShelfIcons.setActualLayoutWidth(width); - float padding = NotificationUtils.interpolate(mCollapsedIcons.getPaddingEnd(), + boolean hasOverflow = mCollapsedIcons.hasOverflow(); + int collapsedPadding = mCollapsedIcons.getPaddingEnd(); + if (!hasOverflow) { + // we have to ensure that adding the low priority notification won't lead to an + // overflow + collapsedPadding -= (1.0f + NotificationIconContainer.OVERFLOW_EARLY_AMOUNT) + * mCollapsedIcons.getIconSize(); + } + float padding = NotificationUtils.interpolate(collapsedPadding, mShelfIcons.getPaddingEnd(), openedAmount); mShelfIcons.setActualPaddingEnd(padding); @@ -479,6 +487,7 @@ public class NotificationShelf extends ActivatableNotificationView { mShelfIcons.getPaddingStart(), openedAmount); mShelfIcons.setActualPaddingStart(paddingStart); mShelfIcons.setOpenedAmount(openedAmount); + mShelfIcons.setVisualOverflowAdaption(mCollapsedIcons.getVisualOverflowAdaption()); } public void setMaxLayoutHeight(int maxLayoutHeight) { 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 a1a8575d733b0..203d6896afb63 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -38,6 +38,13 @@ import java.util.HashMap; * correctly on the screen. */ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { + /** + * A float value indicating how much before the overflow start the icons should transform into + * a dot. A value of 0 means that they are exactly at the end and a value of 1 means it starts + * 1 icon width early. + */ + public static final float OVERFLOW_EARLY_AMOUNT = 0.2f; + private static final int NO_VALUE = Integer.MIN_VALUE; private static final String TAG = "NotificationIconContainer"; private static final boolean DEBUG = false; private static final int CANNED_ANIMATION_DURATION = 100; @@ -83,15 +90,16 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { private final HashMap mIconStates = new HashMap<>(); private int mDotPadding; private int mStaticDotRadius; - private int mActualLayoutWidth = -1; - private float mActualPaddingEnd = -1; - private float mActualPaddingStart = -1; + private int mActualLayoutWidth = NO_VALUE; + private float mActualPaddingEnd = NO_VALUE; + private float mActualPaddingStart = NO_VALUE; private boolean mChangingViewPositions; private int mAddAnimationStartIndex = -1; private int mCannedAnimationStartIndex = -1; private int mSpeedBumpIndex = -1; private int mIconSize; private float mOpenedAmount = 0.0f; + private float mVisualOverflowAdaption; public NotificationIconContainer(Context context, AttributeSet attrs) { super(context, attrs); @@ -226,7 +234,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { int firstOverflowIndex = -1; int childCount = getChildCount(); float layoutEnd = getLayoutEnd(); - float overflowStart = layoutEnd - mIconSize * 2.2f; + float overflowStart = layoutEnd - mIconSize * (2 + OVERFLOW_EARLY_AMOUNT); boolean hasAmbient = mSpeedBumpIndex != -1 && mSpeedBumpIndex < getChildCount(); float visualOverflowStart = 0; for (int i = 0; i < childCount; i++) { @@ -244,7 +252,8 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { || (translationX >= (noOverflowAfter ? layoutEnd - mIconSize : overflowStart)))) { firstOverflowIndex = noOverflowAfter ? i - 1 : i; int totalDotLength = mStaticDotRadius * 6 + 2 * mDotPadding; - visualOverflowStart = overflowStart + mIconSize * 1.2f - totalDotLength / 2 + visualOverflowStart = overflowStart + mIconSize * (1 + OVERFLOW_EARLY_AMOUNT) + - totalDotLength / 2 - mIconSize * 0.5f + mStaticDotRadius; if (isAmbient) { visualOverflowStart = Math.min(translationX, visualOverflowStart @@ -253,6 +262,23 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { visualOverflowStart += (translationX - overflowStart) / mIconSize * (mStaticDotRadius * 2 + mDotPadding); } + if (mShowAllIcons) { + // We want to perfectly position the overflow in the static state, such that + // it's perfectly centered instead of measuring it from the end. + mVisualOverflowAdaption = 0; + if (firstOverflowIndex != -1) { + View firstOverflowView = getChildAt(i); + IconState overflowState = mIconStates.get(firstOverflowView); + float totalAmount = layoutEnd - overflowState.xTranslation; + float newPosition = overflowState.xTranslation + totalAmount / 2 + - totalDotLength / 2 + - mIconSize * 0.5f + mStaticDotRadius; + mVisualOverflowAdaption = newPosition - visualOverflowStart; + visualOverflowStart = newPosition; + } + } else { + visualOverflowStart += mVisualOverflowAdaption * (1f - mOpenedAmount); + } } translationX += iconState.iconAppearAmount * view.getWidth(); } @@ -293,14 +319,14 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { } private float getActualPaddingEnd() { - if (mActualPaddingEnd < 0) { + if (mActualPaddingEnd == NO_VALUE) { return getPaddingEnd(); } return mActualPaddingEnd; } private float getActualPaddingStart() { - if (mActualPaddingStart < 0) { + if (mActualPaddingStart == NO_VALUE) { return getPaddingStart(); } return mActualPaddingStart; @@ -338,7 +364,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { } public int getActualWidth() { - if (mActualLayoutWidth < 0) { + if (mActualLayoutWidth == NO_VALUE) { return getWidth(); } return mActualLayoutWidth; @@ -360,6 +386,23 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { mOpenedAmount = expandAmount; } + public float getVisualOverflowAdaption() { + return mVisualOverflowAdaption; + } + + public void setVisualOverflowAdaption(float visualOverflowAdaption) { + mVisualOverflowAdaption = visualOverflowAdaption; + } + + public boolean hasOverflow() { + float width = (getChildCount() + OVERFLOW_EARLY_AMOUNT) * mIconSize; + return width - (getWidth() - getActualPaddingStart() - getActualPaddingEnd()) > 0; + } + + public int getIconSize() { + return mIconSize; + } + public class IconState extends ViewState { public float iconAppearAmount = 1.0f; public float clampedAppearAmount = 1.0f; From 0e39fac3609d5ccb6a399a01e6614a5e94f8b666 Mon Sep 17 00:00:00 2001 From: Selim Cinek Date: Tue, 6 Dec 2016 11:50:55 -0800 Subject: [PATCH 06/11] Fixed the scrim drawing in seascape The scrim was drawing offset in seascape leading to artifacts. Test: add notifications, observe seascape drawing, no artifacts Bug: 32437839 Change-Id: I63d9f680a62212873a01fb947193f7cece19d81c --- .../android/systemui/statusbar/ScrimView.java | 21 +++++-------------- .../statusbar/phone/ScrimController.java | 4 ---- .../statusbar/phone/StatusBarWindowView.java | 1 - 3 files changed, 5 insertions(+), 21 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java index bfa43fde03d0a..dba7130793cad 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java @@ -42,7 +42,6 @@ public class ScrimView extends View private float mViewAlpha = 1.0f; private ValueAnimator mAlphaAnimator; private Rect mExcludedRect = new Rect(); - private int mLeftInset = 0; private boolean mHasExcludedArea; private ValueAnimator.AnimatorUpdateListener mAlphaUpdateListener = new ValueAnimator.AnimatorUpdateListener() { @@ -88,12 +87,12 @@ public class ScrimView extends View if (mExcludedRect.top > 0) { canvas.drawRect(0, 0, getWidth(), mExcludedRect.top, mPaint); } - if (mExcludedRect.left + mLeftInset > 0) { - canvas.drawRect(0, mExcludedRect.top, mExcludedRect.left + mLeftInset, - mExcludedRect.bottom, mPaint); + if (mExcludedRect.left > 0) { + canvas.drawRect(0, mExcludedRect.top, mExcludedRect.left, mExcludedRect.bottom, + mPaint); } - if (mExcludedRect.right + mLeftInset < getWidth()) { - canvas.drawRect(mExcludedRect.right + mLeftInset, + if (mExcludedRect.right < getWidth()) { + canvas.drawRect(mExcludedRect.right, mExcludedRect.top, getWidth(), mExcludedRect.bottom, @@ -184,14 +183,4 @@ public class ScrimView extends View public void setChangeRunnable(Runnable changeRunnable) { mChangeRunnable = changeRunnable; } - - public void setLeftInset(int leftInset) { - if (mLeftInset != leftInset) { - mLeftInset = leftInset; - - if (mHasExcludedArea) { - invalidate(); - } - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 0e74e579624b6..749ff99e9c6d3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -556,10 +556,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, mScrimBehind.setExcludedArea(area); } - public void setLeftInset(int inset) { - mScrimBehind.setLeftInset(inset); - } - public int getScrimBehindColor() { return mScrimBehind.getScrimColorWithAlpha(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java index f6dd88d9a475f..487f0e73959ce 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java @@ -130,7 +130,6 @@ public class StatusBarWindowView extends FrameLayout { } private void applyMargins() { - mService.mScrimController.setLeftInset(mLeftInset); final int N = getChildCount(); for (int i = 0; i < N; i++) { View child = getChildAt(i); From 01a73f9d0c897a92b163328d26b780d1f337975b Mon Sep 17 00:00:00 2001 From: Selim Cinek Date: Tue, 6 Dec 2016 16:13:42 -0800 Subject: [PATCH 07/11] Don't translate the notification content while scrolling While scrolling we don't translate the notification content anymore and only have it while expanding. Test: add notifications, scroll Bug: 32437839 Change-Id: I21037b40ec92c20f2cd775815dcf5d8399348223 --- .../systemui/statusbar/NotificationShelf.java | 33 ++++++++++--------- .../systemui/statusbar/StatusBarIconView.java | 4 ++- .../phone/NotificationIconContainer.java | 1 + 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 86c674fec43bf..a447d7b5efdc8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -20,8 +20,6 @@ import android.content.Context; import android.content.res.Configuration; import android.os.SystemProperties; import android.util.AttributeSet; -import android.util.FloatProperty; -import android.util.Property; import android.view.View; import android.view.ViewGroup; @@ -287,49 +285,50 @@ public class NotificationShelf extends ActivatableNotificationView { } float viewEnd = viewStart + fullHeight; float fullTransitionAmount; - float iconTransitonAmount; - if (viewEnd >= getTranslationY() && (mAmbientState.isShadeExpanded() + float iconTransitionAmount; + float shelfStart = getTranslationY(); + if (viewEnd >= shelfStart && (mAmbientState.isShadeExpanded() || (!row.isPinned() && !row.isHeadsUpAnimatingAway()))) { - if (viewStart < getTranslationY()) { + if (viewStart < shelfStart) { - float fullAmount = (getTranslationY() - viewStart) / fullHeight; + float fullAmount = (shelfStart - viewStart) / fullHeight; float interpolatedAmount = Interpolators.ACCELERATE_DECELERATE.getInterpolation( fullAmount); interpolatedAmount = NotificationUtils.interpolate( interpolatedAmount, fullAmount, expandAmount); fullTransitionAmount = 1.0f - interpolatedAmount; - iconTransitonAmount = (getTranslationY() - viewStart) / iconTransformDistance; - iconTransitonAmount = Math.min(1.0f, iconTransitonAmount); - iconTransitonAmount = 1.0f - iconTransitonAmount; + iconTransitionAmount = (shelfStart - viewStart) / iconTransformDistance; + iconTransitionAmount = Math.min(1.0f, iconTransitionAmount); + iconTransitionAmount = 1.0f - iconTransitionAmount; } else { fullTransitionAmount = 1.0f; - iconTransitonAmount = 1.0f; + iconTransitionAmount = 1.0f; } } else { fullTransitionAmount = 0.0f; - iconTransitonAmount = 0.0f; + iconTransitionAmount = 0.0f; } - row.setContentTransformationAmount(iconTransitonAmount, isLastChild); - updateIconPositioning(row, iconTransitonAmount, fullTransitionAmount); + updateIconPositioning(row, iconTransitionAmount, fullTransitionAmount, isLastChild); return fullTransitionAmount; } private void updateIconPositioning(ExpandableNotificationRow row, float iconTransitionAmount, - float fullTransitionAmount) { + float fullTransitionAmount, boolean isLastChild) { StatusBarIconView icon = row.getEntry().expandedIcon; NotificationIconContainer.IconState iconState = getIconState(icon); if (iconState == null) { return; } float clampedAmount = iconTransitionAmount > 0.5f ? 1.0f : 0.0f; - boolean isLastChild = isLastChild(row); if (clampedAmount == iconTransitionAmount) { iconState.keepClampedPosition = false; } if (clampedAmount == fullTransitionAmount) { iconState.useFullTransitionAmount = fullTransitionAmount == 0.0f; + iconState.translateContent = mMaxLayoutHeight - getTranslationY() + - getIntrinsicHeight() > 0; } float transitionAmount; boolean needCannedAnimation = iconState.clampedAppearAmount == 1.0f @@ -358,6 +357,10 @@ public class NotificationShelf extends ActivatableNotificationView { : transitionAmount; iconState.clampedAppearAmount = clampedAmount; setIconTransformationAmount(row, transitionAmount); + float contentTransformationAmount = isLastChild || iconState.translateContent + ? iconTransitionAmount + : 0.0f; + row.setContentTransformationAmount(contentTransformationAmount, isLastChild); } private boolean isLastChild(ExpandableNotificationRow row) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index 6650e7271b282..a2c2fd7731415 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -515,7 +515,9 @@ public class StatusBarIconView extends AnimatedImageView { } } else { setIconAppearAmount(visibleState == STATE_ICON ? 1.0f : 0.0f); - setDotAppearAmount(visibleState == STATE_DOT ? 1.0f : 0.0f); + setDotAppearAmount(visibleState == STATE_DOT ? 1.0f + : visibleState == STATE_ICON ? 2.0f + : 0.0f); } } if (!runnableAdded) { 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 203d6896afb63..d323e4f70eb7f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -411,6 +411,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { public boolean needsCannedAnimation; public boolean keepClampedPosition; public boolean useFullTransitionAmount; + public boolean translateContent; @Override public void applyToView(View view) { From 727903ce56bf1b5102dc74816a67b04a2f5b7824 Mon Sep 17 00:00:00 2001 From: Selim Cinek Date: Tue, 6 Dec 2016 17:28:10 -0800 Subject: [PATCH 08/11] Don't do icon animations if we are scrolling fast We are now not doing any icon animations anymore in case we are scrolling fast. Change-Id: I878d20a8e6114dc58c3908d18cc5ba20e59972cf Test: add notifications, scroll fast Bug: 32437839 --- packages/SystemUI/res/values/dimens.xml | 3 +++ .../systemui/statusbar/NotificationShelf.java | 16 +++++++++++----- .../systemui/statusbar/stack/AmbientState.java | 9 +++++++++ .../stack/NotificationStackScrollLayout.java | 3 +++ 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 1b6b04d9a849d..a03e37e032450 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -81,6 +81,9 @@ 148dp + + 1500dp + 32dp diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index a447d7b5efdc8..324eb2df6e5e9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -60,6 +60,7 @@ public class NotificationShelf extends ActivatableNotificationView { private int mNotGoneIndex; private boolean mHasItemsInStableShelf; private NotificationIconContainer mCollapsedIcons; + private int mScrollFastThreshold; public NotificationShelf(Context context, AttributeSet attrs) { super(context, attrs); @@ -101,6 +102,8 @@ public class NotificationShelf extends ActivatableNotificationView { setLayoutParams(layoutParams); int padding = getResources().getDimensionPixelOffset(R.dimen.shelf_icon_container_padding); mShelfIcons.setPadding(padding, 0, padding, 0); + mScrollFastThreshold = getResources().getDimensionPixelOffset( + R.dimen.scroll_fast_threshold); } @Override @@ -197,6 +200,7 @@ public class NotificationShelf extends ActivatableNotificationView { int colorTwoBefore = NO_COLOR; int previousColor = NO_COLOR; float transitionAmount = 0.0f; + boolean scrollingFast = mAmbientState.getCurrentScrollVelocity() > mScrollFastThreshold; int baseZHeight = mAmbientState.getBaseZHeight(); while (notificationIndex < mHostLayout.getChildCount()) { ExpandableView child = (ExpandableView) mHostLayout.getChildAt(notificationIndex); @@ -223,7 +227,8 @@ public class NotificationShelf extends ActivatableNotificationView { } } updateNotificationClipHeight(row, notificationClipEnd); - float inShelfAmount = updateIconAppearance(row, expandAmount, isLastChild); + float inShelfAmount = updateIconAppearance(row, expandAmount, scrollingFast, + isLastChild); numViewsInShelf += inShelfAmount; int ownColorUntinted = row.getBackgroundColorWithoutTint(); if (rowTranslationY >= shelfStart && mNotGoneIndex == -1) { @@ -273,7 +278,7 @@ public class NotificationShelf extends ActivatableNotificationView { * @return the icon amount how much this notification is in the shelf; */ private float updateIconAppearance(ExpandableNotificationRow row, float expandAmount, - boolean isLastChild) { + boolean scrollingFast, boolean isLastChild) { // Let calculate how much the view is in the shelf float viewStart = row.getTranslationY(); int fullHeight = row.getActualHeight() + mPaddingBetweenElements; @@ -310,12 +315,13 @@ public class NotificationShelf extends ActivatableNotificationView { fullTransitionAmount = 0.0f; iconTransitionAmount = 0.0f; } - updateIconPositioning(row, iconTransitionAmount, fullTransitionAmount, isLastChild); + updateIconPositioning(row, iconTransitionAmount, fullTransitionAmount, scrollingFast, + isLastChild); return fullTransitionAmount; } private void updateIconPositioning(ExpandableNotificationRow row, float iconTransitionAmount, - float fullTransitionAmount, boolean isLastChild) { + float fullTransitionAmount, boolean scrollingFast, boolean isLastChild) { StatusBarIconView icon = row.getEntry().expandedIcon; NotificationIconContainer.IconState iconState = getIconState(icon); if (iconState == null) { @@ -326,7 +332,7 @@ public class NotificationShelf extends ActivatableNotificationView { iconState.keepClampedPosition = false; } if (clampedAmount == fullTransitionAmount) { - iconState.useFullTransitionAmount = fullTransitionAmount == 0.0f; + iconState.useFullTransitionAmount = fullTransitionAmount == 0.0f || scrollingFast; iconState.translateContent = mMaxLayoutHeight - getTranslationY() - getIntrinsicHeight() > 0; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java index 26f74eab7ba7d..f468b5c7e11df 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java @@ -52,6 +52,7 @@ public class AmbientState { private int mBaseZHeight; private int mMaxLayoutHeight; private ActivatableNotificationView mLastVisibleBackgroundChild; + private float mCurrentScrollVelocity; public AmbientState(Context context) { reload(context); @@ -241,4 +242,12 @@ public class AmbientState { public ActivatableNotificationView getLastVisibleBackgroundChild() { return mLastVisibleBackgroundChild; } + + public void setCurrentScrollVelocity(float currentScrollVelocity) { + mCurrentScrollVelocity = currentScrollVelocity; + } + + public float getCurrentScrollVelocity() { + return mCurrentScrollVelocity; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index 4fb982b1f26b9..3cd4ebdff329e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -575,6 +575,9 @@ public class NotificationStackScrollLayout extends ViewGroup */ private void updateChildren() { updateScrollStateForAddedChildren(); + mAmbientState.setCurrentScrollVelocity(mScroller.isFinished() + ? 0 + : mScroller.getCurrVelocity()); mAmbientState.setScrollY(mOwnScrollY); mStackScrollAlgorithm.getStackScrollState(mAmbientState, mCurrentStackScrollState); if (!isCurrentlyAnimating() && !mNeedsAnimation) { From 61190fb7e7a520481fc06dba55f99fa91a72e9e4 Mon Sep 17 00:00:00 2001 From: Selim Cinek Date: Wed, 7 Dec 2016 12:16:45 -0800 Subject: [PATCH 09/11] Don't reset the velocity when flinging down just because of falsing The velocity was reset even if the direction was correct. Test: collapse on the lockscreen and then fling down, observe no jitter Bug: 32437839 Change-Id: I92454a1bb3d8156c8c3c537b266d061b16c82c7b --- .../src/com/android/systemui/statusbar/phone/PanelView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java index 570d5d40064f4..83bae11b70ffc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -685,7 +685,7 @@ public abstract class PanelView extends FrameLayout { mOverExpandedBeforeFling = getOverExpansionAmount() > 0f; ValueAnimator animator = createHeightAnimator(target); if (expand) { - if (expandBecauseOfFalsing) { + if (expandBecauseOfFalsing && vel < 0) { vel = 0; } mFlingAnimationUtils.apply(animator, mExpandedHeight, target, vel, getHeight()); From 355652a6c8109a8706cf8c6157cfb1c1b0dcf06e Mon Sep 17 00:00:00 2001 From: Selim Cinek Date: Wed, 7 Dec 2016 13:32:12 -0800 Subject: [PATCH 10/11] Fixes that notifications were sometimes clipped on the lockscreen When dragging down a notification into the bottom, the top notification could become clipped. Test: add notifications, pull down on last notification on lockscreen Bug: 32437839 Change-Id: Ibb58d1aa7575fbe9d84b8542ac57ffae494bb127 --- .../systemui/statusbar/phone/PhoneStatusBar.java | 1 + .../systemui/statusbar/stack/AmbientState.java | 10 ++++++++++ .../stack/NotificationStackScrollLayout.java | 12 +++++++++--- .../statusbar/stack/StackScrollAlgorithm.java | 3 ++- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index bbbdc13d69e34..024c1750bcdf9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -4558,6 +4558,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mGroupManager.setStatusBarState(state); mFalsingManager.setStatusBarState(state); mStatusBarWindowManager.setStatusBarState(state); + mStackScroller.setStatusBarState(state); updateReportRejectedTouchVisibility(); updateDozing(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java index f468b5c7e11df..94fc17a8b1d83 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java @@ -22,6 +22,7 @@ import android.view.View; import com.android.systemui.R; import com.android.systemui.statusbar.ActivatableNotificationView; import com.android.systemui.statusbar.NotificationShelf; +import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.policy.HeadsUpManager; import java.util.ArrayList; @@ -53,6 +54,7 @@ public class AmbientState { private int mMaxLayoutHeight; private ActivatableNotificationView mLastVisibleBackgroundChild; private float mCurrentScrollVelocity; + private int mStatusBarState; public AmbientState(Context context) { reload(context); @@ -250,4 +252,12 @@ public class AmbientState { public float getCurrentScrollVelocity() { return mCurrentScrollVelocity; } + + public boolean isOnKeyguard() { + return mStatusBarState == StatusBarState.KEYGUARD; + } + + public void setStatusBarState(int statusBarState) { + mStatusBarState = statusBarState; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index 3cd4ebdff329e..543550d6395d2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -357,6 +357,7 @@ public class NotificationStackScrollLayout extends ViewGroup private Rect mRequestedClipBounds; private boolean mInHeadsUpPinnedMode; private boolean mHeadsUpAnimatingAway; + private int mStatusBarState; public NotificationStackScrollLayout(Context context) { this(context, null); @@ -1187,7 +1188,7 @@ public class NotificationStackScrollLayout extends ViewGroup } private boolean onKeyguard() { - return mPhoneStatusBar.getBarState() == StatusBarState.KEYGUARD; + return mStatusBarState == StatusBarState.KEYGUARD; } private void setSwipingInProgress(boolean isSwiped) { @@ -2124,7 +2125,7 @@ public class NotificationStackScrollLayout extends ViewGroup top = mTopPadding; bottom = top; } - if (mPhoneStatusBar.getBarState() != StatusBarState.KEYGUARD) { + if (mStatusBarState != StatusBarState.KEYGUARD) { top = (int) Math.max(mTopPadding + mStackTranslation, top); } else { // otherwise the animation from the shade to the keyguard will jump as it's maxed @@ -2358,7 +2359,7 @@ public class NotificationStackScrollLayout extends ViewGroup } break; case MotionEvent.ACTION_UP: - if (mPhoneStatusBar.getBarState() != StatusBarState.KEYGUARD && mTouchIsClick && + if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick && isBelowLastNotification(mInitialTouchX, mInitialTouchY)) { mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY); } @@ -3981,6 +3982,11 @@ public class NotificationStackScrollLayout extends ViewGroup updateClipping(); } + public void setStatusBarState(int statusBarState) { + mStatusBarState = statusBarState; + mAmbientState.setStatusBarState(statusBarState); + } + /** * A listener that is notified when some child locations might have changed. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java index 7afc7ba0a0c1e..50b6d70f437c2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java @@ -124,7 +124,8 @@ public class StackScrollAlgorithm { private void updateClipping(StackScrollState resultState, StackScrollAlgorithmState algorithmState, AmbientState ambientState) { - float drawStart = ambientState.getTopPadding() + ambientState.getStackTranslation(); + float drawStart = !ambientState.isOnKeyguard() ? ambientState.getTopPadding() + + ambientState.getStackTranslation() : 0; float previousNotificationEnd = 0; float previousNotificationStart = 0; int childCount = algorithmState.visibleChildren.size(); From 378053af93b10162f72f91619208ff2c92e11f4c Mon Sep 17 00:00:00 2001 From: Selim Cinek Date: Wed, 7 Dec 2016 15:41:28 -0800 Subject: [PATCH 11/11] Enforcing padding on the bottom of the keyguard When calculating the maximum padding, we need to require some padding such that the indication text isn't overlapped Test: add 6 notifications on sailfish, see no overlap Bug: 32437839 Change-Id: I25effa3e971d4a0bfaef70615c8a6e29c906a6ed --- packages/SystemUI/res/values/dimens.xml | 3 +++ .../systemui/statusbar/phone/NotificationPanelView.java | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index a03e37e032450..f83fab517b2fe 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -97,6 +97,9 @@ 32dp + + 12sp + 48dp diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 99e98f2e66352..b5865db5a2ea6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -208,6 +208,7 @@ public class NotificationPanelView extends PanelView implements }; private NotificationGroupManager mGroupManager; private boolean mOpening; + private int mIndicationBottomPadding; public NotificationPanelView(Context context, AttributeSet attrs) { super(context, attrs); @@ -273,6 +274,8 @@ public class NotificationPanelView extends PanelView implements R.dimen.notification_panel_min_side_margin); mMaxFadeoutHeight = getResources().getDimensionPixelSize( R.dimen.max_notification_fadeout_height); + mIndicationBottomPadding = getResources().getDimensionPixelSize( + R.dimen.keyguard_indication_bottom_padding); } public void updateResources() { @@ -406,7 +409,8 @@ public class NotificationPanelView extends PanelView implements R.dimen.notification_divider_height)); float shelfSize = mNotificationStackScroller.getNotificationShelf().getIntrinsicHeight() + notificationPadding; - float availableSpace = mNotificationStackScroller.getHeight() - minPadding - shelfSize; + float availableSpace = mNotificationStackScroller.getHeight() - minPadding - shelfSize + - mIndicationBottomPadding; int count = 0; for (int i = 0; i < mNotificationStackScroller.getChildCount(); i++) { ExpandableView child = (ExpandableView) mNotificationStackScroller.getChildAt(i);