Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c40c79ac authored by Selim Cinek's avatar Selim Cinek
Browse files

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
parent d127d792
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -16,18 +16,21 @@
  -->

<resources>
    <item type="id" name="translation_x_animator_tag"/>
    <item type="id" name="translation_y_animator_tag"/>
    <item type="id" name="translation_z_animator_tag"/>
    <item type="id" name="alpha_animator_tag"/>
    <item type="id" name="top_inset_animator_tag"/>
    <item type="id" name="height_animator_tag"/>
    <item type="id" name="shadow_alpha_animator_tag"/>
    <item type="id" name="translation_x_animator_end_value_tag"/>
    <item type="id" name="translation_y_animator_end_value_tag"/>
    <item type="id" name="translation_z_animator_end_value_tag"/>
    <item type="id" name="alpha_animator_end_value_tag"/>
    <item type="id" name="top_inset_animator_end_value_tag"/>
    <item type="id" name="height_animator_end_value_tag"/>
    <item type="id" name="shadow_alpha_animator_end_value_tag"/>
    <item type="id" name="translation_x_animator_start_value_tag"/>
    <item type="id" name="translation_y_animator_start_value_tag"/>
    <item type="id" name="translation_z_animator_start_value_tag"/>
    <item type="id" name="alpha_animator_start_value_tag"/>
+8 −0
Original line number Diff line number Diff line
@@ -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;
+12 −2
Original line number Diff line number Diff line
@@ -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;
    }
}
+120 −8
Original line number Diff line number Diff line
@@ -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);