diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 29b5b6205a92b..a7d4aa094cdd6 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -81,6 +81,9 @@ 148dp + + 1500dp + 32dp @@ -94,6 +97,9 @@ 32dp + + 12sp + 48dp diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java index d46d267efdba9..cf8d332710329 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; /** @@ -507,8 +507,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); + } } /** @@ -567,13 +569,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 661cc3ca2b4ae..e89fe55e07123 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; @@ -885,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) { @@ -1679,13 +1687,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); } } @@ -1871,8 +1883,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..324eb2df6e5e9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar; import android.content.Context; import android.content.res.Configuration; +import android.os.SystemProperties; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; @@ -34,19 +35,18 @@ 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; - /** * 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. */ 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; private boolean mDark; private NotificationIconContainer mShelfIcons; - private ArrayList mIcons = new ArrayList<>(); private ShelfState mShelfState; private int[] mTmp = new int[2]; private boolean mHideBackground; @@ -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 @@ -162,6 +165,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; @@ -174,15 +178,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 @@ -196,6 +200,8 @@ 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); notificationIndex++; @@ -204,30 +210,28 @@ 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(); + 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, iconState, icon, expandAmount, + float inShelfAmount = updateIconAppearance(row, expandAmount, scrollingFast, 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); @@ -248,11 +252,9 @@ public class NotificationShelf extends ActivatableNotificationView { notGoneIndex++; previousColor = ownColorUntinted; } + mShelfIcons.setSpeedBumpIndex(mAmbientState.getSpeedBumpIndex()); mShelfIcons.calculateIconTranslations(); mShelfIcons.applyIconStates(); - setVisibility(numViewsInShelf != 0.0f && mAmbientState.isShadeExpanded() - ? VISIBLE - : INVISIBLE); boolean hideBackground = numViewsInShelf < 1.0f; setHideBackground(hideBackground || backgroundForceHidden); if (mNotGoneIndex == -1) { @@ -275,41 +277,109 @@ 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 scrollingFast, 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; - if (viewEnd >= getTranslationY() && (mAmbientState.isShadeExpanded() + float viewEnd = viewStart + fullHeight; + float fullTransitionAmount; + float iconTransitionAmount; + float shelfStart = getTranslationY(); + if (viewEnd >= shelfStart && (mAmbientState.isShadeExpanded() || (!row.isPinned() && !row.isHeadsUpAnimatingAway()))) { - if (viewStart < getTranslationY()) { - float linearAmount = (getTranslationY() - viewStart) / transformHeight; + if (viewStart < shelfStart) { + + float fullAmount = (shelfStart - 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; + + iconTransitionAmount = (shelfStart - viewStart) / iconTransformDistance; + iconTransitionAmount = Math.min(1.0f, iconTransitionAmount); + iconTransitionAmount = 1.0f - iconTransitionAmount; + } else { - iconAppearAmount = 1.0f; + fullTransitionAmount = 1.0f; + iconTransitionAmount = 1.0f; } } else { - iconAppearAmount = 0.0f; + fullTransitionAmount = 0.0f; + iconTransitionAmount = 0.0f; } + updateIconPositioning(row, iconTransitionAmount, fullTransitionAmount, scrollingFast, + isLastChild); + return fullTransitionAmount; + } + + private void updateIconPositioning(ExpandableNotificationRow row, float iconTransitionAmount, + float fullTransitionAmount, boolean scrollingFast, 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; + if (clampedAmount == iconTransitionAmount) { + iconState.keepClampedPosition = false; + } + if (clampedAmount == fullTransitionAmount) { + iconState.useFullTransitionAmount = fullTransitionAmount == 0.0f || scrollingFast; + iconState.translateContent = mMaxLayoutHeight - getTranslationY() + - getIntrinsicHeight() > 0; + } + 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); + float contentTransformationAmount = isLastChild || iconState.translateContent + ? iconTransitionAmount + : 0.0f; + row.setContentTransformationAmount(contentTransformationAmount, isLastChild); + } + + 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 +392,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 +412,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; @@ -368,8 +425,14 @@ public class NotificationShelf extends ActivatableNotificationView { iconState.scaleY = 1.0f; iconState.hidden = false; } + if (row.isAboveShelf()) { + iconState.hidden = true; + } } - return iconAppearAmount; + } + + private NotificationIconContainer.IconState getIconState(StatusBarIconView icon) { + return mShelfIcons.getIconState(icon); } private float getFullyClosedTranslation() { @@ -386,9 +449,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() { @@ -415,13 +480,23 @@ 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); float paddingStart = NotificationUtils.interpolate(start, 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/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/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index d635bb06d318c..a2c2fd7731415 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); @@ -453,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) { @@ -465,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(); @@ -489,22 +494,41 @@ 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); + setDotAppearAmount(visibleState == STATE_DOT ? 1.0f + : visibleState == STATE_ICON ? 2.0f + : 0.0f); } } + if (!runnableAdded) { + runRunnable(endRunnable); + } + } + + private void runRunnable(Runnable runnable) { + if (runnable != null) { + runnable.run(); + } } public void setIconAppearAmount(float iconAppearAmount) { @@ -525,7 +549,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/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 03697b86e31e0..d323e4f70eb7f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -31,15 +31,23 @@ 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 * 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; private static final AnimationProperties DOT_ANIMATION_PROPERTIES = new AnimationProperties() { private AnimationFilter mAnimationFilter = new AnimationFilter().animateX(); @@ -49,6 +57,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(); @@ -59,14 +87,19 @@ 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; - 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 mAnimationStartIndex = -1; + 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); @@ -97,6 +130,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 @@ -105,6 +139,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(); @@ -121,7 +158,8 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { childState.applyToView(child); } } - mAnimationStartIndex = -1; + mAddAnimationStartIndex = -1; + mCannedAnimationStartIndex = -1; } @Override @@ -133,10 +171,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 +187,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) { @@ -177,14 +215,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; } /** @@ -194,42 +231,74 @@ 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 + OVERFLOW_EARLY_AMOUNT); + 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 + OVERFLOW_EARLY_AMOUNT) + - totalDotLength / 2 + - mIconSize * 0.5f + mStaticDotRadius; + if (isAmbient) { + visualOverflowStart = Math.min(translationX, visualOverflowStart + + mStaticDotRadius * 2 + mDotPadding); + } else { + 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(); } - 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; } @@ -250,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; @@ -295,7 +364,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { } public int getActualWidth() { - if (mActualLayoutWidth < 0) { + if (mActualLayoutWidth == NO_VALUE) { return getWidth(); } return mActualLayoutWidth; @@ -305,28 +374,90 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { mChangingViewPositions = changingViewPositions; } + public IconState getIconState(StatusBarIconView icon) { + return mIconStates.get(icon); + } + + public void setSpeedBumpIndex(int speedBumpIndex) { + mSpeedBumpIndex = speedBumpIndex; + } + + public void setOpenedAmount(float expandAmount) { + 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; public int visibleState; public boolean justAdded = true; + public boolean needsCannedAnimation; + public boolean keepClampedPosition; + public boolean useFullTransitionAmount; + public boolean translateContent; @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 +467,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/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); 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 f16c8346d1b08..75cabe715b5fb 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()); 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 4579ce69b0665..b2dae50262b12 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -4567,6 +4567,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/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); 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..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; @@ -52,6 +53,8 @@ public class AmbientState { private int mBaseZHeight; private int mMaxLayoutHeight; private ActivatableNotificationView mLastVisibleBackgroundChild; + private float mCurrentScrollVelocity; + private int mStatusBarState; public AmbientState(Context context) { reload(context); @@ -241,4 +244,20 @@ public class AmbientState { public ActivatableNotificationView getLastVisibleBackgroundChild() { return mLastVisibleBackgroundChild; } + + public void setCurrentScrollVelocity(float currentScrollVelocity) { + mCurrentScrollVelocity = currentScrollVelocity; + } + + 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/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/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index 10d995ccadb52..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); @@ -575,6 +576,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) { @@ -715,7 +719,6 @@ public class NotificationStackScrollLayout extends ViewGroup requestChildrenUpdate(); } setStackTranslation(translationY); - requestChildrenUpdate(); } private void setRequestedClipBounds(Rect clipRect) { @@ -1185,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) { @@ -2122,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 @@ -2356,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); } @@ -3979,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(); 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) {