Merge "Made stack scroller animation and apply logic reusable"

This commit is contained in:
Selim Cinek
2015-03-13 21:40:19 +00:00
committed by Android (Google) Code Review
7 changed files with 399 additions and 292 deletions

View File

@@ -161,7 +161,7 @@ import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener;
import com.android.systemui.statusbar.stack.StackScrollState.ViewState;
import com.android.systemui.statusbar.stack.StackViewState;
import com.android.systemui.volume.VolumeComponent;
import java.io.FileDescriptor;
@@ -444,10 +444,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
// Fingerprint (as computed by getLoggingFingerprint() of the last logged state.
private int mLastLoggedStateFingerprint;
private static final int VISIBLE_LOCATIONS = ViewState.LOCATION_FIRST_CARD
| ViewState.LOCATION_TOP_STACK_PEEKING
| ViewState.LOCATION_MAIN_AREA
| ViewState.LOCATION_BOTTOM_STACK_PEEKING;
private static final int VISIBLE_LOCATIONS = StackViewState.LOCATION_FIRST_CARD
| StackViewState.LOCATION_TOP_STACK_PEEKING
| StackViewState.LOCATION_MAIN_AREA
| StackViewState.LOCATION_BOTTOM_STACK_PEEKING;
private final OnChildLocationsChangedListener mNotificationLocationsChangedListener =
new OnChildLocationsChangedListener() {

View File

@@ -46,7 +46,6 @@ import com.android.systemui.statusbar.StackScrollerDecorView;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
import com.android.systemui.statusbar.policy.ScrollAdapter;
import com.android.systemui.statusbar.stack.StackScrollState.ViewState;
import java.util.ArrayList;
import java.util.HashSet;
@@ -375,15 +374,15 @@ public class NotificationStackScrollLayout extends ViewGroup
* Returns the location the given child is currently rendered at.
*
* @param child the child to get the location for
* @return one of {@link ViewState}'s <code>LOCATION_*</code> constants
* @return one of {@link StackViewState}'s <code>LOCATION_*</code> constants
*/
public int getChildLocation(View child) {
ViewState childViewState = mCurrentStackScrollState.getViewStateForView(child);
StackViewState childViewState = mCurrentStackScrollState.getViewStateForView(child);
if (childViewState == null) {
return ViewState.LOCATION_UNKNOWN;
return StackViewState.LOCATION_UNKNOWN;
}
if (childViewState.gone) {
return ViewState.LOCATION_GONE;
return StackViewState.LOCATION_GONE;
}
return childViewState.location;
}

View File

@@ -178,7 +178,7 @@ public class StackScrollAlgorithm {
int childCount = algorithmState.visibleChildren.size();
for (int i = 0; i < childCount; i++) {
View child = algorithmState.visibleChildren.get(i);
StackScrollState.ViewState childViewState = resultState.getViewStateForView(child);
StackViewState childViewState = resultState.getViewStateForView(child);
// The speed bump can also be gone, so equality needs to be taken when comparing
// indices.
@@ -194,7 +194,7 @@ public class StackScrollAlgorithm {
int childCount = algorithmState.visibleChildren.size();
for (int i = 0; i < childCount; i++) {
ExpandableView child = algorithmState.visibleChildren.get(i);
StackScrollState.ViewState state = resultState.getViewStateForView(child);
StackViewState state = resultState.getViewStateForView(child);
float newYTranslation = state.yTranslation + state.height * (1f - state.scale) / 2f;
float newHeight = state.height * state.scale;
// apply clipping and shadow
@@ -242,8 +242,8 @@ public class StackScrollAlgorithm {
* @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,
float realHeight, float clipHeight, float backgroundHeight) {
private void updateChildClippingAndBackground(StackViewState state, float realHeight,
float clipHeight, float backgroundHeight) {
if (realHeight > clipHeight) {
// Rather overlap than create a hole.
state.topOverLap = (int) Math.floor((realHeight - clipHeight) / state.scale);
@@ -270,7 +270,7 @@ public class StackScrollAlgorithm {
int childCount = algorithmState.visibleChildren.size();
for (int i = 0; i < childCount; i++) {
View child = algorithmState.visibleChildren.get(i);
StackScrollState.ViewState childViewState = resultState.getViewStateForView(child);
StackViewState childViewState = resultState.getViewStateForView(child);
childViewState.dimmed = dimmed;
childViewState.dark = dark;
childViewState.hideSensitive = hideSensitive;
@@ -297,14 +297,14 @@ public class StackScrollAlgorithm {
if (!draggedViews.contains(nextChild)) {
// only if the view is not dragged itself we modify its state to be fully
// visible
StackScrollState.ViewState viewState = resultState.getViewStateForView(
StackViewState viewState = resultState.getViewStateForView(
nextChild);
// The child below the dragged one must be fully visible
viewState.alpha = 1;
}
// Lets set the alpha to the one it currently has, as its currently being dragged
StackScrollState.ViewState viewState = resultState.getViewStateForView(draggedView);
StackViewState viewState = resultState.getViewStateForView(draggedView);
// The dragged child should keep the set alpha
viewState.alpha = draggedView.getAlpha();
}
@@ -320,12 +320,14 @@ public class StackScrollAlgorithm {
int childCount = hostView.getChildCount();
state.visibleChildren.clear();
state.visibleChildren.ensureCapacity(childCount);
int notGoneIndex = 0;
for (int i = 0; i < childCount; i++) {
ExpandableView v = (ExpandableView) hostView.getChildAt(i);
if (v.getVisibility() != View.GONE) {
StackScrollState.ViewState viewState = resultState.getViewStateForView(v);
viewState.notGoneIndex = state.visibleChildren.size();
StackViewState viewState = resultState.getViewStateForView(v);
viewState.notGoneIndex = notGoneIndex;
state.visibleChildren.add(v);
notGoneIndex++;
}
}
}
@@ -355,8 +357,8 @@ public class StackScrollAlgorithm {
int numberOfElementsCompletelyIn = (int) algorithmState.itemsInTopStack;
for (int i = 0; i < childCount; i++) {
ExpandableView child = algorithmState.visibleChildren.get(i);
StackScrollState.ViewState childViewState = resultState.getViewStateForView(child);
childViewState.location = StackScrollState.ViewState.LOCATION_UNKNOWN;
StackViewState childViewState = resultState.getViewStateForView(child);
childViewState.location = StackViewState.LOCATION_UNKNOWN;
int childHeight = getMaxAllowedChildHeight(child);
float yPositionInScrollViewAfterElement = yPositionInScrollView
+ childHeight
@@ -413,7 +415,7 @@ public class StackScrollAlgorithm {
} else {
// Case 3:
// We are in the regular scroll area.
childViewState.location = StackScrollState.ViewState.LOCATION_MAIN_AREA;
childViewState.location = StackViewState.LOCATION_MAIN_AREA;
clampYTranslation(childViewState, childHeight);
}
@@ -427,9 +429,9 @@ public class StackScrollAlgorithm {
bottomPeekStart - mCollapseSecondCardPadding
- childViewState.yTranslation, mCollapsedSize);
}
childViewState.location = StackScrollState.ViewState.LOCATION_FIRST_CARD;
childViewState.location = StackViewState.LOCATION_FIRST_CARD;
}
if (childViewState.location == StackScrollState.ViewState.LOCATION_UNKNOWN) {
if (childViewState.location == StackViewState.LOCATION_UNKNOWN) {
Log.wtf(LOG_TAG, "Failed to assign location for child " + i);
}
currentYPosition = childViewState.yTranslation + childHeight + mPaddingBetweenElements;
@@ -445,7 +447,7 @@ public class StackScrollAlgorithm {
* @param childViewState the view state of the child
* @param childHeight the height of this child
*/
private void clampYTranslation(StackScrollState.ViewState childViewState, int childHeight) {
private void clampYTranslation(StackViewState childViewState, int childHeight) {
clampPositionToBottomStackStart(childViewState, childHeight);
clampPositionToTopStackEnd(childViewState, childHeight);
}
@@ -457,7 +459,7 @@ public class StackScrollAlgorithm {
* @param childViewState the view state of the child
* @param childHeight the height of this child
*/
private void clampPositionToBottomStackStart(StackScrollState.ViewState childViewState,
private void clampPositionToBottomStackStart(StackViewState childViewState,
int childHeight) {
childViewState.yTranslation = Math.min(childViewState.yTranslation,
mInnerHeight - mBottomStackPeekSize - mCollapseSecondCardPadding - childHeight);
@@ -470,7 +472,7 @@ public class StackScrollAlgorithm {
* @param childViewState the view state of the child
* @param childHeight the height of this child
*/
private void clampPositionToTopStackEnd(StackScrollState.ViewState childViewState,
private void clampPositionToTopStackEnd(StackViewState childViewState,
int childHeight) {
childViewState.yTranslation = Math.max(childViewState.yTranslation,
mCollapsedSize - childHeight);
@@ -489,7 +491,7 @@ public class StackScrollAlgorithm {
private void updateStateForChildTransitioningInBottom(StackScrollAlgorithmState algorithmState,
float transitioningPositionStart, float bottomPeakStart, float currentYPosition,
StackScrollState.ViewState childViewState, int childHeight) {
StackViewState childViewState, int childHeight) {
// This is the transitioning element on top of bottom stack, calculate how far we are in.
algorithmState.partialInBottom = 1.0f - (
@@ -510,11 +512,11 @@ public class StackScrollAlgorithm {
// We want at least to be at the end of the top stack when collapsing
clampPositionToTopStackEnd(childViewState, newHeight);
childViewState.location = StackScrollState.ViewState.LOCATION_MAIN_AREA;
childViewState.location = StackViewState.LOCATION_MAIN_AREA;
}
private void updateStateForChildFullyInBottomStack(StackScrollAlgorithmState algorithmState,
float transitioningPositionStart, StackScrollState.ViewState childViewState,
float transitioningPositionStart, StackViewState childViewState,
int childHeight) {
float currentYPosition;
@@ -524,7 +526,7 @@ public class StackScrollAlgorithm {
currentYPosition = transitioningPositionStart
+ mBottomStackIndentationFunctor.getValue(algorithmState.itemsInBottomStack)
- mPaddingBetweenElements;
childViewState.location = StackScrollState.ViewState.LOCATION_BOTTOM_STACK_PEEKING;
childViewState.location = StackViewState.LOCATION_BOTTOM_STACK_PEEKING;
} else {
// we are fully inside the stack
if (algorithmState.itemsInBottomStack > MAX_ITEMS_IN_BOTTOM_STACK + 2) {
@@ -533,7 +535,7 @@ public class StackScrollAlgorithm {
> MAX_ITEMS_IN_BOTTOM_STACK + 1) {
childViewState.alpha = 1.0f - algorithmState.partialInBottom;
}
childViewState.location = StackScrollState.ViewState.LOCATION_BOTTOM_STACK_HIDDEN;
childViewState.location = StackViewState.LOCATION_BOTTOM_STACK_HIDDEN;
currentYPosition = mInnerHeight;
}
childViewState.yTranslation = currentYPosition - childHeight;
@@ -542,7 +544,7 @@ public class StackScrollAlgorithm {
private void updateStateForTopStackChild(StackScrollAlgorithmState algorithmState,
int numberOfElementsCompletelyIn, int i, int childHeight,
StackScrollState.ViewState childViewState, float scrollOffset) {
StackViewState childViewState, float scrollOffset) {
// First we calculate the index relative to the current stack window of size at most
@@ -574,7 +576,7 @@ public class StackScrollAlgorithm {
- mTopStackIndentationFunctor.getValue(numItemsBefore);
childViewState.yTranslation = currentChildEndY - childHeight;
}
childViewState.location = StackScrollState.ViewState.LOCATION_TOP_STACK_PEEKING;
childViewState.location = StackViewState.LOCATION_TOP_STACK_PEEKING;
} else {
if (paddedIndex == -1) {
childViewState.alpha = 1.0f - algorithmState.partialInTop;
@@ -583,7 +585,7 @@ public class StackScrollAlgorithm {
childViewState.alpha = 0.0f;
}
childViewState.yTranslation = mCollapsedSize - childHeight;
childViewState.location = StackScrollState.ViewState.LOCATION_TOP_STACK_HIDDEN;
childViewState.location = StackViewState.LOCATION_TOP_STACK_HIDDEN;
}
@@ -605,7 +607,7 @@ public class StackScrollAlgorithm {
// find the number of elements in the top stack.
for (int i = 0; i < childCount; i++) {
ExpandableView child = algorithmState.visibleChildren.get(i);
StackScrollState.ViewState childViewState = resultState.getViewStateForView(child);
StackViewState childViewState = resultState.getViewStateForView(child);
int childHeight = getMaxAllowedChildHeight(child);
float yPositionInScrollViewAfterElement = yPositionInScrollView
+ childHeight
@@ -676,7 +678,7 @@ public class StackScrollAlgorithm {
int childCount = algorithmState.visibleChildren.size();
for (int i = 0; i < childCount; i++) {
View child = algorithmState.visibleChildren.get(i);
StackScrollState.ViewState childViewState = resultState.getViewStateForView(child);
StackViewState childViewState = resultState.getViewStateForView(child);
if (i < algorithmState.itemsInTopStack) {
float stackIndex = algorithmState.itemsInTopStack - i;

View File

@@ -39,13 +39,13 @@ public class StackScrollState {
private static final String CHILD_NOT_FOUND_TAG = "StackScrollStateNoSuchChild";
private final ViewGroup mHostView;
private Map<ExpandableView, ViewState> mStateMap;
private Map<ExpandableView, StackViewState> mStateMap;
private final Rect mClipRect = new Rect();
private final int mClearAllTopPadding;
public StackScrollState(ViewGroup hostView) {
mHostView = hostView;
mStateMap = new HashMap<ExpandableView, ViewState>();
mStateMap = new HashMap<ExpandableView, StackViewState>();
mClearAllTopPadding = hostView.getContext().getResources().getDimensionPixelSize(
R.dimen.clear_all_padding_top);
}
@@ -58,20 +58,24 @@ public class StackScrollState {
int numChildren = mHostView.getChildCount();
for (int i = 0; i < numChildren; i++) {
ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
ViewState viewState = mStateMap.get(child);
if (viewState == null) {
viewState = new ViewState();
mStateMap.put(child, viewState);
}
// initialize with the default values of the view
viewState.height = child.getIntrinsicHeight();
viewState.gone = child.getVisibility() == View.GONE;
viewState.alpha = 1;
viewState.notGoneIndex = -1;
resetViewState(child);
}
}
public ViewState getViewStateForView(View requestedView) {
private void resetViewState(ExpandableView view) {
StackViewState viewState = mStateMap.get(view);
if (viewState == null) {
viewState = new StackViewState();
mStateMap.put(view, viewState);
}
// initialize with the default values of the view
viewState.height = view.getIntrinsicHeight();
viewState.gone = view.getVisibility() == View.GONE;
viewState.alpha = 1;
viewState.notGoneIndex = -1;
}
public StackViewState getViewStateForView(View requestedView) {
return mStateMap.get(requestedView);
}
@@ -87,105 +91,126 @@ public class StackScrollState {
int numChildren = mHostView.getChildCount();
for (int i = 0; i < numChildren; i++) {
ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
ViewState state = mStateMap.get(child);
if (state == null) {
Log.wtf(CHILD_NOT_FOUND_TAG, "No child state was found when applying this state " +
"to the hostView");
StackViewState state = mStateMap.get(child);
if (!applyState(child, state)) {
continue;
}
if (!state.gone) {
float alpha = child.getAlpha();
float yTranslation = child.getTranslationY();
float xTranslation = child.getTranslationX();
float zTranslation = child.getTranslationZ();
float scale = child.getScaleX();
int height = child.getActualHeight();
float newAlpha = state.alpha;
float newYTranslation = state.yTranslation;
float newZTranslation = state.zTranslation;
float newScale = state.scale;
int newHeight = state.height;
boolean becomesInvisible = newAlpha == 0.0f;
if (alpha != newAlpha && xTranslation == 0) {
// apply layer type
boolean becomesFullyVisible = newAlpha == 1.0f;
boolean newLayerTypeIsHardware = !becomesInvisible && !becomesFullyVisible;
int layerType = child.getLayerType();
int newLayerType = newLayerTypeIsHardware
? View.LAYER_TYPE_HARDWARE
: View.LAYER_TYPE_NONE;
if (layerType != newLayerType) {
child.setLayerType(newLayerType, null);
}
// apply alpha
child.setAlpha(newAlpha);
}
// apply visibility
int oldVisibility = child.getVisibility();
int newVisibility = becomesInvisible ? View.INVISIBLE : View.VISIBLE;
if (newVisibility != oldVisibility) {
child.setVisibility(newVisibility);
}
// apply yTranslation
if (yTranslation != newYTranslation) {
child.setTranslationY(newYTranslation);
}
// apply zTranslation
if (zTranslation != newZTranslation) {
child.setTranslationZ(newZTranslation);
}
// apply scale
if (scale != newScale) {
child.setScaleX(newScale);
child.setScaleY(newScale);
}
// apply height
if (height != newHeight) {
child.setActualHeight(newHeight, false /* notifyListeners */);
}
// apply dimming
child.setDimmed(state.dimmed, false /* animate */);
// apply dark
child.setDark(state.dark, false /* animate */, 0 /* delay */);
// apply hiding sensitive
child.setHideSensitive(
state.hideSensitive, false /* animated */, 0 /* delay */, 0 /* duration */);
// apply speed bump state
child.setBelowSpeedBump(state.belowSpeedBump);
// apply clipping
float oldClipTopAmount = child.getClipTopAmount();
if (oldClipTopAmount != state.clipTopAmount) {
child.setClipTopAmount(state.clipTopAmount);
}
updateChildClip(child, newHeight, state.topOverLap);
if(child instanceof SpeedBumpView) {
performSpeedBumpAnimation(i, (SpeedBumpView) child, state, 0);
} else if (child instanceof DismissView) {
DismissView dismissView = (DismissView) child;
boolean visible = state.topOverLap < mClearAllTopPadding;
dismissView.performVisibilityAnimation(visible && !dismissView.willBeGone());
} else if (child instanceof EmptyShadeView) {
EmptyShadeView emptyShadeView = (EmptyShadeView) child;
boolean visible = state.topOverLap <= 0;
emptyShadeView.performVisibilityAnimation(
visible && !emptyShadeView.willBeGone());
}
if(child instanceof SpeedBumpView) {
performSpeedBumpAnimation(i, (SpeedBumpView) child, state, 0);
} else if (child instanceof DismissView) {
DismissView dismissView = (DismissView) child;
boolean visible = state.topOverLap < mClearAllTopPadding;
dismissView.performVisibilityAnimation(visible && !dismissView.willBeGone());
} else if (child instanceof EmptyShadeView) {
EmptyShadeView emptyShadeView = (EmptyShadeView) child;
boolean visible = state.topOverLap <= 0;
emptyShadeView.performVisibilityAnimation(
visible && !emptyShadeView.willBeGone());
}
}
}
/**
* Applies a {@link StackViewState} to an {@link ExpandableView}.
*
* @return whether the state was applied correctly
*/
public boolean applyState(ExpandableView view, StackViewState state) {
if (state == null) {
Log.wtf(CHILD_NOT_FOUND_TAG, "No child state was found when applying this state " +
"to the hostView");
return false;
}
if (state.gone) {
return false;
}
applyViewState(view, state);
int height = view.getActualHeight();
int newHeight = state.height;
// apply height
if (height != newHeight) {
view.setActualHeight(newHeight, false /* notifyListeners */);
}
// apply dimming
view.setDimmed(state.dimmed, false /* animate */);
// apply dark
view.setDark(state.dark, false /* animate */, 0 /* delay */);
// apply hiding sensitive
view.setHideSensitive(
state.hideSensitive, false /* animated */, 0 /* delay */, 0 /* duration */);
// apply speed bump state
view.setBelowSpeedBump(state.belowSpeedBump);
// apply clipping
float oldClipTopAmount = view.getClipTopAmount();
if (oldClipTopAmount != state.clipTopAmount) {
view.setClipTopAmount(state.clipTopAmount);
}
updateChildClip(view, newHeight, state.topOverLap);
return true;
}
/**
* Applies a {@link ViewState} to a normal view.
*/
public void applyViewState(View view, ViewState state) {
float alpha = view.getAlpha();
float yTranslation = view.getTranslationY();
float xTranslation = view.getTranslationX();
float zTranslation = view.getTranslationZ();
float scale = view.getScaleX();
float newAlpha = state.alpha;
float newYTranslation = state.yTranslation;
float newZTranslation = state.zTranslation;
float newScale = state.scale;
boolean becomesInvisible = newAlpha == 0.0f;
if (alpha != newAlpha && xTranslation == 0) {
// apply layer type
boolean becomesFullyVisible = newAlpha == 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(newAlpha);
}
// apply visibility
int oldVisibility = view.getVisibility();
int newVisibility = becomesInvisible ? View.INVISIBLE : View.VISIBLE;
if (newVisibility != oldVisibility) {
view.setVisibility(newVisibility);
}
// apply yTranslation
if (yTranslation != newYTranslation) {
view.setTranslationY(newYTranslation);
}
// apply zTranslation
if (zTranslation != newZTranslation) {
view.setTranslationZ(newZTranslation);
}
// apply scale
if (scale != newScale) {
view.setScaleX(newScale);
view.setScaleY(newScale);
}
}
/**
* Updates the clipping of a view
*
@@ -201,12 +226,12 @@ public class StackScrollState {
child.setClipBounds(mClipRect);
}
public void performSpeedBumpAnimation(int i, SpeedBumpView speedBump, ViewState state,
public void performSpeedBumpAnimation(int i, SpeedBumpView speedBump, StackViewState state,
long delay) {
View nextChild = getNextChildNotGone(i);
if (nextChild != null) {
float lineEnd = state.yTranslation + state.height / 2;
ViewState nextState = getViewStateForView(nextChild);
StackViewState nextState = getViewStateForView(nextChild);
boolean startIsAboveNext = nextState.yTranslation > lineEnd;
speedBump.animateDivider(startIsAboveNext, delay, null /* onFinishedRunnable */);
}
@@ -223,53 +248,4 @@ public class StackScrollState {
return null;
}
public static class ViewState {
// These are flags such that we can create masks for filtering.
public static final int LOCATION_UNKNOWN = 0x00;
public static final int LOCATION_FIRST_CARD = 0x01;
public static final int LOCATION_TOP_STACK_HIDDEN = 0x02;
public static final int LOCATION_TOP_STACK_PEEKING = 0x04;
public static final int LOCATION_MAIN_AREA = 0x08;
public static final int LOCATION_BOTTOM_STACK_PEEKING = 0x10;
public static final int LOCATION_BOTTOM_STACK_HIDDEN = 0x20;
/** The view isn't layouted at all. */
public static final int LOCATION_GONE = 0x40;
float alpha;
float yTranslation;
float zTranslation;
int height;
boolean gone;
float scale;
boolean dimmed;
boolean dark;
boolean hideSensitive;
boolean belowSpeedBump;
/**
* 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
*/
int notGoneIndex;
/**
* The location this view is currently rendered at.
*
* <p>See <code>LOCATION_</code> flags.</p>
*/
int location;
}
}

View File

@@ -113,13 +113,13 @@ public class StackStateAnimator {
for (int i = 0; i < childCount; i++) {
final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
StackScrollState.ViewState viewState = finalState.getViewStateForView(child);
StackViewState viewState = finalState.getViewStateForView(child);
if (viewState == null || child.getVisibility() == View.GONE) {
continue;
}
child.setClipBounds(null);
startAnimations(child, viewState, finalState, i);
startStackAnimations(child, viewState, finalState, i, -1 /* fixedDelay */);
}
if (!isRunning()) {
// no child has preformed any animation, lets finish
@@ -134,7 +134,7 @@ public class StackStateAnimator {
for (int i = childCount - 1; i >= 0; i--) {
final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
StackScrollState.ViewState viewState = finalState.getViewStateForView(child);
StackViewState viewState = finalState.getViewStateForView(child);
if (viewState == null || child.getVisibility() == View.GONE) {
continue;
}
@@ -145,18 +145,29 @@ public class StackStateAnimator {
return -1;
}
/**
* Start an animation to the given viewState
*/
private void startAnimations(final ExpandableView child, StackScrollState.ViewState viewState,
StackScrollState finalState, int i) {
int childVisibility = child.getVisibility();
boolean wasVisible = childVisibility == View.VISIBLE;
final float alpha = viewState.alpha;
if (!wasVisible && alpha != 0 && !viewState.gone) {
child.setVisibility(View.VISIBLE);
}
/**
* Start an animation to the given {@link StackViewState}.
*
* @param child the child to start the animation on
* @param viewState the {@link StackViewState} of the view to animate to
* @param finalState the final state after the animation
* @param i the index of the view; only relevant if the view is the speed bump and is
* ignored otherwise
* @param fixedDelay a fixed delay if desired or -1 if the delay should be calculated
*/
public void startStackAnimations(final ExpandableView child, StackViewState viewState,
StackScrollState finalState, int i, long fixedDelay) {
final float alpha = viewState.alpha;
boolean wasAdded = mNewAddChildren.contains(child);
long duration = mCurrentLength;
if (wasAdded && mAnimationFilter.hasGoToFullShadeEvent) {
child.setTranslationY(child.getTranslationY() + mGoToFullShadeAppearingTranslation);
float longerDurationFactor = viewState.notGoneIndex - mCurrentLastNotAddedIndex;
longerDurationFactor = (float) Math.pow(longerDurationFactor, 0.7f);
duration = ANIMATION_DURATION_APPEAR_DISAPPEAR + 50 +
(long) (100 * longerDurationFactor);
}
boolean yTranslationChanging = child.getTranslationY() != viewState.yTranslation;
boolean zTranslationChanging = child.getTranslationZ() != viewState.zTranslation;
boolean scaleChanging = child.getScaleX() != viewState.scale;
@@ -164,94 +175,40 @@ public class StackStateAnimator {
boolean heightChanging = viewState.height != child.getActualHeight();
boolean darkChanging = viewState.dark != child.isDark();
boolean topInsetChanging = viewState.clipTopAmount != child.getClipTopAmount();
boolean wasAdded = mNewAddChildren.contains(child);
boolean hasDelays = mAnimationFilter.hasDelays;
boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || scaleChanging ||
alphaChanging || heightChanging || topInsetChanging || darkChanging;
boolean noAnimation = wasAdded;
long delay = 0;
long duration = mCurrentLength;
if (hasDelays && isDelayRelevant || wasAdded) {
if (fixedDelay != -1) {
delay = fixedDelay;
} else if (hasDelays && isDelayRelevant || wasAdded) {
delay = mCurrentAdditionalDelay + calculateChildAnimationDelay(viewState, finalState);
}
if (wasAdded && mAnimationFilter.hasGoToFullShadeEvent) {
child.setTranslationY(child.getTranslationY() + mGoToFullShadeAppearingTranslation);
yTranslationChanging = true;
float longerDurationFactor = viewState.notGoneIndex - mCurrentLastNotAddedIndex;
longerDurationFactor = (float) Math.pow(longerDurationFactor, 0.7f);
duration = ANIMATION_DURATION_APPEAR_DISAPPEAR + 50 +
(long) (100 * longerDurationFactor);
}
// start translationY animation
if (yTranslationChanging) {
if (noAnimation && !mAnimationFilter.hasGoToFullShadeEvent) {
child.setTranslationY(viewState.yTranslation);
} else {
startYTranslationAnimation(child, viewState, duration, delay);
}
}
// start translationZ animation
if (zTranslationChanging) {
if (noAnimation) {
child.setTranslationZ(viewState.zTranslation);
} else {
startZTranslationAnimation(child, viewState, duration, delay);
}
}
// start scale animation
if (scaleChanging) {
if (noAnimation) {
child.setScaleX(viewState.scale);
child.setScaleY(viewState.scale);
} else {
startScaleAnimation(child, viewState, duration);
}
}
// start alpha animation
if (alphaChanging && child.getTranslationX() == 0) {
if (noAnimation) {
child.setAlpha(viewState.alpha);
} else {
startAlphaAnimation(child, viewState, duration, delay);
}
}
startViewAnimations(child, viewState, delay, duration);
// start height animation
if (heightChanging && child.getActualHeight() != 0) {
if (noAnimation) {
child.setActualHeight(viewState.height, false);
} else {
startHeightAnimation(child, viewState, duration, delay);
}
startHeightAnimation(child, viewState, duration, delay);
}
// start top inset animation
if (topInsetChanging) {
if (noAnimation) {
child.setClipTopAmount(viewState.clipTopAmount);
} else {
startInsetAnimation(child, viewState, duration, delay);
}
startInsetAnimation(child, viewState, duration, delay);
}
// start dimmed animation
child.setDimmed(viewState.dimmed, mAnimationFilter.animateDimmed && !wasAdded
&& !noAnimation);
child.setDimmed(viewState.dimmed, mAnimationFilter.animateDimmed);
// start dark animation
child.setDark(viewState.dark, mAnimationFilter.animateDark && !noAnimation, delay);
child.setDark(viewState.dark, mAnimationFilter.animateDark, delay);
// apply speed bump state
child.setBelowSpeedBump(viewState.belowSpeedBump);
// start hiding sensitive animation
child.setHideSensitive(viewState.hideSensitive, mAnimationFilter.animateHideSensitive &&
!wasAdded && !noAnimation, delay, duration);
child.setHideSensitive(viewState.hideSensitive, mAnimationFilter.animateHideSensitive,
delay, duration);
if (wasAdded) {
child.performAddAnimation(delay, mCurrentLength);
@@ -262,7 +219,48 @@ public class StackStateAnimator {
}
}
private long calculateChildAnimationDelay(StackScrollState.ViewState viewState,
/**
* Start an animation to a new {@link ViewState}.
*
* @param child the child to start the animation on
* @param viewState the {@link StackViewState} of the view to animate to
* @param delay a fixed delay
* @param duration the duration of the animation
*/
public void startViewAnimations(View child, ViewState viewState, long delay, long duration) {
boolean wasVisible = child.getVisibility() == View.VISIBLE;
final float alpha = viewState.alpha;
if (!wasVisible && alpha != 0 && !viewState.gone) {
child.setVisibility(View.VISIBLE);
}
boolean yTranslationChanging = child.getTranslationY() != viewState.yTranslation;
boolean zTranslationChanging = child.getTranslationZ() != viewState.zTranslation;
boolean scaleChanging = child.getScaleX() != viewState.scale;
float childAlpha = child.getVisibility() == View.INVISIBLE ? 0.0f : child.getAlpha();
boolean alphaChanging = viewState.alpha != childAlpha;
// start translationY animation
if (yTranslationChanging) {
startYTranslationAnimation(child, viewState, duration, delay);
}
// start translationZ animation
if (zTranslationChanging) {
startZTranslationAnimation(child, viewState, duration, delay);
}
// start scale animation
if (scaleChanging) {
startScaleAnimation(child, viewState, duration);
}
// start alpha animation
if (alphaChanging && child.getTranslationX() == 0) {
startAlphaAnimation(child, viewState, duration, delay);
}
}
private long calculateChildAnimationDelay(StackViewState viewState,
StackScrollState finalState) {
if (mAnimationFilter.hasDarkEvent) {
return calculateDelayDark(viewState);
@@ -314,7 +312,7 @@ public class StackStateAnimator {
return minDelay;
}
private long calculateDelayDark(StackScrollState.ViewState viewState) {
private long calculateDelayDark(StackViewState viewState) {
int referenceIndex;
if (mAnimationFilter.darkAnimationOriginIndex ==
NotificationStackScrollLayout.AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE) {
@@ -328,14 +326,14 @@ public class StackStateAnimator {
return Math.abs(referenceIndex - viewState.notGoneIndex) * ANIMATION_DELAY_PER_ELEMENT_DARK;
}
private long calculateDelayGoToFullShade(StackScrollState.ViewState viewState) {
private long calculateDelayGoToFullShade(StackViewState viewState) {
float index = viewState.notGoneIndex;
index = (float) Math.pow(index, 0.7f);
return (long) (index * ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE);
}
private void startHeightAnimation(final ExpandableView child,
StackScrollState.ViewState viewState, long duration, long delay) {
StackViewState viewState, long duration, long delay) {
Integer previousStartValue = getChildTag(child, TAG_START_HEIGHT);
Integer previousEndValue = getChildTag(child, TAG_END_HEIGHT);
int newEndValue = viewState.height;
@@ -394,7 +392,7 @@ public class StackStateAnimator {
}
private void startInsetAnimation(final ExpandableView child,
StackScrollState.ViewState viewState, long duration, long delay) {
StackViewState viewState, long duration, long delay) {
Integer previousStartValue = getChildTag(child, TAG_START_TOP_INSET);
Integer previousEndValue = getChildTag(child, TAG_END_TOP_INSET);
int newEndValue = viewState.clipTopAmount;
@@ -451,8 +449,8 @@ public class StackStateAnimator {
child.setTag(TAG_END_TOP_INSET, newEndValue);
}
private void startAlphaAnimation(final ExpandableView child,
final StackScrollState.ViewState viewState, long duration, long delay) {
private void startAlphaAnimation(final View child,
final ViewState viewState, long duration, long delay) {
Float previousStartValue = getChildTag(child,TAG_START_ALPHA);
Float previousEndValue = getChildTag(child,TAG_END_ALPHA);
final float newEndValue = viewState.alpha;
@@ -525,8 +523,8 @@ public class StackStateAnimator {
child.setTag(TAG_END_ALPHA, newEndValue);
}
private void startZTranslationAnimation(final ExpandableView child,
final StackScrollState.ViewState viewState, long duration, long delay) {
private void startZTranslationAnimation(final View child,
final ViewState viewState, long duration, long delay) {
Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Z);
Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Z);
float newEndValue = viewState.zTranslation;
@@ -577,8 +575,8 @@ public class StackStateAnimator {
child.setTag(TAG_END_TRANSLATION_Z, newEndValue);
}
private void startYTranslationAnimation(final ExpandableView child,
StackScrollState.ViewState viewState, long duration, long delay) {
private void startYTranslationAnimation(final View child,
ViewState viewState, long duration, long delay) {
Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Y);
Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y);
float newEndValue = viewState.yTranslation;
@@ -630,8 +628,8 @@ public class StackStateAnimator {
child.setTag(TAG_END_TRANSLATION_Y, newEndValue);
}
private void startScaleAnimation(final ExpandableView child,
StackScrollState.ViewState viewState, long duration) {
private void startScaleAnimation(final View child,
ViewState viewState, long duration) {
Float previousStartValue = getChildTag(child, TAG_START_SCALE);
Float previousEndValue = getChildTag(child, TAG_END_SCALE);
float newEndValue = viewState.scale;
@@ -765,7 +763,7 @@ public class StackStateAnimator {
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_ADD) {
// This item is added, initialize it's properties.
StackScrollState.ViewState viewState = finalState
StackViewState viewState = finalState
.getViewStateForView(changingView);
if (viewState == null) {
// The position for this child was never generated, let's continue.
@@ -776,10 +774,7 @@ public class StackStateAnimator {
finalState.removeViewStateForView(changingView);
continue;
}
changingView.setAlpha(viewState.alpha);
changingView.setTranslationY(viewState.yTranslation);
changingView.setTranslationZ(viewState.zTranslation);
changingView.setActualHeight(viewState.height, false);
finalState.applyState(changingView, viewState);
mNewAddChildren.add(changingView);
} else if (event.animationType ==
@@ -791,7 +786,7 @@ public class StackStateAnimator {
// Find the amount to translate up. This is needed in order to understand the
// direction of the remove animation (either downwards or upwards)
StackScrollState.ViewState viewState = finalState
StackViewState viewState = finalState
.getViewStateForView(event.viewAfterChangingView);
int actualHeight = changingView.getActualHeight();
// upwards by default
@@ -813,7 +808,7 @@ public class StackStateAnimator {
mHostLayout.getOverlay().remove(changingView);
}
});
} else if (event.animationType ==
} else if (event.animationType ==
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
// A race condition can trigger the view to be added to the overlay even though
// it is swiped out. So let's remove it

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.systemui.statusbar.stack;
import android.view.View;
import com.android.systemui.statusbar.ExpandableView;
/**
* A state of an expandable view
*/
public class StackViewState extends ViewState {
// These are flags such that we can create masks for filtering.
public static final int LOCATION_UNKNOWN = 0x00;
public static final int LOCATION_FIRST_CARD = 0x01;
public static final int LOCATION_TOP_STACK_HIDDEN = 0x02;
public static final int LOCATION_TOP_STACK_PEEKING = 0x04;
public static final int LOCATION_MAIN_AREA = 0x08;
public static final int LOCATION_BOTTOM_STACK_PEEKING = 0x10;
public static final int LOCATION_BOTTOM_STACK_HIDDEN = 0x20;
/** The view isn't layouted at all. */
public static final int LOCATION_GONE = 0x40;
public int height;
public boolean dimmed;
public boolean dark;
public boolean hideSensitive;
public boolean belowSpeedBump;
/**
* The amount which the view should be clipped from the top. This is calculated to
* perceive consistent shadows.
*/
public int clipTopAmount;
/**
* How much does the child overlap with the previous view on the top? Can be used for
* a clipping optimization
*/
public int topOverLap;
/**
* The index of the view, only accounting for views not equal to GONE
*/
public int notGoneIndex;
/**
* The location this view is currently rendered at.
*
* <p>See <code>LOCATION_</code> flags.</p>
*/
public int location;
@Override
public void copyFrom(ViewState viewState) {
super.copyFrom(viewState);
if (viewState instanceof StackViewState) {
StackViewState svs = (StackViewState) viewState;
height = svs.height;
dimmed = svs.dimmed;
dark = svs.dark;
hideSensitive = svs.hideSensitive;
belowSpeedBump = svs.belowSpeedBump;
clipTopAmount = svs.clipTopAmount;
topOverLap = svs.topOverLap;
notGoneIndex = svs.notGoneIndex;
location = svs.location;
}
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.systemui.statusbar.stack;
import android.view.View;
/**
* A state of a view. This can be used to apply a set of view properties to a view with
* {@link com.android.systemui.statusbar.stack.StackScrollState} or start animations with
* {@link com.android.systemui.statusbar.stack.StackStateAnimator}.
*/
public class ViewState {
public float alpha;
public float yTranslation;
public float zTranslation;
public boolean gone;
public float scale;
public void copyFrom(ViewState viewState) {
alpha = viewState.alpha;
yTranslation = viewState.yTranslation;
zTranslation = viewState.zTranslation;
gone = viewState.gone;
scale = viewState.scale;
}
public void initFrom(View view) {
alpha = view.getVisibility() == View.INVISIBLE ? 0.0f : view.getAlpha();
yTranslation = view.getTranslationY();
zTranslation = view.getTranslationZ();
gone = view.getVisibility() == View.GONE;
scale = view.getScaleX();
}
}