Merge changes I25effa3e,Ibb58d1aa,I92454a1b,I878d20a8,I21037b40, ...

* changes:
  Enforcing padding on the bottom of the keyguard
  Fixes that notifications were sometimes clipped on the lockscreen
  Don't reset the velocity when flinging down just because of falsing
  Don't do icon animations if we are scrolling fast
  Don't translate the notification content while scrolling
  Fixed the scrim drawing in seascape
  Placed the overflow indicator perfectly in the collapsed center
  Improved the low priority behavior with the shelf
  Improved the performance of the notification shelf
  Fixed a bug where heads up notifications had no icon
  Added possibility to use canned animation for icons
This commit is contained in:
Selim Cinek
2016-12-08 18:48:45 +00:00
committed by Android (Google) Code Review
20 changed files with 560 additions and 239 deletions

View File

@@ -81,6 +81,9 @@
<!-- Height of a heads up notification in the status bar -->
<dimen name="notification_max_heads_up_height">148dp</dimen>
<!-- a threshold in dp per second that is considered fast scrolling -->
<dimen name="scroll_fast_threshold">1500dp</dimen>
<!-- Height of a the shelf with the notification icons -->
<dimen name="notification_shelf_height">32dp</dimen>
@@ -94,6 +97,9 @@
<!-- The amount the content shifts upwards when transforming into the icon -->
<dimen name="notification_icon_transform_content_shift">32dp</dimen>
<!-- The padding on the bottom of the notifications on the keyguard -->
<dimen name="keyguard_indication_bottom_padding">12sp</dimen>
<!-- Minimum layouted height of a notification in the statusbar-->
<dimen name="min_notification_layout_height">48dp</dimen>

View File

@@ -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);
}
/**

View File

@@ -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);
}

View File

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

View File

@@ -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<StatusBarIconView> 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<View, NotificationIconContainer.IconState> 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) {

View File

@@ -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();
}
}
}
}

View File

@@ -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);
}
}

View File

@@ -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<NotificationData.Entry> 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<NotificationData.Entry, StatusBarIconView> function,
NotificationIconContainer hostLayout) {
NotificationIconContainer hostLayout, boolean showAmbient) {
ArrayList<StatusBarIconView> 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));
}
}

View File

@@ -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<View, IconState> mIconStates = new WeakHashMap<>();
private final HashMap<View, IconState> 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<View, IconState> 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);
}
}
}
}

View File

@@ -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);

View File

@@ -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());

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -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;

View File

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

View File

@@ -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.
*/

View File

@@ -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();

View File

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