From c40c79ac66d8162880e0ef76c98f44bc40797bbd Mon Sep 17 00:00:00 2001 From: Selim Cinek Date: Tue, 8 Nov 2016 09:52:52 -0800 Subject: [PATCH] Added the possibility to animate X and refactoring A viewstate can now animate its X value. This also refactors the animation logic such that an application when an animation is running will update the existing animation nicely. Test: manual, observe views animating in X Bug: 32437839 Change-Id: Ic091d87e530af793281ca3f2b1e9370ff5dac236 --- packages/SystemUI/res/values/ids.xml | 3 + .../statusbar/stack/AnimationFilter.java | 8 ++ .../statusbar/stack/AnimationProperties.java | 14 +- .../systemui/statusbar/stack/ViewState.java | 128 ++++++++++++++++-- 4 files changed, 143 insertions(+), 10 deletions(-) diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index 94d79f27e67f9..8c80c71f869e8 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -16,18 +16,21 @@ --> + + + diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java index 561b18a685326..d3d58f90c9e68 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java @@ -23,6 +23,7 @@ import java.util.ArrayList; */ public class AnimationFilter { boolean animateAlpha; + boolean animateX; boolean animateY; boolean animateZ; boolean animateHeight; @@ -42,6 +43,11 @@ public class AnimationFilter { return this; } + public AnimationFilter animateX() { + animateX = true; + return this; + } + public AnimationFilter animateY() { animateY = true; return this; @@ -116,6 +122,7 @@ public class AnimationFilter { private void combineFilter(AnimationFilter filter) { animateAlpha |= filter.animateAlpha; + animateX |= filter.animateX; animateY |= filter.animateY; animateZ |= filter.animateZ; animateHeight |= filter.animateHeight; @@ -129,6 +136,7 @@ public class AnimationFilter { private void reset() { animateAlpha = false; + animateX = false; animateY = false; animateZ = false; animateHeight = false; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationProperties.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationProperties.java index 3cb6e5b1ca2f7..0de774dc381b2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationProperties.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationProperties.java @@ -32,7 +32,7 @@ public class AnimationProperties { * @return an animation filter for this animation. */ public AnimationFilter getAnimationFilter() { - return null; + return new AnimationFilter(); } /** @@ -49,7 +49,17 @@ public class AnimationProperties { /** * Get a custom interpolator for a property instead of the normal one. */ - public Interpolator getCustomInterpolator(View child, Property translationY) { + public Interpolator getCustomInterpolator(View child, Property property) { return null; } + + public AnimationProperties setDuration(long duration) { + this.duration = duration; + return this; + } + + public AnimationProperties setDelay(long delay) { + this.delay = delay; + return this; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java index c120bbbe23cef..124cabb0770f9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/ViewState.java @@ -36,12 +36,26 @@ import com.android.systemui.statusbar.policy.HeadsUpManager; */ public class ViewState { + /** + * Some animation properties that can be used to update running animations but not creating + * any new ones. + */ + protected static final AnimationProperties NO_NEW_ANIMATIONS = new AnimationProperties() { + AnimationFilter mAnimationFilter = new AnimationFilter(); + @Override + public AnimationFilter getAnimationFilter() { + return mAnimationFilter; + } + }; + private static final int TAG_ANIMATOR_TRANSLATION_X = R.id.translation_x_animator_tag; 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_X = R.id.translation_x_animator_end_value_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_X = R.id.translation_x_animator_start_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; @@ -86,7 +100,10 @@ public class ViewState { return; } boolean becomesInvisible = this.alpha == 0.0f || this.hidden; - if (view.getAlpha() != this.alpha) { + boolean animatingAlpha = isAnimating(view, TAG_ANIMATOR_ALPHA); + if (animatingAlpha) { + updateAlphaAnimation(view); + } else if (view.getAlpha() != this.alpha) { // apply layer type boolean becomesFullyVisible = this.alpha == 1.0f; boolean newLayerTypeIsHardware = !becomesInvisible && !becomesFullyVisible @@ -114,17 +131,26 @@ public class ViewState { } // apply xTranslation - if (view.getTranslationX() != this.xTranslation) { + boolean animatingX = isAnimating(view, TAG_ANIMATOR_TRANSLATION_X); + if (animatingX) { + updateAnimationX(view); + } else if (view.getTranslationX() != this.xTranslation){ view.setTranslationX(this.xTranslation); } // apply yTranslation - if (view.getTranslationY() != this.yTranslation) { + boolean animatingY = isAnimating(view, TAG_ANIMATOR_TRANSLATION_Y); + if (animatingY) { + updateAnimationY(view); + } else if (view.getTranslationY() != this.yTranslation) { view.setTranslationY(this.yTranslation); } // apply zTranslation - if (view.getTranslationZ() != this.zTranslation) { + boolean animatingZ = isAnimating(view, TAG_ANIMATOR_TRANSLATION_Z); + if (animatingZ) { + updateAnimationZ(view); + } else if (view.getTranslationZ() != this.zTranslation) { view.setTranslationZ(this.zTranslation); } @@ -139,6 +165,10 @@ public class ViewState { } } + private boolean isAnimating(View view, int tag) { + return getChildTag(view, tag) != null; + } + /** * Start an animation to this viewstate * @param child the view to animate @@ -151,8 +181,6 @@ public class ViewState { && !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) { @@ -160,15 +188,22 @@ public class ViewState { alphaChanging &= !((ExpandableView) child).willBeGone(); } + // start translationX animation + if (child.getTranslationX() != this.xTranslation) { + startXTranslationAnimation(child, animationProperties); + } else { + abortAnimation(child, TAG_ANIMATOR_TRANSLATION_X); + } + // start translationY animation - if (yTranslationChanging) { + if (child.getTranslationY() != this.yTranslation) { startYTranslationAnimation(child, animationProperties); } else { abortAnimation(child, TAG_ANIMATOR_TRANSLATION_Y); } // start translationZ animation - if (zTranslationChanging) { + if (child.getTranslationZ() != this.zTranslation) { startZTranslationAnimation(child, animationProperties); } else { abortAnimation(child, TAG_ANIMATOR_TRANSLATION_Z); @@ -182,6 +217,10 @@ public class ViewState { } } + private void updateAlphaAnimation(View view) { + startAlphaAnimation(view, NO_NEW_ANIMATIONS); + } + private void startAlphaAnimation(final View child, AnimationProperties properties) { Float previousStartValue = getChildTag(child,TAG_START_ALPHA); Float previousEndValue = getChildTag(child,TAG_END_ALPHA); @@ -260,6 +299,10 @@ public class ViewState { child.setTag(TAG_END_ALPHA, newEndValue); } + private void updateAnimationZ(View view) { + startZTranslationAnimation(view, NO_NEW_ANIMATIONS); + } + private void startZTranslationAnimation(final View child, AnimationProperties properties) { Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Z); Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Z); @@ -316,6 +359,75 @@ public class ViewState { child.setTag(TAG_END_TRANSLATION_Z, newEndValue); } + private void updateAnimationX(View view) { + startXTranslationAnimation(view, NO_NEW_ANIMATIONS); + } + + private void startXTranslationAnimation(final View child, AnimationProperties properties) { + Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_X); + Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_X); + float newEndValue = this.xTranslation; + if (previousEndValue != null && previousEndValue == newEndValue) { + return; + } + ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_X); + AnimationFilter filter = properties.getAnimationFilter(); + if (!filter.animateX) { + // 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_X, newStartValue); + child.setTag(TAG_END_TRANSLATION_X, newEndValue); + previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); + return; + } else { + // no new animation needed, let's just apply the value + child.setTranslationX(newEndValue); + return; + } + } + + ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_X, + child.getTranslationX(), newEndValue); + Interpolator customInterpolator = properties.getCustomInterpolator(child, + View.TRANSLATION_X); + 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) { + child.setTag(TAG_ANIMATOR_TRANSLATION_X, null); + child.setTag(TAG_START_TRANSLATION_X, null); + child.setTag(TAG_END_TRANSLATION_X, null); + } + }); + startAnimator(animator, listener); + child.setTag(TAG_ANIMATOR_TRANSLATION_X, animator); + child.setTag(TAG_START_TRANSLATION_X, child.getTranslationX()); + child.setTag(TAG_END_TRANSLATION_X, newEndValue); + } + + private void updateAnimationY(View view) { + startYTranslationAnimation(view, NO_NEW_ANIMATIONS); + } + private void startYTranslationAnimation(final View child, AnimationProperties properties) { Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Y); Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y);