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

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

Made it easier to use updatable Animators

The existing system is extended such that AnimationProperties
can be easier animated and running animations updated.

As a first sample this animates the scale of the
icons in the shelf.

Change-Id: Ic88e8094d53f37ab13f5e9e00796b63d229a5114
Test: runtest systemui
Bug: 32437839
parent 061d9072
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -20,12 +20,16 @@
    <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="scale_x_animator_tag"/>
    <item type="id" name="scale_y_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="scale_x_animator_end_value_tag"/>
    <item type="id" name="scale_y_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"/>
@@ -33,6 +37,8 @@
    <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="scale_x_animator_start_value_tag"/>
    <item type="id" name="scale_y_animator_start_value_tag"/>
    <item type="id" name="alpha_animator_start_value_tag"/>
    <item type="id" name="top_inset_animator_start_value_tag"/>
    <item type="id" name="height_animator_start_value_tag"/>
+111 −0
Original line number Diff line number Diff line
/*
 * 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.notification;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.util.Property;
import android.view.View;
import android.view.animation.Interpolator;

import com.android.systemui.Interpolators;
import com.android.systemui.statusbar.stack.AnimationFilter;
import com.android.systemui.statusbar.stack.AnimationProperties;
import com.android.systemui.statusbar.stack.ViewState;

/**
 * An animator to animate properties
 */
public class PropertyAnimator {

    public static <T extends View> void startAnimation(final T view,
            AnimatableProperty animatableProperty, float newEndValue,
            AnimationProperties properties) {
        Property<T, Float> property = animatableProperty.getProperty();
        int animationStartTag = animatableProperty.getAnimationStartTag();
        int animationEndTag = animatableProperty.getAnimationEndTag();
        Float previousStartValue = ViewState.getChildTag(view, animationStartTag);
        Float previousEndValue = ViewState.getChildTag(view, animationEndTag);
        if (previousEndValue != null && previousEndValue == newEndValue) {
            return;
        }
        int animatorTag = animatableProperty.getAnimatorTag();
        ValueAnimator previousAnimator = ViewState.getChildTag(view, animatorTag);
        AnimationFilter filter = properties.getAnimationFilter();
        if (!filter.shouldAnimateProperty(property)) {
            // 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);
                view.setTag(animationStartTag, newStartValue);
                view.setTag(animationEndTag, newEndValue);
                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
                return;
            } else {
                // no new animation needed, let's just apply the value
                property.set(view, newEndValue);
                return;
            }
        }

        Float currentValue = property.get(view);
        ValueAnimator animator = ValueAnimator.ofFloat(currentValue, newEndValue);
        animator.addUpdateListener(
                animation -> property.set(view, (Float) animation.getAnimatedValue()));
        Interpolator customInterpolator = properties.getCustomInterpolator(view, property);
        Interpolator interpolator =  customInterpolator != null ? customInterpolator
                : Interpolators.FAST_OUT_SLOW_IN;
        animator.setInterpolator(interpolator);
        long newDuration = ViewState.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) {
                view.setTag(animatorTag, null);
                view.setTag(animationStartTag, null);
                view.setTag(animationEndTag, null);
            }
        });
        ViewState.startAnimator(animator, listener);
        view.setTag(animatorTag, animator);
        view.setTag(animationStartTag, currentValue);
        view.setTag(animationEndTag, newEndValue);
    }

    public interface AnimatableProperty {
        int getAnimationStartTag();
        int getAnimationEndTag();
        int getAnimatorTag();
        Property getProperty();
    }
}
+3 −4
Original line number Diff line number Diff line
@@ -62,8 +62,8 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {
    }.setDuration(200);

    private static final AnimationProperties ICON_ANIMATION_PROPERTIES = new AnimationProperties() {
        private AnimationFilter mAnimationFilter = new AnimationFilter().animateY().animateAlpha();
        // TODO: add scale
        private AnimationFilter mAnimationFilter = new AnimationFilter().animateY().animateAlpha()
                .animateScale();

        @Override
        public AnimationFilter getAnimationFilter() {
@@ -75,13 +75,12 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout {

    private static final AnimationProperties mTempProperties = new AnimationProperties() {
        private AnimationFilter mAnimationFilter = new AnimationFilter();
        // TODO: add scale

        @Override
        public AnimationFilter getAnimationFilter() {
            return mAnimationFilter;
        }
    }.setDuration(CANNED_ANIMATION_DURATION);
    };

    private static final AnimationProperties ADD_ICON_PROPERTIES = new AnimationProperties() {
        private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha();
+24 −0
Original line number Diff line number Diff line
@@ -16,7 +16,12 @@

package com.android.systemui.statusbar.stack;

import android.support.v4.util.ArraySet;
import android.util.Property;
import android.view.View;

import java.util.ArrayList;
import java.util.HashSet;

/**
 * Filters the animations for only a certain type of properties.
@@ -37,12 +42,19 @@ public class AnimationFilter {
    boolean hasDarkEvent;
    boolean hasHeadsUpDisappearClickEvent;
    int darkAnimationOriginIndex;
    private ArraySet<Property> mAnimatedProperties = new ArraySet<>();

    public AnimationFilter animateAlpha() {
        animateAlpha = true;
        return this;
    }

    public AnimationFilter animateScale() {
        animate(View.SCALE_X);
        animate(View.SCALE_Y);
        return this;
    }

    public AnimationFilter animateX() {
        animateX = true;
        return this;
@@ -132,6 +144,7 @@ public class AnimationFilter {
        animateHideSensitive |= filter.animateHideSensitive;
        animateShadowAlpha |= filter.animateShadowAlpha;
        hasDelays |= filter.hasDelays;
        mAnimatedProperties.addAll(filter.mAnimatedProperties);
    }

    public void reset() {
@@ -151,5 +164,16 @@ public class AnimationFilter {
        hasHeadsUpDisappearClickEvent = false;
        darkAnimationOriginIndex =
                NotificationStackScrollLayout.AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
        mAnimatedProperties.clear();
    }

    public AnimationFilter animate(Property property) {
        mAnimatedProperties.add(property);
        return this;
    }

    public boolean shouldAnimateProperty(Property property) {
        // TODO: migrate all existing animators to properties
        return mAnimatedProperties.contains(property);
    }
}
+93 −7
Original line number Diff line number Diff line
@@ -21,12 +21,14 @@ 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.animation.Interpolator;

import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.statusbar.ExpandableView;
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.policy.HeadsUpManager;

/**
@@ -60,6 +62,54 @@ public class ViewState {
    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 PropertyAnimator.AnimatableProperty SCALE_X_PROPERTY
            = new PropertyAnimator.AnimatableProperty() {

        @Override
        public int getAnimationStartTag() {
            return R.id.scale_x_animator_start_value_tag;
        }

        @Override
        public int getAnimationEndTag() {
            return R.id.scale_x_animator_end_value_tag;
        }

        @Override
        public int getAnimatorTag() {
            return R.id.scale_x_animator_tag;
        }

        @Override
        public Property getProperty() {
            return View.SCALE_X;
        }
    };

    private static final PropertyAnimator.AnimatableProperty SCALE_Y_PROPERTY
            = new PropertyAnimator.AnimatableProperty() {

        @Override
        public int getAnimationStartTag() {
            return R.id.scale_y_animator_start_value_tag;
        }

        @Override
        public int getAnimationEndTag() {
            return R.id.scale_y_animator_end_value_tag;
        }

        @Override
        public int getAnimatorTag() {
            return R.id.scale_y_animator_tag;
        }

        @Override
        public Property getProperty() {
            return View.SCALE_Y;
        }
    };

    public float alpha;
    public float xTranslation;
    public float yTranslation;
@@ -125,13 +175,19 @@ public class ViewState {
        }

        // apply scaleX
        if (view.getScaleX() != this.scaleX) {
            view.setScaleX(this.scaleX);
        boolean animatingScaleX = isAnimating(view, SCALE_X_PROPERTY);
        if (animatingScaleX) {
            updateAnimation(view, SCALE_X_PROPERTY, scaleX);
        } else if (view.getScaleX() != scaleX) {
            view.setScaleX(scaleX);
        }

        // apply scaleY
        if (view.getScaleY() != this.scaleY) {
            view.setScaleY(this.scaleY);
        boolean animatingScaleY = isAnimating(view, SCALE_Y_PROPERTY);
        if (animatingScaleY) {
            updateAnimation(view, SCALE_Y_PROPERTY, scaleY);
        } else if (view.getScaleY() != scaleY) {
            view.setScaleY(scaleY);
        }

        boolean becomesInvisible = this.alpha == 0.0f || (this.hidden && !isAnimating(view));
@@ -179,13 +235,23 @@ public class ViewState {
        if (isAnimating(view, TAG_ANIMATOR_ALPHA)) {
            return true;
        }
        if (isAnimating(view, SCALE_X_PROPERTY)) {
            return true;
        }
        if (isAnimating(view, SCALE_Y_PROPERTY)) {
            return true;
        }
        return false;
    }

    private boolean isAnimating(View view, int tag) {
    private static boolean isAnimating(View view, int tag) {
        return getChildTag(view, tag) != null;
    }

    public static boolean isAnimating(View view, PropertyAnimator.AnimatableProperty property) {
        return getChildTag(view, property.getAnimatorTag()) != null;
    }

    /**
     * Start an animation to this viewstate
     * @param child the view to animate
@@ -226,6 +292,20 @@ public class ViewState {
            abortAnimation(child, TAG_ANIMATOR_TRANSLATION_Z);
        }

        // start scaleX animation
        if (child.getScaleX() != scaleX) {
            PropertyAnimator.startAnimation(child, SCALE_X_PROPERTY, scaleX, animationProperties);
        } else {
            abortAnimation(child, SCALE_X_PROPERTY.getAnimatorTag());
        }

        // start scaleX animation
        if (child.getScaleY() != scaleY) {
            PropertyAnimator.startAnimation(child, SCALE_Y_PROPERTY, scaleY, animationProperties);
        } else {
            abortAnimation(child, SCALE_Y_PROPERTY.getAnimatorTag());
        }

        // start alpha animation
        if (alphaChanging) {
            startAlphaAnimation(child, animationProperties);
@@ -320,6 +400,11 @@ public class ViewState {
        startZTranslationAnimation(view, NO_NEW_ANIMATIONS);
    }

    private void updateAnimation(View view, PropertyAnimator.AnimatableProperty property,
            float endValue) {
        PropertyAnimator.startAnimation(view, property, endValue, 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);
@@ -514,7 +599,7 @@ public class ViewState {
        }
    }

    protected void startAnimator(Animator animator, AnimatorListenerAdapter listener) {
    public static 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);
@@ -540,7 +625,8 @@ public class ViewState {
     * @param previousAnimator the animator which was running before
     * @return the new duration
     */
    protected long cancelAnimatorAndGetNewDuration(long duration, ValueAnimator previousAnimator) {
    public static 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
Loading