am 807a8155: Introduced animations for the clipTopAmount of notifications.
* commit '807a81552eae005882c65df2c01ce2bed9478f4a': Introduced animations for the clipTopAmount of notifications.
This commit is contained in:
@@ -27,6 +27,7 @@ public class AnimationFilter {
|
||||
boolean animateZ;
|
||||
boolean animateScale;
|
||||
boolean animateHeight;
|
||||
boolean animateTopInset;
|
||||
boolean animateDimmed;
|
||||
boolean hasDelays;
|
||||
|
||||
@@ -60,6 +61,11 @@ public class AnimationFilter {
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnimationFilter animateTopInset() {
|
||||
animateTopInset = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnimationFilter animateDimmed() {
|
||||
animateDimmed = true;
|
||||
return this;
|
||||
@@ -84,6 +90,7 @@ public class AnimationFilter {
|
||||
animateZ |= filter.animateZ;
|
||||
animateScale |= filter.animateScale;
|
||||
animateHeight |= filter.animateHeight;
|
||||
animateTopInset |= filter.animateTopInset;
|
||||
animateDimmed |= filter.animateDimmed;
|
||||
hasDelays |= filter.hasDelays;
|
||||
}
|
||||
@@ -94,6 +101,7 @@ public class AnimationFilter {
|
||||
animateZ = false;
|
||||
animateScale = false;
|
||||
animateHeight = false;
|
||||
animateTopInset = false;
|
||||
animateDimmed = false;
|
||||
hasDelays = false;
|
||||
}
|
||||
|
||||
@@ -1599,6 +1599,7 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
new AnimationFilter()
|
||||
.animateAlpha()
|
||||
.animateHeight()
|
||||
.animateTopInset()
|
||||
.animateY()
|
||||
.animateZ()
|
||||
.hasDelays(),
|
||||
@@ -1607,6 +1608,7 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
new AnimationFilter()
|
||||
.animateAlpha()
|
||||
.animateHeight()
|
||||
.animateTopInset()
|
||||
.animateY()
|
||||
.animateZ()
|
||||
.hasDelays(),
|
||||
@@ -1615,6 +1617,7 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
new AnimationFilter()
|
||||
.animateAlpha()
|
||||
.animateHeight()
|
||||
.animateTopInset()
|
||||
.animateY()
|
||||
.animateZ()
|
||||
.hasDelays(),
|
||||
@@ -1623,6 +1626,7 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
new AnimationFilter()
|
||||
.animateAlpha()
|
||||
.animateHeight()
|
||||
.animateTopInset()
|
||||
.animateY()
|
||||
.animateDimmed()
|
||||
.animateScale()
|
||||
@@ -1651,6 +1655,7 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
new AnimationFilter()
|
||||
.animateAlpha()
|
||||
.animateHeight()
|
||||
.animateTopInset()
|
||||
.animateY()
|
||||
.animateZ()
|
||||
};
|
||||
|
||||
@@ -49,6 +49,7 @@ public class StackScrollAlgorithm {
|
||||
private int mBottomStackPeekSize;
|
||||
private int mZDistanceBetweenElements;
|
||||
private int mZBasicHeight;
|
||||
private int mRoundedRectCornerRadius;
|
||||
|
||||
private StackIndentationFunctor mTopStackIndentationFunctor;
|
||||
private StackIndentationFunctor mBottomStackIndentationFunctor;
|
||||
@@ -111,6 +112,8 @@ public class StackScrollAlgorithm {
|
||||
mZBasicHeight = (MAX_ITEMS_IN_BOTTOM_STACK + 1) * mZDistanceBetweenElements;
|
||||
mBottomStackSlowDownLength = context.getResources()
|
||||
.getDimensionPixelSize(R.dimen.bottom_stack_slow_down_length);
|
||||
mRoundedRectCornerRadius = context.getResources().getDimensionPixelSize(
|
||||
com.android.internal.R.dimen.notification_quantum_rounded_rect_radius);
|
||||
}
|
||||
|
||||
|
||||
@@ -146,6 +149,67 @@ public class StackScrollAlgorithm {
|
||||
|
||||
handleDraggedViews(ambientState, resultState, algorithmState);
|
||||
updateDimmedActivated(ambientState, resultState, algorithmState);
|
||||
updateClipping(resultState, algorithmState);
|
||||
}
|
||||
|
||||
private void updateClipping(StackScrollState resultState,
|
||||
StackScrollAlgorithmState algorithmState) {
|
||||
float previousNotificationEnd = 0;
|
||||
float previousNotificationStart = 0;
|
||||
boolean previousNotificationIsSwiped = false;
|
||||
int childCount = algorithmState.visibleChildren.size();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
ExpandableView child = algorithmState.visibleChildren.get(i);
|
||||
StackScrollState.ViewState state = resultState.getViewStateForView(child);
|
||||
float newYTranslation = state.yTranslation;
|
||||
int newHeight = state.height;
|
||||
// apply clipping and shadow
|
||||
float newNotificationEnd = newYTranslation + newHeight;
|
||||
|
||||
// In the unlocked shade we have to clip a little bit higher because of the rounded
|
||||
// corners of the notifications.
|
||||
float clippingCorrection = state.dimmed ? 0 : mRoundedRectCornerRadius;
|
||||
|
||||
// When the previous notification is swiped, we don't clip the content to the
|
||||
// bottom of it.
|
||||
float clipHeight = previousNotificationIsSwiped
|
||||
? newHeight
|
||||
: newNotificationEnd - (previousNotificationEnd - clippingCorrection);
|
||||
|
||||
updateChildClippingAndBackground(state, newHeight, clipHeight,
|
||||
(int) (newHeight - (previousNotificationStart - newYTranslation)));
|
||||
|
||||
if (!child.isTransparent()) {
|
||||
// Only update the previous values if we are not transparent,
|
||||
// otherwise we would clip to a transparent view.
|
||||
previousNotificationStart = newYTranslation + child.getClipTopAmount();
|
||||
previousNotificationEnd = newNotificationEnd;
|
||||
previousNotificationIsSwiped = child.getTranslationX() != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the shadow outline and the clipping for a view.
|
||||
*
|
||||
* @param state the viewState to update
|
||||
* @param realHeight the currently applied height of the view
|
||||
* @param clipHeight the desired clip height, the rest of the view will be clipped from the top
|
||||
* @param backgroundHeight the desired background height. The shadows of the view will be
|
||||
* based on this height and the content will be clipped from the top
|
||||
*/
|
||||
private void updateChildClippingAndBackground(StackScrollState.ViewState state, int realHeight,
|
||||
float clipHeight, int backgroundHeight) {
|
||||
if (realHeight > clipHeight) {
|
||||
state.topOverLap = (int) (realHeight - clipHeight);
|
||||
} else {
|
||||
state.topOverLap = 0;
|
||||
}
|
||||
if (realHeight > backgroundHeight) {
|
||||
state.clipTopAmount = (realHeight - backgroundHeight);
|
||||
} else {
|
||||
state.clipTopAmount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package com.android.systemui.statusbar.stack;
|
||||
|
||||
import android.graphics.Outline;
|
||||
import android.graphics.Rect;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
@@ -37,15 +36,12 @@ public class StackScrollState {
|
||||
private static final String CHILD_NOT_FOUND_TAG = "StackScrollStateNoSuchChild";
|
||||
|
||||
private final ViewGroup mHostView;
|
||||
private final int mRoundedRectCornerRadius;
|
||||
private Map<ExpandableView, ViewState> mStateMap;
|
||||
private final Rect mClipRect = new Rect();
|
||||
|
||||
public StackScrollState(ViewGroup hostView) {
|
||||
mHostView = hostView;
|
||||
mStateMap = new HashMap<ExpandableView, ViewState>();
|
||||
mRoundedRectCornerRadius = mHostView.getResources().getDimensionPixelSize(
|
||||
com.android.internal.R.dimen.notification_quantum_rounded_rect_radius);
|
||||
}
|
||||
|
||||
public ViewGroup getHostView() {
|
||||
@@ -83,9 +79,6 @@ public class StackScrollState {
|
||||
*/
|
||||
public void apply() {
|
||||
int numChildren = mHostView.getChildCount();
|
||||
float previousNotificationEnd = 0;
|
||||
float previousNotificationStart = 0;
|
||||
boolean previousNotificationIsSwiped = false;
|
||||
for (int i = 0; i < numChildren; i++) {
|
||||
ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
|
||||
ViewState state = mStateMap.get(child);
|
||||
@@ -155,39 +148,41 @@ public class StackScrollState {
|
||||
// apply dimming
|
||||
child.setDimmed(state.dimmed, false /* animate */);
|
||||
|
||||
// apply clipping and shadow
|
||||
float newNotificationEnd = newYTranslation + newHeight;
|
||||
float oldClipTopAmount = child.getClipTopAmount();
|
||||
if (oldClipTopAmount != state.clipTopAmount) {
|
||||
child.setClipTopAmount(state.clipTopAmount);
|
||||
}
|
||||
|
||||
// In the unlocked shade we have to clip a little bit higher because of the rounded
|
||||
// corners of the notifications.
|
||||
float clippingCorrection = state.dimmed ? 0 : mRoundedRectCornerRadius;
|
||||
|
||||
// When the previous notification is swiped, we don't clip the content to the
|
||||
// bottom of it.
|
||||
float clipHeight = previousNotificationIsSwiped
|
||||
? newHeight
|
||||
: newNotificationEnd - (previousNotificationEnd - clippingCorrection);
|
||||
|
||||
updateChildClippingAndBackground(child, newHeight,
|
||||
clipHeight,
|
||||
(int) (newHeight - (previousNotificationStart - newYTranslation)));
|
||||
|
||||
if (!child.isTransparent()) {
|
||||
// Only update the previous values if we are not transparent,
|
||||
// otherwise we would clip to a transparent view.
|
||||
previousNotificationStart = newYTranslation + child.getClipTopAmount();
|
||||
previousNotificationEnd = newNotificationEnd;
|
||||
previousNotificationIsSwiped = child.getTranslationX() != 0;
|
||||
if (state.topOverLap != 0) {
|
||||
updateChildClip(child, newHeight, state.topOverLap);
|
||||
} else {
|
||||
child.setClipBounds(null);
|
||||
}
|
||||
|
||||
if(child instanceof SpeedBumpView) {
|
||||
performSpeedBumpAnimation(i, (SpeedBumpView) child, newNotificationEnd,
|
||||
float speedBumpEnd = newYTranslation + newHeight;
|
||||
performSpeedBumpAnimation(i, (SpeedBumpView) child, speedBumpEnd,
|
||||
newYTranslation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the clipping of a view
|
||||
*
|
||||
* @param child the view to update
|
||||
* @param height the currently applied height of the view
|
||||
* @param clipInset how much should this view be clipped from the top
|
||||
*/
|
||||
private void updateChildClip(View child, int height, int clipInset) {
|
||||
mClipRect.set(0,
|
||||
clipInset,
|
||||
child.getWidth(),
|
||||
height);
|
||||
child.setClipBounds(mClipRect);
|
||||
}
|
||||
|
||||
private void performSpeedBumpAnimation(int i, SpeedBumpView speedBump, float speedBumpEnd,
|
||||
float speedBumpStart) {
|
||||
View nextChild = getNextChildNotGone(i);
|
||||
@@ -216,45 +211,6 @@ public class StackScrollState {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the shadow outline and the clipping for a view.
|
||||
*
|
||||
* @param child the view to update
|
||||
* @param realHeight the currently applied height of the view
|
||||
* @param clipHeight the desired clip height, the rest of the view will be clipped from the top
|
||||
* @param backgroundHeight the desired background height. The shadows of the view will be
|
||||
* based on this height and the content will be clipped from the top
|
||||
*/
|
||||
private void updateChildClippingAndBackground(ExpandableView child, int realHeight,
|
||||
float clipHeight, int backgroundHeight) {
|
||||
if (realHeight > clipHeight) {
|
||||
updateChildClip(child, realHeight, clipHeight);
|
||||
} else {
|
||||
child.setClipBounds(null);
|
||||
}
|
||||
if (realHeight > backgroundHeight) {
|
||||
child.setClipTopAmount(realHeight - backgroundHeight);
|
||||
} else {
|
||||
child.setClipTopAmount(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the clipping of a view
|
||||
*
|
||||
* @param child the view to update
|
||||
* @param height the currently applied height of the view
|
||||
* @param clipHeight the desired clip height, the rest of the view will be clipped from the top
|
||||
*/
|
||||
private void updateChildClip(View child, int height, float clipHeight) {
|
||||
int clipInset = (int) (height - clipHeight);
|
||||
mClipRect.set(0,
|
||||
clipInset,
|
||||
child.getWidth(),
|
||||
height);
|
||||
child.setClipBounds(mClipRect);
|
||||
}
|
||||
|
||||
public static class ViewState {
|
||||
|
||||
// These are flags such that we can create masks for filtering.
|
||||
@@ -275,6 +231,18 @@ public class StackScrollState {
|
||||
float scale;
|
||||
boolean dimmed;
|
||||
|
||||
/**
|
||||
* The amount which the view should be clipped from the top. This is calculated to
|
||||
* perceive consistent shadows.
|
||||
*/
|
||||
int clipTopAmount;
|
||||
|
||||
/**
|
||||
* How much does the child overlap with the previous view on the top? Can be used for
|
||||
* a clipping optimization
|
||||
*/
|
||||
int topOverLap;
|
||||
|
||||
/**
|
||||
* The index of the view, only accounting for views not equal to GONE
|
||||
*/
|
||||
|
||||
@@ -133,10 +133,11 @@ public class StackStateAnimator {
|
||||
boolean scaleChanging = child.getScaleX() != viewState.scale;
|
||||
boolean alphaChanging = alpha != child.getAlpha();
|
||||
boolean heightChanging = viewState.height != child.getActualHeight();
|
||||
boolean topInsetChanging = viewState.clipTopAmount != child.getClipTopAmount();
|
||||
boolean wasAdded = mNewAddChildren.contains(child);
|
||||
boolean hasDelays = mAnimationFilter.hasDelays;
|
||||
boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || scaleChanging ||
|
||||
alphaChanging || heightChanging;
|
||||
alphaChanging || heightChanging || topInsetChanging;
|
||||
long delay = 0;
|
||||
if (hasDelays && isDelayRelevant || wasAdded) {
|
||||
delay = calculateChildAnimationDelay(viewState, finalState);
|
||||
@@ -167,6 +168,11 @@ public class StackStateAnimator {
|
||||
startHeightAnimation(child, viewState, delay);
|
||||
}
|
||||
|
||||
// start top inset animation
|
||||
if (topInsetChanging) {
|
||||
startInsetAnimation(child, viewState, delay);
|
||||
}
|
||||
|
||||
// start dimmed animation
|
||||
child.setDimmed(viewState.dimmed, mAnimationFilter.animateDimmed);
|
||||
|
||||
@@ -280,6 +286,64 @@ public class StackStateAnimator {
|
||||
child.setTag(TAG_END_HEIGHT, newEndValue);
|
||||
}
|
||||
|
||||
private void startInsetAnimation(final ExpandableView child,
|
||||
StackScrollState.ViewState viewState, long delay) {
|
||||
Integer previousStartValue = getChildTag(child, TAG_START_TOP_INSET);
|
||||
Integer previousEndValue = getChildTag(child, TAG_END_TOP_INSET);
|
||||
int newEndValue = viewState.clipTopAmount;
|
||||
if (previousEndValue != null && previousEndValue == newEndValue) {
|
||||
return;
|
||||
}
|
||||
ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TOP_INSET);
|
||||
if (!mAnimationFilter.animateTopInset) {
|
||||
// just a local update was performed
|
||||
if (previousAnimator != null) {
|
||||
// we need to increase all animation keyframes of the previous animator by the
|
||||
// relative change to the end value
|
||||
PropertyValuesHolder[] values = previousAnimator.getValues();
|
||||
int relativeDiff = newEndValue - previousEndValue;
|
||||
int newStartValue = previousStartValue + relativeDiff;
|
||||
values[0].setIntValues(newStartValue, newEndValue);
|
||||
child.setTag(TAG_START_TOP_INSET, newStartValue);
|
||||
child.setTag(TAG_END_TOP_INSET, newEndValue);
|
||||
previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
|
||||
return;
|
||||
} else {
|
||||
// no new animation needed, let's just apply the value
|
||||
child.setClipTopAmount(newEndValue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ValueAnimator animator = ValueAnimator.ofInt(child.getClipTopAmount(), newEndValue);
|
||||
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
child.setClipTopAmount((int) animation.getAnimatedValue());
|
||||
}
|
||||
});
|
||||
animator.setInterpolator(mFastOutSlowInInterpolator);
|
||||
long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
|
||||
animator.setDuration(newDuration);
|
||||
if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
|
||||
animator.setStartDelay(delay);
|
||||
}
|
||||
animator.addListener(getGlobalAnimationFinishedListener());
|
||||
// remove the tag when the animation is finished
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
child.setTag(TAG_ANIMATOR_TOP_INSET, null);
|
||||
child.setTag(TAG_START_TOP_INSET, null);
|
||||
child.setTag(TAG_END_TOP_INSET, null);
|
||||
}
|
||||
});
|
||||
startAnimator(animator);
|
||||
child.setTag(TAG_ANIMATOR_TOP_INSET, animator);
|
||||
child.setTag(TAG_START_TOP_INSET, child.getClipTopAmount());
|
||||
child.setTag(TAG_END_TOP_INSET, newEndValue);
|
||||
}
|
||||
|
||||
private void startAlphaAnimation(final ExpandableView child,
|
||||
final StackScrollState.ViewState viewState, long delay) {
|
||||
Float previousStartValue = getChildTag(child,TAG_START_ALPHA);
|
||||
|
||||
Reference in New Issue
Block a user