blob: 620af0c0cecb1c51aef12e01fcda5c9289a6b717 [file] [log] [blame]
/*
* Copyright (C) 2014 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.animation.ValueAnimator;
import android.view.View;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import com.android.systemui.statusbar.ExpandableView;
import java.util.ArrayList;
/**
* An stack state animator which handles animations to new StackScrollStates
*/
public class StackStateAnimator {
private static final int ANIMATION_DURATION = 360;
private final Interpolator mFastOutSlowInInterpolator;
public NotificationStackScrollLayout mHostLayout;
private boolean mAnimationIsRunning;
private ArrayList<NotificationStackScrollLayout.AnimationEvent> mHandledEvents =
new ArrayList<>();
public StackStateAnimator(NotificationStackScrollLayout hostLayout) {
mHostLayout = hostLayout;
mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(hostLayout.getContext(),
android.R.interpolator.fast_out_slow_in);
}
public boolean isRunning() {
return mAnimationIsRunning;
}
public void startAnimationForEvents(
ArrayList<NotificationStackScrollLayout.AnimationEvent> mAnimationEvents,
StackScrollState finalState) {
int numEvents = mAnimationEvents.size();
if (numEvents == 0) {
// No events, so we don't perform any animation
return;
}
long lastEventStartTime = mAnimationEvents.get(numEvents - 1).eventStartTime;
long eventEnd = lastEventStartTime + ANIMATION_DURATION;
long currentTime = AnimationUtils.currentAnimationTimeMillis();
long newDuration = eventEnd - currentTime;
if (newDuration <= 0) {
// last event is long before this, so we don't do anything
return;
}
initializeAddedViewStates(mAnimationEvents, finalState);
int childCount = mHostLayout.getChildCount();
boolean isFirstAnimatingView = true;
for (int i = 0; i < childCount; i++) {
final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
StackScrollState.ViewState viewState = finalState.getViewStateForView(child);
if (viewState == null) {
continue;
}
int childVisibility = child.getVisibility();
boolean wasVisible = childVisibility == View.VISIBLE;
final float alpha = viewState.alpha;
if (!wasVisible && alpha != 0 && !viewState.gone) {
child.setVisibility(View.VISIBLE);
}
startPropertyAnimation(newDuration, isFirstAnimatingView, child, viewState, alpha);
// TODO: animate clipBounds
child.setClipBounds(null);
int currentHeigth = child.getActualHeight();
if (viewState.height != currentHeigth) {
startHeightAnimation(newDuration, child, viewState, currentHeigth);
}
isFirstAnimatingView = false;
}
mAnimationIsRunning = true;
}
private void startPropertyAnimation(long newDuration, final boolean hasFinishAction,
final ExpandableView child, StackScrollState.ViewState viewState, final float alpha) {
child.animate().setInterpolator(mFastOutSlowInInterpolator)
.alpha(alpha)
.translationY(viewState.yTranslation)
.translationZ(viewState.zTranslation)
.setDuration(newDuration)
.withEndAction(new Runnable() {
@Override
public void run() {
mAnimationIsRunning = false;
if (hasFinishAction) {
mHandledEvents.clear();
mHostLayout.onChildAnimationFinished();
}
if (alpha == 0) {
child.setVisibility(View.INVISIBLE);
}
}
});
}
private void startHeightAnimation(long newDuration, final ExpandableView child,
StackScrollState.ViewState viewState, int currentHeigth) {
ValueAnimator heightAnimator = ValueAnimator.ofInt(currentHeigth, viewState.height);
heightAnimator.setInterpolator(mFastOutSlowInInterpolator);
heightAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
child.setActualHeight((int) animation.getAnimatedValue());
}
});
heightAnimator.setDuration(newDuration);
heightAnimator.start();
}
/**
* Initialize the viewStates for the added children
*
* @param animationEvents the animation events who contain the added children
* @param finalState the final state to animate to
*/
private void initializeAddedViewStates(
ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents,
StackScrollState finalState) {
for (NotificationStackScrollLayout.AnimationEvent event: animationEvents) {
View changingView = event.changingView;
if (event.animationType == NotificationStackScrollLayout.AnimationEvent
.ANIMATION_TYPE_ADD && !mHandledEvents.contains(event)) {
// This item is added, initialize it's properties.
StackScrollState.ViewState viewState = finalState.getViewStateForView(changingView);
if (viewState == null) {
// The position for this child was never generated, let's continue.
continue;
}
changingView.setAlpha(0);
changingView.setTranslationY(viewState.yTranslation);
changingView.setTranslationZ(viewState.zTranslation);
mHandledEvents.add(event);
}
}
}
}