Improved the clipping of notification groups

Groups now correctly respect the clipBottomAmount and
animations work much better when swiping things away.

Also removed the group measuring logic when expanding,
Since we're now not shrinking the first notification anymore!

This also fixed a bug where a child could be invisible

Test: Add group, swipe children away
Bug: 32437839
Bug: 33203156
Change-Id: I255aa9695086e64eb10c7dccdc6122d8a8572bb5
This commit is contained in:
Selim Cinek
2016-11-21 17:21:13 -08:00
parent 1f8c21c116
commit b3dadccbc5
9 changed files with 57 additions and 84 deletions

View File

@@ -1685,6 +1685,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
if (mGuts != null) {
mGuts.setClipBottomAmount(clipBottomAmount);
}
if (mChildrenContainer != null) {
mChildrenContainer.setClipBottomAmount(clipBottomAmount);
}
}
public boolean isMaxExpandHeightInitialized() {
@@ -1863,9 +1866,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
super.applyToView(view);
if (view instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
if (this.isBottomClipped) {
row.setClipToActualHeight(true);
}
row.applyChildrenState(mOverallState);
}
}

View File

@@ -41,7 +41,7 @@ public abstract class ExpandableOutlineView extends ExpandableView {
outline.setRect(translation,
mClipTopAmount,
getWidth() + translation,
Math.max(getActualHeight(), mClipTopAmount));
Math.max(getActualHeight() - mClipBottomAmount, mClipTopAmount));
} else {
outline.setRect(mOutlineRect);
}
@@ -66,6 +66,12 @@ public abstract class ExpandableOutlineView extends ExpandableView {
invalidateOutline();
}
@Override
public void setClipBottomAmount(int clipBottomAmount) {
super.setClipBottomAmount(clipBottomAmount);
invalidateOutline();
}
protected void setOutlineAlpha(float alpha) {
if (alpha != mOutlineAlpha) {
mOutlineAlpha = alpha;

View File

@@ -38,7 +38,7 @@ public abstract class ExpandableView extends FrameLayout {
protected OnHeightChangedListener mOnHeightChangedListener;
private int mActualHeight;
protected int mClipTopAmount;
private float mClipBottomAmount;
protected int mClipBottomAmount;
private boolean mDark;
private ArrayList<View> mMatchParentViews = new ArrayList<View>();
private static Rect mClipRect = new Rect();
@@ -241,7 +241,7 @@ public abstract class ExpandableView extends FrameLayout {
return mClipTopAmount;
}
public float getClipBottomAmount() {
public int getClipBottomAmount() {
return mClipBottomAmount;
}
@@ -354,11 +354,8 @@ public abstract class ExpandableView extends FrameLayout {
private void updateClipping() {
if (mClipToActualHeight) {
int top = getClipTopAmount();
if (top >= getActualHeight()) {
top = getActualHeight() - 1;
}
mClipRect.set(0, top, getWidth(), (int) (getActualHeight() + getExtraBottomPadding()
- mClipBottomAmount));
mClipRect.set(0, top, getWidth(), Math.max(getActualHeight() + getExtraBottomPadding()
- mClipBottomAmount, top));
setClipBounds(mClipRect);
} else {
setClipBounds(null);

View File

@@ -156,7 +156,6 @@ public class NotificationShelf extends ActivatableNotificationView {
mShelfState.alpha = 1.0f;
mShelfState.belowSpeedBump = mAmbientState.getSpeedBumpIndex() == 0;
mShelfState.shadowAlpha = 1.0f;
mShelfState.isBottomClipped = false;
mShelfState.hideSensitive = false;
mShelfState.xTranslation = getTranslationX();
if (mNotGoneIndex != -1) {

View File

@@ -48,7 +48,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
return mAnimationFilter;
}
}.setDuration(200);
private static final AnimationProperties ADD_ICON_PROPERTIES = new AnimationProperties() {
private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha();

View File

@@ -106,11 +106,6 @@ public class ExpandableViewState extends ViewState {
*/
public int location;
/**
* Whether a child in a group is being clipped at the bottom.
*/
public boolean isBottomClipped;
@Override
public void copyFrom(ViewState viewState) {
super.copyFrom(viewState);
@@ -125,7 +120,6 @@ public class ExpandableViewState extends ViewState {
clipTopAmount = svs.clipTopAmount;
notGoneIndex = svs.notGoneIndex;
location = svs.location;
isBottomClipped = svs.isBottomClipped;
}
}

View File

@@ -76,6 +76,7 @@ public class NotificationChildrenContainer extends ViewGroup {
private NotificationViewWrapper mNotificationHeaderWrapper;
private NotificationHeaderUtil mHeaderUtil;
private ViewState mHeaderViewState;
private int mClipBottomAmount;
public NotificationChildrenContainer(Context context) {
this(context, null);
@@ -433,7 +434,6 @@ public class NotificationChildrenContainer extends ViewGroup {
boolean childrenExpanded = !mNotificationParent.isGroupExpansionChanging()
&& mChildrenExpanded;
int parentHeight = parentState.height;
for (int i = 0; i < childCount; i++) {
ExpandableNotificationRow child = mChildren.get(i);
if (!firstChild) {
@@ -457,20 +457,9 @@ public class NotificationChildrenContainer extends ViewGroup {
ExpandableViewState childState = resultState.getViewStateForView(child);
int intrinsicHeight = child.getIntrinsicHeight();
if (childrenExpanded) {
// When a group is expanded and moving into bottom stack, the bottom visible child
// adjusts its height to move into it. Children after it are hidden.
if (updateChildStateForExpandedGroup(child, parentHeight, childState, yPosition)) {
// Clipping might be deactivated if the view is transforming, however, clipping
// the child into the bottom stack should take precedent over this.
childState.isBottomClipped = true;
}
} else {
childState.hidden = false;
childState.height = intrinsicHeight;
childState.isBottomClipped = false;
}
childState.height = intrinsicHeight;
childState.yTranslation = yPosition;
childState.hidden = false;
// When the group is expanded, the children cast the shadows rather than the parent
// so use the parent's elevation here.
childState.zTranslation = childrenExpanded
@@ -601,6 +590,34 @@ public class NotificationChildrenContainer extends ViewGroup {
if (mHeaderViewState != null) {
mHeaderViewState.applyToView(mNotificationHeader);
}
updateChildrenClipping();
}
private void updateChildrenClipping() {
int childCount = mChildren.size();
int layoutEnd = mNotificationParent.getActualHeight() - mClipBottomAmount;
for (int i = 0; i < childCount; i++) {
ExpandableNotificationRow child = mChildren.get(i);
if (child.getVisibility() == GONE) {
continue;
}
float childTop = child.getTranslationY();
float childBottom = childTop + child.getActualHeight();
boolean visible = true;
int clipBottomAmount = 0;
if (childTop > layoutEnd) {
visible = false;
} else if (childBottom > layoutEnd) {
clipBottomAmount = (int) (childBottom - layoutEnd);
}
boolean isVisible = child.getVisibility() == VISIBLE;
if (visible != isVisible) {
child.setVisibility(visible ? VISIBLE : INVISIBLE);
}
child.setClipBottomAmount(clipBottomAmount);
}
}
/**
@@ -653,6 +670,7 @@ public class NotificationChildrenContainer extends ViewGroup {
if (mNotificationHeader != null) {
mHeaderViewState.applyToView(mNotificationHeader);
}
updateChildrenClipping();
}
public ExpandableNotificationRow getViewAtPosition(float y) {
@@ -889,4 +907,9 @@ public class NotificationChildrenContainer extends ViewGroup {
}
}
}
public void setClipBottomAmount(int clipBottomAmount) {
mClipBottomAmount = clipBottomAmount;
updateChildrenClipping();
}
}

View File

@@ -766,12 +766,14 @@ public class NotificationStackScrollLayout extends ViewGroup
*/
private float getAppearEndPosition() {
int appearPosition;
int minNotificationsForShelf = 1;
if (mTrackingHeadsUp || mHeadsUpManager.hasPinnedHeadsUp()) {
appearPosition = mHeadsUpManager.getTopHeadsUpPinnedHeight();
minNotificationsForShelf = 2;
} else {
appearPosition = getFirstItemMinHeight();
appearPosition = 0;
}
if (getNotGoneChildCount() > 1) {
if (getNotGoneChildCount() >= minNotificationsForShelf) {
appearPosition += mShelf.getIntrinsicHeight();
}
return appearPosition + (onKeyguard() ? mTopPadding : mIntrinsicPadding);
@@ -1092,29 +1094,7 @@ public class NotificationStackScrollLayout extends ViewGroup
@Override
public int getMaxExpandHeight(ExpandableView view) {
int maxContentHeight = view.getMaxContentHeight();
if (view.isSummaryWithChildren() && view.getParent() == this) {
// Faking a measure with the group expanded to simulate how the group would look if
// it was. Doing a calculation here would be highly non-trivial because of the
// algorithm
mGroupExpandedForMeasure = true;
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
mGroupManager.toggleGroupExpansion(row.getStatusBarNotification());
row.setForceUnlocked(true);
mAmbientState.setLayoutHeight(mMaxLayoutHeight);
mStackScrollAlgorithm.getStackScrollState(mAmbientState, mCurrentStackScrollState);
mAmbientState.setLayoutHeight(getLayoutHeight());
mGroupManager.toggleGroupExpansion(
row.getStatusBarNotification());
mGroupExpandedForMeasure = false;
row.setForceUnlocked(false);
ExpandableViewState viewState = mCurrentStackScrollState.getViewStateForView(view);
if (viewState != null) {
// The view could have been removed
return Math.min(viewState.height, maxContentHeight);
}
}
return maxContentHeight;
return view.getMaxContentHeight();
}
public void setScrollingEnabled(boolean enable) {

View File

@@ -323,9 +323,6 @@ public class StackScrollAlgorithm {
int childHeight = getMaxAllowedChildHeight(child);
childViewState.yTranslation = currentYPosition;
boolean isDismissView = child instanceof DismissView;
if (i == 0) {
updateFirstChildHeight(child, childViewState, childHeight, algorithmState, ambientState);
}
childViewState.location = ExpandableViewState.LOCATION_MAIN_AREA;
if (isDismissView) {
@@ -449,29 +446,6 @@ public class StackScrollAlgorithm {
return child == null? mCollapsedSize : child.getHeight();
}
/**
* Update the height of the first child i.e clamp it to the bottom stack
* @param child the child to update
* @param childViewState the viewstate of the child
* @param childHeight the height of the child
* @param algorithmState the algorithm state
* @param ambientState The ambient state of the algorithm
*/
protected void updateFirstChildHeight(ExpandableView child, ExpandableViewState childViewState,
int childHeight, StackScrollAlgorithmState algorithmState,
AmbientState ambientState) {
int bottomStart= ambientState.getInnerHeight();
if (algorithmState.visibleChildren.size() > 1) {
bottomStart -= ambientState.getShelf().getIntrinsicHeight()
- mPaddingBetweenElements;
}
bottomStart += ambientState.getScrollY();
// Collapse and expand the first child while the shade is being expanded
childViewState.height = (int) Math.max(Math.min(bottomStart, (float) childHeight),
child.getCollapsedHeight());
}
/**
* Calculate the Z positions for all children based on the number of items in both stacks and
* save it in the resultState