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) {