Refactored the stack animation logic
Animations are now also applicable to normal views and are more modularly applied to specific stack view states. Test: Add notifications, observe animations Bug: 32437839 Change-Id: I75ebf98657749b50d43c88c4c39c5d4c302b1280
This commit is contained in:
@@ -37,6 +37,7 @@ import com.android.systemui.R;
|
||||
import com.android.systemui.classifier.FalsingManager;
|
||||
import com.android.systemui.statusbar.notification.FakeShadowView;
|
||||
import com.android.systemui.statusbar.notification.NotificationUtils;
|
||||
import com.android.systemui.statusbar.stack.ExpandableViewState;
|
||||
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
|
||||
import com.android.systemui.statusbar.stack.StackStateAnimator;
|
||||
|
||||
|
||||
@@ -51,11 +51,11 @@ import com.android.systemui.classifier.FalsingManager;
|
||||
import com.android.systemui.statusbar.notification.HybridNotificationView;
|
||||
import com.android.systemui.statusbar.phone.NotificationGroupManager;
|
||||
import com.android.systemui.statusbar.policy.HeadsUpManager;
|
||||
import com.android.systemui.statusbar.stack.AnimationProperties;
|
||||
import com.android.systemui.statusbar.stack.ExpandableViewState;
|
||||
import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
|
||||
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
|
||||
import com.android.systemui.statusbar.stack.StackScrollState;
|
||||
import com.android.systemui.statusbar.stack.StackStateAnimator;
|
||||
import com.android.systemui.statusbar.stack.ExpandableViewState;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -485,11 +485,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
|
||||
}
|
||||
}
|
||||
|
||||
public void startChildAnimation(StackScrollState finalState,
|
||||
StackStateAnimator stateAnimator, long delay, long duration) {
|
||||
public void startChildAnimation(StackScrollState finalState, AnimationProperties properties) {
|
||||
if (mIsSummaryWithChildren) {
|
||||
mChildrenContainer.startAnimationToState(finalState, stateAnimator, delay,
|
||||
duration);
|
||||
mChildrenContainer.startAnimationToState(finalState, properties);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1762,7 +1760,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
|
||||
return new NotificationViewState(stackScrollState);
|
||||
}
|
||||
|
||||
public static class NotificationViewState extends ExpandableViewState {
|
||||
public class NotificationViewState extends ExpandableViewState {
|
||||
|
||||
private final StackScrollState mOverallState;
|
||||
|
||||
@@ -1782,5 +1780,22 @@ public class ExpandableNotificationRow extends ActivatableNotificationView {
|
||||
row.applyChildrenState(mOverallState);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onYTranslationAnimationFinished() {
|
||||
super.onYTranslationAnimationFinished();
|
||||
if (mHeadsupDisappearRunning) {
|
||||
setHeadsupDisappearRunning(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void animateTo(View child, AnimationProperties properties) {
|
||||
super.animateTo(child, properties);
|
||||
if (child instanceof ExpandableNotificationRow) {
|
||||
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
|
||||
row.startChildAnimation(mOverallState, properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import com.android.systemui.statusbar.notification.NotificationUtils;
|
||||
import com.android.systemui.statusbar.phone.NotificationIconContainer;
|
||||
import com.android.systemui.statusbar.phone.NotificationPanelView;
|
||||
import com.android.systemui.statusbar.stack.AmbientState;
|
||||
import com.android.systemui.statusbar.stack.AnimationProperties;
|
||||
import com.android.systemui.statusbar.stack.ExpandableViewState;
|
||||
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
|
||||
import com.android.systemui.statusbar.stack.StackScrollAlgorithm;
|
||||
@@ -300,5 +301,12 @@ public class NotificationShelf extends ActivatableNotificationView {
|
||||
updateAppearance();
|
||||
setIconContainerTranslation(iconContainerTranslation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void animateTo(View child, AnimationProperties properties) {
|
||||
super.animateTo(child, properties);
|
||||
updateAppearance();
|
||||
setIconContainerTranslation(iconContainerTranslation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Rect;
|
||||
import android.support.v4.graphics.ColorUtils;
|
||||
import android.view.View;
|
||||
@@ -37,7 +36,7 @@ import com.android.systemui.statusbar.ExpandableNotificationRow;
|
||||
import com.android.systemui.statusbar.NotificationData;
|
||||
import com.android.systemui.statusbar.ScrimView;
|
||||
import com.android.systemui.statusbar.policy.HeadsUpManager;
|
||||
import com.android.systemui.statusbar.stack.StackStateAnimator;
|
||||
import com.android.systemui.statusbar.stack.ViewState;
|
||||
|
||||
/**
|
||||
* Controls both the scrim behind the notifications and in front of the notifications (when a
|
||||
@@ -478,14 +477,14 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
|
||||
return;
|
||||
}
|
||||
|
||||
ValueAnimator previousAnimator = StackStateAnimator.getChildTag(scrim,
|
||||
ValueAnimator previousAnimator = ViewState.getChildTag(scrim,
|
||||
TAG_KEY_ANIM);
|
||||
float animEndValue = -1;
|
||||
if (previousAnimator != null) {
|
||||
if (animate || alpha == currentAlpha) {
|
||||
previousAnimator.cancel();
|
||||
} else {
|
||||
animEndValue = StackStateAnimator.getChildTag(scrim, TAG_END_ALPHA);
|
||||
animEndValue = ViewState.getChildTag(scrim, TAG_END_ALPHA);
|
||||
}
|
||||
}
|
||||
if (alpha != currentAlpha && alpha != animEndValue) {
|
||||
@@ -495,10 +494,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
|
||||
scrim.setTag(TAG_END_ALPHA, alpha);
|
||||
} else {
|
||||
if (previousAnimator != null) {
|
||||
float previousStartValue = StackStateAnimator.getChildTag(scrim,
|
||||
TAG_START_ALPHA);
|
||||
float previousEndValue = StackStateAnimator.getChildTag(scrim,
|
||||
TAG_END_ALPHA);
|
||||
float previousStartValue = ViewState.getChildTag(scrim, TAG_START_ALPHA);
|
||||
float previousEndValue = ViewState.getChildTag(scrim, TAG_END_ALPHA);
|
||||
// we need to increase all animation keyframes of the previous animator by the
|
||||
// relative change to the end value
|
||||
PropertyValuesHolder[] values = previousAnimator.getValues();
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.AnimatorListenerAdapter;
|
||||
import android.util.Property;
|
||||
import android.view.View;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
/**
|
||||
* Properties for a View animation
|
||||
*/
|
||||
public class AnimationProperties {
|
||||
public long duration;
|
||||
public long delay;
|
||||
|
||||
/**
|
||||
* @return an animation filter for this animation.
|
||||
*/
|
||||
public AnimationFilter getAnimationFilter() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a listener that should be run whenever any property finished its animation
|
||||
*/
|
||||
public AnimatorListenerAdapter getAnimationFinishListener() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean wasAdded(View view) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a custom interpolator for a property instead of the normal one.
|
||||
*/
|
||||
public Interpolator getCustomInterpolator(View child, Property translationY) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -16,8 +16,15 @@
|
||||
|
||||
package com.android.systemui.statusbar.stack;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.systemui.Interpolators;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.statusbar.ExpandableNotificationRow;
|
||||
import com.android.systemui.statusbar.ExpandableView;
|
||||
|
||||
/**
|
||||
@@ -25,6 +32,16 @@ import com.android.systemui.statusbar.ExpandableView;
|
||||
*/
|
||||
public class ExpandableViewState extends ViewState {
|
||||
|
||||
private static final int TAG_ANIMATOR_HEIGHT = R.id.height_animator_tag;
|
||||
private static final int TAG_ANIMATOR_TOP_INSET = R.id.top_inset_animator_tag;
|
||||
private static final int TAG_ANIMATOR_SHADOW_ALPHA = R.id.shadow_alpha_animator_tag;
|
||||
private static final int TAG_END_HEIGHT = R.id.height_animator_end_value_tag;
|
||||
private static final int TAG_END_TOP_INSET = R.id.top_inset_animator_end_value_tag;
|
||||
private static final int TAG_END_SHADOW_ALPHA = R.id.shadow_alpha_animator_end_value_tag;
|
||||
private static final int TAG_START_HEIGHT = R.id.height_animator_start_value_tag;
|
||||
private static final int TAG_START_TOP_INSET = R.id.top_inset_animator_start_value_tag;
|
||||
private static final int TAG_START_SHADOW_ALPHA = R.id.shadow_alpha_animator_start_value_tag;
|
||||
|
||||
// These are flags such that we can create masks for filtering.
|
||||
|
||||
/**
|
||||
@@ -156,4 +173,274 @@ public class ExpandableViewState extends ViewState {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void animateTo(View child, AnimationProperties properties) {
|
||||
super.animateTo(child, properties);
|
||||
if (!(child instanceof ExpandableView)) {
|
||||
return;
|
||||
}
|
||||
ExpandableView expandableView = (ExpandableView) child;
|
||||
AnimationFilter animationFilter = properties.getAnimationFilter();
|
||||
|
||||
// start height animation
|
||||
if (this.height != expandableView.getActualHeight()) {
|
||||
startHeightAnimation(expandableView, properties);
|
||||
} else {
|
||||
abortAnimation(child, TAG_ANIMATOR_HEIGHT);
|
||||
}
|
||||
|
||||
// start shadow alpha animation
|
||||
if (this.shadowAlpha != expandableView.getShadowAlpha()) {
|
||||
startShadowAlphaAnimation(expandableView, properties);
|
||||
} else {
|
||||
abortAnimation(child, TAG_ANIMATOR_SHADOW_ALPHA);
|
||||
}
|
||||
|
||||
// start top inset animation
|
||||
if (this.clipTopAmount != expandableView.getClipTopAmount()) {
|
||||
startInsetAnimation(expandableView, properties);
|
||||
} else {
|
||||
abortAnimation(child, TAG_ANIMATOR_TOP_INSET);
|
||||
}
|
||||
|
||||
// start dimmed animation
|
||||
expandableView.setDimmed(this.dimmed, animationFilter.animateDimmed);
|
||||
|
||||
// apply below shelf state
|
||||
expandableView.setBelowShelf(this.belowShelf);
|
||||
|
||||
// start hiding sensitive animation
|
||||
expandableView.setHideSensitive(this.hideSensitive, animationFilter.animateHideSensitive,
|
||||
properties.delay, properties.duration);
|
||||
|
||||
// start dark animation
|
||||
expandableView.setDark(this.dark, animationFilter.animateDark, properties.delay);
|
||||
|
||||
if (properties.wasAdded(child)) {
|
||||
expandableView.performAddAnimation(properties.delay, properties.duration);
|
||||
}
|
||||
}
|
||||
|
||||
private void startHeightAnimation(final ExpandableView child, AnimationProperties properties) {
|
||||
Integer previousStartValue = getChildTag(child, TAG_START_HEIGHT);
|
||||
Integer previousEndValue = getChildTag(child, TAG_END_HEIGHT);
|
||||
int newEndValue = this.height;
|
||||
if (previousEndValue != null && previousEndValue == newEndValue) {
|
||||
return;
|
||||
}
|
||||
ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_HEIGHT);
|
||||
AnimationFilter filter = properties.getAnimationFilter();
|
||||
if (!filter.animateHeight) {
|
||||
// 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_HEIGHT, newStartValue);
|
||||
child.setTag(TAG_END_HEIGHT, newEndValue);
|
||||
previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
|
||||
return;
|
||||
} else {
|
||||
// no new animation needed, let's just apply the value
|
||||
child.setActualHeight(newEndValue, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ValueAnimator animator = ValueAnimator.ofInt(child.getActualHeight(), newEndValue);
|
||||
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
child.setActualHeight((int) animation.getAnimatedValue(),
|
||||
false /* notifyListeners */);
|
||||
}
|
||||
});
|
||||
animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
|
||||
long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
|
||||
animator.setDuration(newDuration);
|
||||
if (properties.delay > 0 && (previousAnimator == null
|
||||
|| previousAnimator.getAnimatedFraction() == 0)) {
|
||||
animator.setStartDelay(properties.delay);
|
||||
}
|
||||
AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
|
||||
if (listener != null) {
|
||||
animator.addListener(listener);
|
||||
}
|
||||
// remove the tag when the animation is finished
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
boolean mWasCancelled;
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
child.setTag(TAG_ANIMATOR_HEIGHT, null);
|
||||
child.setTag(TAG_START_HEIGHT, null);
|
||||
child.setTag(TAG_END_HEIGHT, null);
|
||||
child.setActualHeightAnimating(false);
|
||||
if (!mWasCancelled && child instanceof ExpandableNotificationRow) {
|
||||
((ExpandableNotificationRow) child).setGroupExpansionChanging(
|
||||
false /* isExpansionChanging */);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
mWasCancelled = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
mWasCancelled = true;
|
||||
}
|
||||
});
|
||||
startAnimator(animator, listener);
|
||||
child.setTag(TAG_ANIMATOR_HEIGHT, animator);
|
||||
child.setTag(TAG_START_HEIGHT, child.getActualHeight());
|
||||
child.setTag(TAG_END_HEIGHT, newEndValue);
|
||||
child.setActualHeightAnimating(true);
|
||||
}
|
||||
|
||||
private void startShadowAlphaAnimation(final ExpandableView child,
|
||||
AnimationProperties properties) {
|
||||
Float previousStartValue = getChildTag(child, TAG_START_SHADOW_ALPHA);
|
||||
Float previousEndValue = getChildTag(child, TAG_END_SHADOW_ALPHA);
|
||||
float newEndValue = this.shadowAlpha;
|
||||
if (previousEndValue != null && previousEndValue == newEndValue) {
|
||||
return;
|
||||
}
|
||||
ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_SHADOW_ALPHA);
|
||||
AnimationFilter filter = properties.getAnimationFilter();
|
||||
if (!filter.animateShadowAlpha) {
|
||||
// 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();
|
||||
float relativeDiff = newEndValue - previousEndValue;
|
||||
float newStartValue = previousStartValue + relativeDiff;
|
||||
values[0].setFloatValues(newStartValue, newEndValue);
|
||||
child.setTag(TAG_START_SHADOW_ALPHA, newStartValue);
|
||||
child.setTag(TAG_END_SHADOW_ALPHA, newEndValue);
|
||||
previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
|
||||
return;
|
||||
} else {
|
||||
// no new animation needed, let's just apply the value
|
||||
child.setShadowAlpha(newEndValue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ValueAnimator animator = ValueAnimator.ofFloat(child.getShadowAlpha(), newEndValue);
|
||||
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
child.setShadowAlpha((float) animation.getAnimatedValue());
|
||||
}
|
||||
});
|
||||
animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
|
||||
long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
|
||||
animator.setDuration(newDuration);
|
||||
if (properties.delay > 0 && (previousAnimator == null
|
||||
|| previousAnimator.getAnimatedFraction() == 0)) {
|
||||
animator.setStartDelay(properties.delay);
|
||||
}
|
||||
AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
|
||||
if (listener != null) {
|
||||
animator.addListener(listener);
|
||||
}
|
||||
// remove the tag when the animation is finished
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
child.setTag(TAG_ANIMATOR_SHADOW_ALPHA, null);
|
||||
child.setTag(TAG_START_SHADOW_ALPHA, null);
|
||||
child.setTag(TAG_END_SHADOW_ALPHA, null);
|
||||
}
|
||||
});
|
||||
startAnimator(animator, listener);
|
||||
child.setTag(TAG_ANIMATOR_SHADOW_ALPHA, animator);
|
||||
child.setTag(TAG_START_SHADOW_ALPHA, child.getShadowAlpha());
|
||||
child.setTag(TAG_END_SHADOW_ALPHA, newEndValue);
|
||||
}
|
||||
|
||||
private void startInsetAnimation(final ExpandableView child, AnimationProperties properties) {
|
||||
Integer previousStartValue = getChildTag(child, TAG_START_TOP_INSET);
|
||||
Integer previousEndValue = getChildTag(child, TAG_END_TOP_INSET);
|
||||
int newEndValue = this.clipTopAmount;
|
||||
if (previousEndValue != null && previousEndValue == newEndValue) {
|
||||
return;
|
||||
}
|
||||
ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TOP_INSET);
|
||||
AnimationFilter filter = properties.getAnimationFilter();
|
||||
if (!filter.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(Interpolators.FAST_OUT_SLOW_IN);
|
||||
long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
|
||||
animator.setDuration(newDuration);
|
||||
if (properties.delay > 0 && (previousAnimator == null
|
||||
|| previousAnimator.getAnimatedFraction() == 0)) {
|
||||
animator.setStartDelay(properties.delay);
|
||||
}
|
||||
AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
|
||||
if (listener != null) {
|
||||
animator.addListener(listener);
|
||||
}
|
||||
// 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, listener);
|
||||
child.setTag(TAG_ANIMATOR_TOP_INSET, animator);
|
||||
child.setTag(TAG_START_TOP_INSET, child.getClipTopAmount());
|
||||
child.setTag(TAG_END_TOP_INSET, newEndValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the end value of the height animation running on a view or the actualHeight
|
||||
* if no animation is running.
|
||||
*/
|
||||
public static int getFinalActualHeight(ExpandableView view) {
|
||||
if (view == null) {
|
||||
return 0;
|
||||
}
|
||||
ValueAnimator heightAnimator = getChildTag(view, TAG_ANIMATOR_HEIGHT);
|
||||
if (heightAnimator == null) {
|
||||
return view.getActualHeight();
|
||||
} else {
|
||||
return getChildTag(view, TAG_END_HEIGHT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -594,11 +594,11 @@ public class NotificationChildrenContainer extends ViewGroup {
|
||||
// There is no fake shadow to be drawn on the children
|
||||
child.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
|
||||
}
|
||||
if (mOverflowNumber != null) {
|
||||
if (mGroupOverFlowState != null) {
|
||||
mGroupOverFlowState.applyToView(mOverflowNumber);
|
||||
mNeverAppliedGroupState = false;
|
||||
}
|
||||
if (mNotificationHeader != null) {
|
||||
if (mHeaderViewState != null) {
|
||||
mHeaderViewState.applyToView(mNotificationHeader);
|
||||
}
|
||||
}
|
||||
@@ -614,8 +614,7 @@ public class NotificationChildrenContainer extends ViewGroup {
|
||||
return;
|
||||
}
|
||||
|
||||
public void startAnimationToState(StackScrollState state, StackStateAnimator stateAnimator,
|
||||
long baseDelay, long duration) {
|
||||
public void startAnimationToState(StackScrollState state, AnimationProperties properties) {
|
||||
int childCount = mChildren.size();
|
||||
ViewState tmpState = new ViewState();
|
||||
float expandFraction = getGroupExpandFraction();
|
||||
@@ -624,7 +623,7 @@ public class NotificationChildrenContainer extends ViewGroup {
|
||||
for (int i = childCount - 1; i >= 0; i--) {
|
||||
ExpandableNotificationRow child = mChildren.get(i);
|
||||
ExpandableViewState viewState = state.getViewStateForView(child);
|
||||
stateAnimator.startStackAnimations(child, viewState, state, -1, baseDelay);
|
||||
viewState.animateTo(child, properties);
|
||||
|
||||
// layout the divider
|
||||
View divider = mDividers.get(i);
|
||||
@@ -637,7 +636,7 @@ public class NotificationChildrenContainer extends ViewGroup {
|
||||
}
|
||||
tmpState.hidden = !dividersVisible;
|
||||
tmpState.alpha = alpha;
|
||||
stateAnimator.startViewAnimations(divider, tmpState, baseDelay, duration);
|
||||
tmpState.animateTo(divider, properties);
|
||||
// There is no fake shadow to be drawn on the children
|
||||
child.setFakeShadowIntensity(0.0f, 0.0f, 0, 0);
|
||||
}
|
||||
@@ -649,8 +648,7 @@ public class NotificationChildrenContainer extends ViewGroup {
|
||||
mGroupOverFlowState.alpha = alpha;
|
||||
mNeverAppliedGroupState = false;
|
||||
}
|
||||
stateAnimator.startViewAnimations(mOverflowNumber, mGroupOverFlowState,
|
||||
baseDelay, duration);
|
||||
mGroupOverFlowState.animateTo(mOverflowNumber, properties);
|
||||
}
|
||||
if (mNotificationHeader != null) {
|
||||
mHeaderViewState.applyToView(mNotificationHeader);
|
||||
|
||||
@@ -69,9 +69,9 @@ import com.android.systemui.statusbar.EmptyShadeView;
|
||||
import com.android.systemui.statusbar.ExpandableNotificationRow;
|
||||
import com.android.systemui.statusbar.ExpandableView;
|
||||
import com.android.systemui.statusbar.NotificationGuts;
|
||||
import com.android.systemui.statusbar.NotificationShelf;
|
||||
import com.android.systemui.statusbar.NotificationSettingsIconRow;
|
||||
import com.android.systemui.statusbar.NotificationSettingsIconRow.SettingsIconRowListener;
|
||||
import com.android.systemui.statusbar.NotificationShelf;
|
||||
import com.android.systemui.statusbar.StackScrollerDecorView;
|
||||
import com.android.systemui.statusbar.StatusBarState;
|
||||
import com.android.systemui.statusbar.notification.FakeShadowView;
|
||||
@@ -2095,7 +2095,7 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
ActivatableNotificationView firstView = mFirstVisibleBackgroundChild;
|
||||
int top = 0;
|
||||
if (firstView != null) {
|
||||
int finalTranslationY = (int) StackStateAnimator.getFinalTranslationY(firstView);
|
||||
int finalTranslationY = (int) ViewState.getFinalTranslationY(firstView);
|
||||
if (mAnimateNextBackgroundTop
|
||||
|| mTopAnimator == null && mCurrentBounds.top == finalTranslationY
|
||||
|| mTopAnimator != null && mEndAnimationRect.top == finalTranslationY) {
|
||||
@@ -2108,8 +2108,8 @@ public class NotificationStackScrollLayout extends ViewGroup
|
||||
ActivatableNotificationView lastView = mLastVisibleBackgroundChild;
|
||||
int bottom = 0;
|
||||
if (lastView != null) {
|
||||
int finalTranslationY = (int) StackStateAnimator.getFinalTranslationY(lastView);
|
||||
int finalHeight = StackStateAnimator.getFinalActualHeight(lastView);
|
||||
int finalTranslationY = (int) ViewState.getFinalTranslationY(lastView);
|
||||
int finalHeight = ExpandableViewState.getFinalActualHeight(lastView);
|
||||
int finalBottom = finalTranslationY + finalHeight;
|
||||
finalBottom = Math.min(finalBottom, getHeight());
|
||||
if (mAnimateNextBackgroundBottom
|
||||
|
||||
@@ -18,9 +18,8 @@ package com.android.systemui.statusbar.stack;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.util.Property;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.Interpolator;
|
||||
@@ -29,7 +28,6 @@ import com.android.systemui.Interpolators;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.statusbar.ExpandableNotificationRow;
|
||||
import com.android.systemui.statusbar.ExpandableView;
|
||||
import com.android.systemui.statusbar.policy.HeadsUpManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
@@ -54,28 +52,10 @@ public class StackStateAnimator {
|
||||
public static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2;
|
||||
public static final int ANIMATION_DELAY_HEADS_UP = 120;
|
||||
|
||||
private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag;
|
||||
private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag;
|
||||
private static final int TAG_ANIMATOR_ALPHA = R.id.alpha_animator_tag;
|
||||
private static final int TAG_ANIMATOR_HEIGHT = R.id.height_animator_tag;
|
||||
private static final int TAG_ANIMATOR_TOP_INSET = R.id.top_inset_animator_tag;
|
||||
private static final int TAG_ANIMATOR_SHADOW_ALPHA = R.id.shadow_alpha_animator_tag;
|
||||
private static final int TAG_END_TRANSLATION_Y = R.id.translation_y_animator_end_value_tag;
|
||||
private static final int TAG_END_TRANSLATION_Z = R.id.translation_z_animator_end_value_tag;
|
||||
private static final int TAG_END_ALPHA = R.id.alpha_animator_end_value_tag;
|
||||
private static final int TAG_END_HEIGHT = R.id.height_animator_end_value_tag;
|
||||
private static final int TAG_END_TOP_INSET = R.id.top_inset_animator_end_value_tag;
|
||||
private static final int TAG_END_SHADOW_ALPHA = R.id.shadow_alpha_animator_end_value_tag;
|
||||
private static final int TAG_START_TRANSLATION_Y = R.id.translation_y_animator_start_value_tag;
|
||||
private static final int TAG_START_TRANSLATION_Z = R.id.translation_z_animator_start_value_tag;
|
||||
private static final int TAG_START_ALPHA = R.id.alpha_animator_start_value_tag;
|
||||
private static final int TAG_START_HEIGHT = R.id.height_animator_start_value_tag;
|
||||
private static final int TAG_START_TOP_INSET = R.id.top_inset_animator_start_value_tag;
|
||||
private static final int TAG_START_SHADOW_ALPHA = R.id.shadow_alpha_animator_start_value_tag;
|
||||
|
||||
private final Interpolator mHeadsUpAppearInterpolator;
|
||||
private final int mGoToFullShadeAppearingTranslation;
|
||||
private final ExpandableViewState mTmpState = new ExpandableViewState();
|
||||
private final AnimationProperties mAnimationProperties;
|
||||
public NotificationStackScrollLayout mHostLayout;
|
||||
private ArrayList<NotificationStackScrollLayout.AnimationEvent> mNewEvents =
|
||||
new ArrayList<>();
|
||||
@@ -102,6 +82,30 @@ public class StackStateAnimator {
|
||||
hostLayout.getContext().getResources().getDimensionPixelSize(
|
||||
R.dimen.go_to_full_shade_appearing_translation);
|
||||
mHeadsUpAppearInterpolator = new HeadsUpAppearInterpolator();
|
||||
mAnimationProperties = new AnimationProperties() {
|
||||
@Override
|
||||
public AnimationFilter getAnimationFilter() {
|
||||
return mAnimationFilter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnimatorListenerAdapter getAnimationFinishListener() {
|
||||
return getGlobalAnimationFinishedListener();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wasAdded(View view) {
|
||||
return mNewAddChildren.contains(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Interpolator getCustomInterpolator(View child, Property property) {
|
||||
if (mHeadsUpAppearChildren.contains(child) && View.TRANSLATION_Y.equals(property)) {
|
||||
return mHeadsUpAppearInterpolator;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
@@ -128,7 +132,8 @@ public class StackStateAnimator {
|
||||
continue;
|
||||
}
|
||||
|
||||
startStackAnimations(child, viewState, finalState, i, -1 /* fixedDelay */);
|
||||
initAnimationProperties(finalState, child, viewState);
|
||||
viewState.animateTo(child, mAnimationProperties);
|
||||
}
|
||||
if (!isRunning()) {
|
||||
// no child has preformed any animation, lets finish
|
||||
@@ -140,6 +145,36 @@ public class StackStateAnimator {
|
||||
mNewAddChildren.clear();
|
||||
}
|
||||
|
||||
private void initAnimationProperties(StackScrollState finalState, ExpandableView child,
|
||||
ExpandableViewState viewState) {
|
||||
boolean wasAdded = mAnimationProperties.wasAdded(child);
|
||||
mAnimationProperties.duration = mCurrentLength;
|
||||
adaptDurationWhenGoingToFullShade(child, viewState, wasAdded);
|
||||
mAnimationProperties.delay = 0;
|
||||
if (wasAdded || mAnimationFilter.hasDelays
|
||||
&& (viewState.yTranslation != child.getTranslationY()
|
||||
|| viewState.zTranslation != child.getTranslationZ()
|
||||
|| viewState.alpha != child.getAlpha()
|
||||
|| viewState.height != child.getActualHeight()
|
||||
|| viewState.clipTopAmount != child.getClipTopAmount()
|
||||
|| viewState.dark != child.isDark()
|
||||
|| viewState.shadowAlpha != child.getShadowAlpha())) {
|
||||
mAnimationProperties.delay = mCurrentAdditionalDelay
|
||||
+ calculateChildAnimationDelay(viewState, finalState);
|
||||
}
|
||||
}
|
||||
|
||||
private void adaptDurationWhenGoingToFullShade(ExpandableView child,
|
||||
ExpandableViewState viewState, boolean wasAdded) {
|
||||
if (wasAdded && mAnimationFilter.hasGoToFullShadeEvent) {
|
||||
child.setTranslationY(child.getTranslationY() + mGoToFullShadeAppearingTranslation);
|
||||
float longerDurationFactor = viewState.notGoneIndex - mCurrentLastNotAddedIndex;
|
||||
longerDurationFactor = (float) Math.pow(longerDurationFactor, 0.7f);
|
||||
mAnimationProperties.duration = ANIMATION_DURATION_APPEAR_DISAPPEAR + 50 +
|
||||
(long) (100 * longerDurationFactor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a view should not perform an animation and applies it directly.
|
||||
*
|
||||
@@ -150,7 +185,7 @@ public class StackStateAnimator {
|
||||
if (mShadeExpanded) {
|
||||
return false;
|
||||
}
|
||||
if (getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y) != null) {
|
||||
if (ViewState.isAnimatingY(child)) {
|
||||
// A Y translation animation is running
|
||||
return false;
|
||||
}
|
||||
@@ -182,143 +217,6 @@ public class StackStateAnimator {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Start an animation to the given {@link ExpandableViewState}.
|
||||
*
|
||||
* @param child the child to start the animation on
|
||||
* @param viewState the {@link ExpandableViewState} 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, ExpandableViewState viewState,
|
||||
StackScrollState finalState, int i, long fixedDelay) {
|
||||
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 alphaChanging = viewState.alpha != child.getAlpha();
|
||||
boolean heightChanging = viewState.height != child.getActualHeight();
|
||||
boolean shadowAlphaChanging = viewState.shadowAlpha != child.getShadowAlpha();
|
||||
boolean darkChanging = viewState.dark != child.isDark();
|
||||
boolean topInsetChanging = viewState.clipTopAmount != child.getClipTopAmount();
|
||||
boolean hasDelays = mAnimationFilter.hasDelays;
|
||||
boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || alphaChanging
|
||||
|| heightChanging || topInsetChanging || darkChanging || shadowAlphaChanging;
|
||||
long delay = 0;
|
||||
if (fixedDelay != -1) {
|
||||
delay = fixedDelay;
|
||||
} else if (hasDelays && isDelayRelevant || wasAdded) {
|
||||
delay = mCurrentAdditionalDelay + calculateChildAnimationDelay(viewState, finalState);
|
||||
}
|
||||
|
||||
startViewAnimations(child, viewState, delay, duration);
|
||||
|
||||
// start height animation
|
||||
if (heightChanging) {
|
||||
startHeightAnimation(child, viewState, duration, delay);
|
||||
} else {
|
||||
abortAnimation(child, TAG_ANIMATOR_HEIGHT);
|
||||
}
|
||||
|
||||
// start shadow alpha animation
|
||||
if (shadowAlphaChanging) {
|
||||
startShadowAlphaAnimation(child, viewState, duration, delay);
|
||||
} else {
|
||||
abortAnimation(child, TAG_ANIMATOR_SHADOW_ALPHA);
|
||||
}
|
||||
|
||||
// start top inset animation
|
||||
if (topInsetChanging) {
|
||||
startInsetAnimation(child, viewState, duration, delay);
|
||||
} else {
|
||||
abortAnimation(child, TAG_ANIMATOR_TOP_INSET);
|
||||
}
|
||||
|
||||
// start dimmed animation
|
||||
child.setDimmed(viewState.dimmed, mAnimationFilter.animateDimmed);
|
||||
|
||||
// apply below shelf state
|
||||
child.setBelowShelf(viewState.belowShelf);
|
||||
|
||||
// start hiding sensitive animation
|
||||
child.setHideSensitive(viewState.hideSensitive, mAnimationFilter.animateHideSensitive,
|
||||
delay, duration);
|
||||
|
||||
// start dark animation
|
||||
child.setDark(viewState.dark, mAnimationFilter.animateDark, delay);
|
||||
|
||||
if (wasAdded) {
|
||||
child.performAddAnimation(delay, mCurrentLength);
|
||||
}
|
||||
if (child instanceof ExpandableNotificationRow) {
|
||||
ExpandableNotificationRow row = (ExpandableNotificationRow) child;
|
||||
row.startChildAnimation(finalState, this, delay, duration);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start an animation to a new {@link ViewState}.
|
||||
*
|
||||
* @param child the child to start the animation on
|
||||
* @param viewState the {@link ExpandableViewState} 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 || child.getAlpha() != 0)
|
||||
&& !viewState.gone && !viewState.hidden) {
|
||||
child.setVisibility(View.VISIBLE);
|
||||
}
|
||||
boolean yTranslationChanging = child.getTranslationY() != viewState.yTranslation;
|
||||
boolean zTranslationChanging = child.getTranslationZ() != viewState.zTranslation;
|
||||
float childAlpha = child.getAlpha();
|
||||
boolean alphaChanging = viewState.alpha != childAlpha;
|
||||
if (child instanceof ExpandableView) {
|
||||
// We don't want views to change visibility when they are animating to GONE
|
||||
alphaChanging &= !((ExpandableView) child).willBeGone();
|
||||
}
|
||||
|
||||
// start translationY animation
|
||||
if (yTranslationChanging) {
|
||||
startYTranslationAnimation(child, viewState, duration, delay);
|
||||
} else {
|
||||
abortAnimation(child, TAG_ANIMATOR_TRANSLATION_Y);
|
||||
}
|
||||
|
||||
// start translationZ animation
|
||||
if (zTranslationChanging) {
|
||||
startZTranslationAnimation(child, viewState, duration, delay);
|
||||
} else {
|
||||
abortAnimation(child, TAG_ANIMATOR_TRANSLATION_Z);
|
||||
}
|
||||
|
||||
// start alpha animation
|
||||
if (alphaChanging && child.getTranslationX() == 0) {
|
||||
startAlphaAnimation(child, viewState, duration, delay);
|
||||
} else {
|
||||
abortAnimation(child, TAG_ANIMATOR_ALPHA);
|
||||
}
|
||||
}
|
||||
|
||||
private void abortAnimation(View child, int animatorTag) {
|
||||
Animator previousAnimator = getChildTag(child, animatorTag);
|
||||
if (previousAnimator != null) {
|
||||
previousAnimator.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
private long calculateChildAnimationDelay(ExpandableViewState viewState,
|
||||
StackScrollState finalState) {
|
||||
if (mAnimationFilter.hasDarkEvent) {
|
||||
@@ -394,396 +292,6 @@ public class StackStateAnimator {
|
||||
return (long) (index * ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE);
|
||||
}
|
||||
|
||||
private void startShadowAlphaAnimation(final ExpandableView child,
|
||||
ExpandableViewState viewState, long duration, long delay) {
|
||||
Float previousStartValue = getChildTag(child, TAG_START_SHADOW_ALPHA);
|
||||
Float previousEndValue = getChildTag(child, TAG_END_SHADOW_ALPHA);
|
||||
float newEndValue = viewState.shadowAlpha;
|
||||
if (previousEndValue != null && previousEndValue == newEndValue) {
|
||||
return;
|
||||
}
|
||||
ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_SHADOW_ALPHA);
|
||||
if (!mAnimationFilter.animateShadowAlpha) {
|
||||
// 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();
|
||||
float relativeDiff = newEndValue - previousEndValue;
|
||||
float newStartValue = previousStartValue + relativeDiff;
|
||||
values[0].setFloatValues(newStartValue, newEndValue);
|
||||
child.setTag(TAG_START_SHADOW_ALPHA, newStartValue);
|
||||
child.setTag(TAG_END_SHADOW_ALPHA, newEndValue);
|
||||
previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
|
||||
return;
|
||||
} else {
|
||||
// no new animation needed, let's just apply the value
|
||||
child.setShadowAlpha(newEndValue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ValueAnimator animator = ValueAnimator.ofFloat(child.getShadowAlpha(), newEndValue);
|
||||
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
child.setShadowAlpha((float) animation.getAnimatedValue());
|
||||
}
|
||||
});
|
||||
animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
|
||||
long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
|
||||
animator.setDuration(newDuration);
|
||||
if (delay > 0 && (previousAnimator == null
|
||||
|| previousAnimator.getAnimatedFraction() == 0)) {
|
||||
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_SHADOW_ALPHA, null);
|
||||
child.setTag(TAG_START_SHADOW_ALPHA, null);
|
||||
child.setTag(TAG_END_SHADOW_ALPHA, null);
|
||||
}
|
||||
});
|
||||
startAnimator(animator);
|
||||
child.setTag(TAG_ANIMATOR_SHADOW_ALPHA, animator);
|
||||
child.setTag(TAG_START_SHADOW_ALPHA, child.getShadowAlpha());
|
||||
child.setTag(TAG_END_SHADOW_ALPHA, newEndValue);
|
||||
}
|
||||
|
||||
private void startHeightAnimation(final ExpandableView child,
|
||||
ExpandableViewState viewState, long duration, long delay) {
|
||||
Integer previousStartValue = getChildTag(child, TAG_START_HEIGHT);
|
||||
Integer previousEndValue = getChildTag(child, TAG_END_HEIGHT);
|
||||
int newEndValue = viewState.height;
|
||||
if (previousEndValue != null && previousEndValue == newEndValue) {
|
||||
return;
|
||||
}
|
||||
ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_HEIGHT);
|
||||
if (!mAnimationFilter.animateHeight) {
|
||||
// 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_HEIGHT, newStartValue);
|
||||
child.setTag(TAG_END_HEIGHT, newEndValue);
|
||||
previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
|
||||
return;
|
||||
} else {
|
||||
// no new animation needed, let's just apply the value
|
||||
child.setActualHeight(newEndValue, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ValueAnimator animator = ValueAnimator.ofInt(child.getActualHeight(), newEndValue);
|
||||
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator animation) {
|
||||
child.setActualHeight((int) animation.getAnimatedValue(),
|
||||
false /* notifyListeners */);
|
||||
}
|
||||
});
|
||||
animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
|
||||
long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
|
||||
animator.setDuration(newDuration);
|
||||
if (delay > 0 && (previousAnimator == null
|
||||
|| previousAnimator.getAnimatedFraction() == 0)) {
|
||||
animator.setStartDelay(delay);
|
||||
}
|
||||
animator.addListener(getGlobalAnimationFinishedListener());
|
||||
// remove the tag when the animation is finished
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
boolean mWasCancelled;
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
child.setTag(TAG_ANIMATOR_HEIGHT, null);
|
||||
child.setTag(TAG_START_HEIGHT, null);
|
||||
child.setTag(TAG_END_HEIGHT, null);
|
||||
child.setActualHeightAnimating(false);
|
||||
if (!mWasCancelled && child instanceof ExpandableNotificationRow) {
|
||||
((ExpandableNotificationRow) child).setGroupExpansionChanging(
|
||||
false /* isExpansionChanging */);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
mWasCancelled = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
mWasCancelled = true;
|
||||
}
|
||||
});
|
||||
startAnimator(animator);
|
||||
child.setTag(TAG_ANIMATOR_HEIGHT, animator);
|
||||
child.setTag(TAG_START_HEIGHT, child.getActualHeight());
|
||||
child.setTag(TAG_END_HEIGHT, newEndValue);
|
||||
child.setActualHeightAnimating(true);
|
||||
}
|
||||
|
||||
private void startInsetAnimation(final ExpandableView child,
|
||||
ExpandableViewState 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;
|
||||
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(Interpolators.FAST_OUT_SLOW_IN);
|
||||
long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
|
||||
animator.setDuration(newDuration);
|
||||
if (delay > 0 && (previousAnimator == null
|
||||
|| previousAnimator.getAnimatedFraction() == 0)) {
|
||||
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 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;
|
||||
if (previousEndValue != null && previousEndValue == newEndValue) {
|
||||
return;
|
||||
}
|
||||
ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_ALPHA);
|
||||
if (!mAnimationFilter.animateAlpha) {
|
||||
// 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();
|
||||
float relativeDiff = newEndValue - previousEndValue;
|
||||
float newStartValue = previousStartValue + relativeDiff;
|
||||
values[0].setFloatValues(newStartValue, newEndValue);
|
||||
child.setTag(TAG_START_ALPHA, newStartValue);
|
||||
child.setTag(TAG_END_ALPHA, newEndValue);
|
||||
previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
|
||||
return;
|
||||
} else {
|
||||
// no new animation needed, let's just apply the value
|
||||
child.setAlpha(newEndValue);
|
||||
if (newEndValue == 0) {
|
||||
child.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.ALPHA,
|
||||
child.getAlpha(), newEndValue);
|
||||
animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
|
||||
// Handle layer type
|
||||
child.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
public boolean mWasCancelled;
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
child.setLayerType(View.LAYER_TYPE_NONE, null);
|
||||
if (newEndValue == 0 && !mWasCancelled) {
|
||||
child.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
// remove the tag when the animation is finished
|
||||
child.setTag(TAG_ANIMATOR_ALPHA, null);
|
||||
child.setTag(TAG_START_ALPHA, null);
|
||||
child.setTag(TAG_END_ALPHA, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
mWasCancelled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
mWasCancelled = false;
|
||||
}
|
||||
});
|
||||
long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
|
||||
animator.setDuration(newDuration);
|
||||
if (delay > 0 && (previousAnimator == null
|
||||
|| previousAnimator.getAnimatedFraction() == 0)) {
|
||||
animator.setStartDelay(delay);
|
||||
}
|
||||
animator.addListener(getGlobalAnimationFinishedListener());
|
||||
|
||||
startAnimator(animator);
|
||||
child.setTag(TAG_ANIMATOR_ALPHA, animator);
|
||||
child.setTag(TAG_START_ALPHA, child.getAlpha());
|
||||
child.setTag(TAG_END_ALPHA, newEndValue);
|
||||
}
|
||||
|
||||
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;
|
||||
if (previousEndValue != null && previousEndValue == newEndValue) {
|
||||
return;
|
||||
}
|
||||
ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Z);
|
||||
if (!mAnimationFilter.animateZ) {
|
||||
// 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();
|
||||
float relativeDiff = newEndValue - previousEndValue;
|
||||
float newStartValue = previousStartValue + relativeDiff;
|
||||
values[0].setFloatValues(newStartValue, newEndValue);
|
||||
child.setTag(TAG_START_TRANSLATION_Z, newStartValue);
|
||||
child.setTag(TAG_END_TRANSLATION_Z, newEndValue);
|
||||
previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
|
||||
return;
|
||||
} else {
|
||||
// no new animation needed, let's just apply the value
|
||||
child.setTranslationZ(newEndValue);
|
||||
}
|
||||
}
|
||||
|
||||
ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Z,
|
||||
child.getTranslationZ(), newEndValue);
|
||||
animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
|
||||
long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
|
||||
animator.setDuration(newDuration);
|
||||
if (delay > 0 && (previousAnimator == null
|
||||
|| previousAnimator.getAnimatedFraction() == 0)) {
|
||||
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_TRANSLATION_Z, null);
|
||||
child.setTag(TAG_START_TRANSLATION_Z, null);
|
||||
child.setTag(TAG_END_TRANSLATION_Z, null);
|
||||
}
|
||||
});
|
||||
startAnimator(animator);
|
||||
child.setTag(TAG_ANIMATOR_TRANSLATION_Z, animator);
|
||||
child.setTag(TAG_START_TRANSLATION_Z, child.getTranslationZ());
|
||||
child.setTag(TAG_END_TRANSLATION_Z, newEndValue);
|
||||
}
|
||||
|
||||
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;
|
||||
if (previousEndValue != null && previousEndValue == newEndValue) {
|
||||
return;
|
||||
}
|
||||
ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y);
|
||||
if (!mAnimationFilter.animateY) {
|
||||
// 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();
|
||||
float relativeDiff = newEndValue - previousEndValue;
|
||||
float newStartValue = previousStartValue + relativeDiff;
|
||||
values[0].setFloatValues(newStartValue, newEndValue);
|
||||
child.setTag(TAG_START_TRANSLATION_Y, newStartValue);
|
||||
child.setTag(TAG_END_TRANSLATION_Y, newEndValue);
|
||||
previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
|
||||
return;
|
||||
} else {
|
||||
// no new animation needed, let's just apply the value
|
||||
child.setTranslationY(newEndValue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y,
|
||||
child.getTranslationY(), newEndValue);
|
||||
Interpolator interpolator = mHeadsUpAppearChildren.contains(child) ?
|
||||
mHeadsUpAppearInterpolator :Interpolators.FAST_OUT_SLOW_IN;
|
||||
animator.setInterpolator(interpolator);
|
||||
long newDuration = cancelAnimatorAndGetNewDuration(duration, previousAnimator);
|
||||
animator.setDuration(newDuration);
|
||||
if (delay > 0 && (previousAnimator == null
|
||||
|| previousAnimator.getAnimatedFraction() == 0)) {
|
||||
animator.setStartDelay(delay);
|
||||
}
|
||||
animator.addListener(getGlobalAnimationFinishedListener());
|
||||
final boolean isHeadsUpDisappear = mHeadsUpDisappearChildren.contains(child);
|
||||
// remove the tag when the animation is finished
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
HeadsUpManager.setIsClickedNotification(child, false);
|
||||
child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null);
|
||||
child.setTag(TAG_START_TRANSLATION_Y, null);
|
||||
child.setTag(TAG_END_TRANSLATION_Y, null);
|
||||
if (isHeadsUpDisappear) {
|
||||
((ExpandableNotificationRow) child).setHeadsupDisappearRunning(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
startAnimator(animator);
|
||||
child.setTag(TAG_ANIMATOR_TRANSLATION_Y, animator);
|
||||
child.setTag(TAG_START_TRANSLATION_Y, child.getTranslationY());
|
||||
child.setTag(TAG_END_TRANSLATION_Y, newEndValue);
|
||||
}
|
||||
|
||||
private void startAnimator(ValueAnimator animator) {
|
||||
mAnimatorSet.add(animator);
|
||||
animator.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an adapter which ensures that onAnimationFinished is called once no animation is
|
||||
* running anymore
|
||||
@@ -814,33 +322,11 @@ public class StackStateAnimator {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
mWasCancelled = false;
|
||||
mAnimatorSet.add(animation);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static <T> T getChildTag(View child, int tag) {
|
||||
return (T) child.getTag(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the previous animator and get the duration of the new animation.
|
||||
*
|
||||
* @param duration the new duration
|
||||
* @param previousAnimator the animator which was running before
|
||||
* @return the new duration
|
||||
*/
|
||||
private long cancelAnimatorAndGetNewDuration(long duration, ValueAnimator previousAnimator) {
|
||||
long newDuration = duration;
|
||||
if (previousAnimator != null) {
|
||||
// We take either the desired length of the new animation or the remaining time of
|
||||
// the previous animator, whichever is longer.
|
||||
newDuration = Math.max(previousAnimator.getDuration()
|
||||
- previousAnimator.getCurrentPlayTime(), newDuration);
|
||||
previousAnimator.cancel();
|
||||
}
|
||||
return newDuration;
|
||||
}
|
||||
|
||||
private void onAnimationFinished() {
|
||||
mHostLayout.onChildAnimationFinished();
|
||||
for (View v : mChildrenToClearFromOverlay) {
|
||||
@@ -942,12 +428,13 @@ public class StackStateAnimator {
|
||||
// We temporarily enable Y animations, the real filter will be combined
|
||||
// afterwards anyway
|
||||
mAnimationFilter.animateY = true;
|
||||
startViewAnimations(changingView, mTmpState,
|
||||
mAnimationProperties.delay =
|
||||
event.animationType == NotificationStackScrollLayout
|
||||
.AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
|
||||
? ANIMATION_DELAY_HEADS_UP
|
||||
: 0,
|
||||
ANIMATION_DURATION_HEADS_UP_DISAPPEAR);
|
||||
? ANIMATION_DELAY_HEADS_UP
|
||||
: 0;
|
||||
mAnimationProperties.duration = ANIMATION_DURATION_HEADS_UP_DISAPPEAR;
|
||||
mTmpState.animateTo(changingView, mAnimationProperties);
|
||||
mChildrenToClearFromOverlay.add(changingView);
|
||||
}
|
||||
}
|
||||
@@ -1007,38 +494,6 @@ public class StackStateAnimator {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the end value of the height animation running on a view or the actualHeight
|
||||
* if no animation is running.
|
||||
*/
|
||||
public static int getFinalActualHeight(ExpandableView view) {
|
||||
if (view == null) {
|
||||
return 0;
|
||||
}
|
||||
ValueAnimator heightAnimator = getChildTag(view, TAG_ANIMATOR_HEIGHT);
|
||||
if (heightAnimator == null) {
|
||||
return view.getActualHeight();
|
||||
} else {
|
||||
return getChildTag(view, TAG_END_HEIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the end value of the yTranslation animation running on a view or the yTranslation
|
||||
* if no animation is running.
|
||||
*/
|
||||
public static float getFinalTranslationY(View view) {
|
||||
if (view == null) {
|
||||
return 0;
|
||||
}
|
||||
ValueAnimator yAnimator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Y);
|
||||
if (yAnimator == null) {
|
||||
return view.getTranslationY();
|
||||
} else {
|
||||
return getChildTag(view, TAG_END_TRANSLATION_Y);
|
||||
}
|
||||
}
|
||||
|
||||
public void setHeadsUpAppearHeightBottom(int headsUpAppearHeightBottom) {
|
||||
mHeadsUpAppearHeightBottom = headsUpAppearHeightBottom;
|
||||
}
|
||||
|
||||
@@ -16,9 +16,18 @@
|
||||
|
||||
package com.android.systemui.statusbar.stack;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.view.View;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import com.android.systemui.Interpolators;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.statusbar.ExpandableView;
|
||||
import com.android.systemui.statusbar.policy.HeadsUpManager;
|
||||
|
||||
/**
|
||||
* A state of a view. This can be used to apply a set of view properties to a view with
|
||||
@@ -27,6 +36,16 @@ import com.android.systemui.statusbar.ExpandableView;
|
||||
*/
|
||||
public class ViewState {
|
||||
|
||||
private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag;
|
||||
private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag;
|
||||
private static final int TAG_ANIMATOR_ALPHA = R.id.alpha_animator_tag;
|
||||
private static final int TAG_END_TRANSLATION_Y = R.id.translation_y_animator_end_value_tag;
|
||||
private static final int TAG_END_TRANSLATION_Z = R.id.translation_z_animator_end_value_tag;
|
||||
private static final int TAG_END_ALPHA = R.id.alpha_animator_end_value_tag;
|
||||
private static final int TAG_START_TRANSLATION_Y = R.id.translation_y_animator_start_value_tag;
|
||||
private static final int TAG_START_TRANSLATION_Z = R.id.translation_z_animator_start_value_tag;
|
||||
private static final int TAG_START_ALPHA = R.id.alpha_animator_start_value_tag;
|
||||
|
||||
public float alpha;
|
||||
public float xTranslation;
|
||||
public float yTranslation;
|
||||
@@ -119,4 +138,305 @@ public class ViewState {
|
||||
view.setScaleY(this.scaleY);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start an animation to this viewstate
|
||||
* @param child the view to animate
|
||||
* @param animationProperties the properties of the animation
|
||||
*/
|
||||
public void animateTo(View child, AnimationProperties animationProperties) {
|
||||
boolean wasVisible = child.getVisibility() == View.VISIBLE;
|
||||
final float alpha = this.alpha;
|
||||
if (!wasVisible && (alpha != 0 || child.getAlpha() != 0)
|
||||
&& !this.gone && !this.hidden) {
|
||||
child.setVisibility(View.VISIBLE);
|
||||
}
|
||||
boolean yTranslationChanging = child.getTranslationY() != this.yTranslation;
|
||||
boolean zTranslationChanging = child.getTranslationZ() != this.zTranslation;
|
||||
float childAlpha = child.getAlpha();
|
||||
boolean alphaChanging = this.alpha != childAlpha;
|
||||
if (child instanceof ExpandableView) {
|
||||
// We don't want views to change visibility when they are animating to GONE
|
||||
alphaChanging &= !((ExpandableView) child).willBeGone();
|
||||
}
|
||||
|
||||
// start translationY animation
|
||||
if (yTranslationChanging) {
|
||||
startYTranslationAnimation(child, animationProperties);
|
||||
} else {
|
||||
abortAnimation(child, TAG_ANIMATOR_TRANSLATION_Y);
|
||||
}
|
||||
|
||||
// start translationZ animation
|
||||
if (zTranslationChanging) {
|
||||
startZTranslationAnimation(child, animationProperties);
|
||||
} else {
|
||||
abortAnimation(child, TAG_ANIMATOR_TRANSLATION_Z);
|
||||
}
|
||||
|
||||
// start alpha animation
|
||||
if (alphaChanging && child.getTranslationX() == 0) {
|
||||
startAlphaAnimation(child, animationProperties);
|
||||
} else {
|
||||
abortAnimation(child, TAG_ANIMATOR_ALPHA);
|
||||
}
|
||||
}
|
||||
|
||||
private void startAlphaAnimation(final View child, AnimationProperties properties) {
|
||||
Float previousStartValue = getChildTag(child,TAG_START_ALPHA);
|
||||
Float previousEndValue = getChildTag(child,TAG_END_ALPHA);
|
||||
final float newEndValue = this.alpha;
|
||||
if (previousEndValue != null && previousEndValue == newEndValue) {
|
||||
return;
|
||||
}
|
||||
ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_ALPHA);
|
||||
AnimationFilter filter = properties.getAnimationFilter();
|
||||
if (!filter.animateAlpha) {
|
||||
// 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();
|
||||
float relativeDiff = newEndValue - previousEndValue;
|
||||
float newStartValue = previousStartValue + relativeDiff;
|
||||
values[0].setFloatValues(newStartValue, newEndValue);
|
||||
child.setTag(TAG_START_ALPHA, newStartValue);
|
||||
child.setTag(TAG_END_ALPHA, newEndValue);
|
||||
previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
|
||||
return;
|
||||
} else {
|
||||
// no new animation needed, let's just apply the value
|
||||
child.setAlpha(newEndValue);
|
||||
if (newEndValue == 0) {
|
||||
child.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.ALPHA,
|
||||
child.getAlpha(), newEndValue);
|
||||
animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
|
||||
// Handle layer type
|
||||
child.setLayerType(View.LAYER_TYPE_HARDWARE, null);
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
public boolean mWasCancelled;
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
child.setLayerType(View.LAYER_TYPE_NONE, null);
|
||||
if (newEndValue == 0 && !mWasCancelled) {
|
||||
child.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
// remove the tag when the animation is finished
|
||||
child.setTag(TAG_ANIMATOR_ALPHA, null);
|
||||
child.setTag(TAG_START_ALPHA, null);
|
||||
child.setTag(TAG_END_ALPHA, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
mWasCancelled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
mWasCancelled = false;
|
||||
}
|
||||
});
|
||||
long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
|
||||
animator.setDuration(newDuration);
|
||||
if (properties.delay > 0 && (previousAnimator == null
|
||||
|| previousAnimator.getAnimatedFraction() == 0)) {
|
||||
animator.setStartDelay(properties.delay);
|
||||
}
|
||||
AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
|
||||
if (listener != null) {
|
||||
animator.addListener(listener);
|
||||
}
|
||||
|
||||
startAnimator(animator, listener);
|
||||
child.setTag(TAG_ANIMATOR_ALPHA, animator);
|
||||
child.setTag(TAG_START_ALPHA, child.getAlpha());
|
||||
child.setTag(TAG_END_ALPHA, newEndValue);
|
||||
}
|
||||
|
||||
private void startZTranslationAnimation(final View child, AnimationProperties properties) {
|
||||
Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Z);
|
||||
Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Z);
|
||||
float newEndValue = this.zTranslation;
|
||||
if (previousEndValue != null && previousEndValue == newEndValue) {
|
||||
return;
|
||||
}
|
||||
ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Z);
|
||||
AnimationFilter filter = properties.getAnimationFilter();
|
||||
if (!filter.animateZ) {
|
||||
// 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();
|
||||
float relativeDiff = newEndValue - previousEndValue;
|
||||
float newStartValue = previousStartValue + relativeDiff;
|
||||
values[0].setFloatValues(newStartValue, newEndValue);
|
||||
child.setTag(TAG_START_TRANSLATION_Z, newStartValue);
|
||||
child.setTag(TAG_END_TRANSLATION_Z, newEndValue);
|
||||
previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
|
||||
return;
|
||||
} else {
|
||||
// no new animation needed, let's just apply the value
|
||||
child.setTranslationZ(newEndValue);
|
||||
}
|
||||
}
|
||||
|
||||
ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Z,
|
||||
child.getTranslationZ(), newEndValue);
|
||||
animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
|
||||
long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
|
||||
animator.setDuration(newDuration);
|
||||
if (properties.delay > 0 && (previousAnimator == null
|
||||
|| previousAnimator.getAnimatedFraction() == 0)) {
|
||||
animator.setStartDelay(properties.delay);
|
||||
}
|
||||
AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
|
||||
if (listener != null) {
|
||||
animator.addListener(listener);
|
||||
}
|
||||
// remove the tag when the animation is finished
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
child.setTag(TAG_ANIMATOR_TRANSLATION_Z, null);
|
||||
child.setTag(TAG_START_TRANSLATION_Z, null);
|
||||
child.setTag(TAG_END_TRANSLATION_Z, null);
|
||||
}
|
||||
});
|
||||
startAnimator(animator, listener);
|
||||
child.setTag(TAG_ANIMATOR_TRANSLATION_Z, animator);
|
||||
child.setTag(TAG_START_TRANSLATION_Z, child.getTranslationZ());
|
||||
child.setTag(TAG_END_TRANSLATION_Z, newEndValue);
|
||||
}
|
||||
|
||||
private void startYTranslationAnimation(final View child, AnimationProperties properties) {
|
||||
Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Y);
|
||||
Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y);
|
||||
float newEndValue = this.yTranslation;
|
||||
if (previousEndValue != null && previousEndValue == newEndValue) {
|
||||
return;
|
||||
}
|
||||
ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y);
|
||||
AnimationFilter filter = properties.getAnimationFilter();
|
||||
if (!filter.animateY) {
|
||||
// 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();
|
||||
float relativeDiff = newEndValue - previousEndValue;
|
||||
float newStartValue = previousStartValue + relativeDiff;
|
||||
values[0].setFloatValues(newStartValue, newEndValue);
|
||||
child.setTag(TAG_START_TRANSLATION_Y, newStartValue);
|
||||
child.setTag(TAG_END_TRANSLATION_Y, newEndValue);
|
||||
previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
|
||||
return;
|
||||
} else {
|
||||
// no new animation needed, let's just apply the value
|
||||
child.setTranslationY(newEndValue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y,
|
||||
child.getTranslationY(), newEndValue);
|
||||
Interpolator customInterpolator = properties.getCustomInterpolator(child,
|
||||
View.TRANSLATION_Y);
|
||||
Interpolator interpolator = customInterpolator != null ? customInterpolator
|
||||
: Interpolators.FAST_OUT_SLOW_IN;
|
||||
animator.setInterpolator(interpolator);
|
||||
long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
|
||||
animator.setDuration(newDuration);
|
||||
if (properties.delay > 0 && (previousAnimator == null
|
||||
|| previousAnimator.getAnimatedFraction() == 0)) {
|
||||
animator.setStartDelay(properties.delay);
|
||||
}
|
||||
AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
|
||||
if (listener != null) {
|
||||
animator.addListener(listener);
|
||||
}
|
||||
// remove the tag when the animation is finished
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
HeadsUpManager.setIsClickedNotification(child, false);
|
||||
child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null);
|
||||
child.setTag(TAG_START_TRANSLATION_Y, null);
|
||||
child.setTag(TAG_END_TRANSLATION_Y, null);
|
||||
onYTranslationAnimationFinished();
|
||||
}
|
||||
});
|
||||
startAnimator(animator, listener);
|
||||
child.setTag(TAG_ANIMATOR_TRANSLATION_Y, animator);
|
||||
child.setTag(TAG_START_TRANSLATION_Y, child.getTranslationY());
|
||||
child.setTag(TAG_END_TRANSLATION_Y, newEndValue);
|
||||
}
|
||||
|
||||
protected void onYTranslationAnimationFinished() {
|
||||
}
|
||||
|
||||
protected void startAnimator(Animator animator, AnimatorListenerAdapter listener) {
|
||||
if (listener != null) {
|
||||
// Even if there's a delay we'd want to notify it of the start immediately.
|
||||
listener.onAnimationStart(animator);
|
||||
}
|
||||
animator.start();
|
||||
}
|
||||
|
||||
public static <T> T getChildTag(View child, int tag) {
|
||||
return (T) child.getTag(tag);
|
||||
}
|
||||
|
||||
protected void abortAnimation(View child, int animatorTag) {
|
||||
Animator previousAnimator = getChildTag(child, animatorTag);
|
||||
if (previousAnimator != null) {
|
||||
previousAnimator.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the previous animator and get the duration of the new animation.
|
||||
*
|
||||
* @param duration the new duration
|
||||
* @param previousAnimator the animator which was running before
|
||||
* @return the new duration
|
||||
*/
|
||||
protected long cancelAnimatorAndGetNewDuration(long duration, ValueAnimator previousAnimator) {
|
||||
long newDuration = duration;
|
||||
if (previousAnimator != null) {
|
||||
// We take either the desired length of the new animation or the remaining time of
|
||||
// the previous animator, whichever is longer.
|
||||
newDuration = Math.max(previousAnimator.getDuration()
|
||||
- previousAnimator.getCurrentPlayTime(), newDuration);
|
||||
previousAnimator.cancel();
|
||||
}
|
||||
return newDuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the end value of the yTranslation animation running on a view or the yTranslation
|
||||
* if no animation is running.
|
||||
*/
|
||||
public static float getFinalTranslationY(View view) {
|
||||
if (view == null) {
|
||||
return 0;
|
||||
}
|
||||
ValueAnimator yAnimator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Y);
|
||||
if (yAnimator == null) {
|
||||
return view.getTranslationY();
|
||||
} else {
|
||||
return getChildTag(view, TAG_END_TRANSLATION_Y);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isAnimatingY(ExpandableView child) {
|
||||
return getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y) != null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user