1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.systemui.statusbar.notification;
18 
19 import android.animation.Animator;
20 import android.animation.AnimatorListenerAdapter;
21 import android.animation.PropertyValuesHolder;
22 import android.animation.ValueAnimator;
23 import android.util.Property;
24 import android.view.View;
25 import android.view.animation.Interpolator;
26 
27 import com.android.systemui.Interpolators;
28 import com.android.systemui.statusbar.notification.stack.AnimationFilter;
29 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
30 import com.android.systemui.statusbar.notification.stack.ViewState;
31 
32 /**
33  * An animator to animate properties
34  */
35 public class PropertyAnimator {
36 
setProperty(final T view, AnimatableProperty animatableProperty, float newEndValue, AnimationProperties properties, boolean animated)37     public static <T extends View> void setProperty(final T view,
38             AnimatableProperty animatableProperty, float newEndValue,
39             AnimationProperties properties, boolean animated) {
40         int animatorTag = animatableProperty.getAnimatorTag();
41         ValueAnimator previousAnimator = ViewState.getChildTag(view, animatorTag);
42         if (previousAnimator != null || animated) {
43             startAnimation(view, animatableProperty, newEndValue, properties);
44         } else {
45             // no new animation needed, let's just apply the value
46             animatableProperty.getProperty().set(view, newEndValue);
47         }
48     }
49 
startAnimation(final T view, AnimatableProperty animatableProperty, float newEndValue, AnimationProperties properties)50     public static <T extends View> void startAnimation(final T view,
51             AnimatableProperty animatableProperty, float newEndValue,
52             AnimationProperties properties) {
53         Property<T, Float> property = animatableProperty.getProperty();
54         int animationStartTag = animatableProperty.getAnimationStartTag();
55         int animationEndTag = animatableProperty.getAnimationEndTag();
56         Float previousStartValue = ViewState.getChildTag(view, animationStartTag);
57         Float previousEndValue = ViewState.getChildTag(view, animationEndTag);
58         if (previousEndValue != null && previousEndValue == newEndValue) {
59             return;
60         }
61         int animatorTag = animatableProperty.getAnimatorTag();
62         ValueAnimator previousAnimator = ViewState.getChildTag(view, animatorTag);
63         AnimationFilter filter = properties.getAnimationFilter();
64         if (!filter.shouldAnimateProperty(property)) {
65             // just a local update was performed
66             if (previousAnimator != null) {
67                 // we need to increase all animation keyframes of the previous animator by the
68                 // relative change to the end value
69                 PropertyValuesHolder[] values = previousAnimator.getValues();
70                 float relativeDiff = newEndValue - previousEndValue;
71                 float newStartValue = previousStartValue + relativeDiff;
72                 values[0].setFloatValues(newStartValue, newEndValue);
73                 view.setTag(animationStartTag, newStartValue);
74                 view.setTag(animationEndTag, newEndValue);
75                 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
76                 return;
77             } else {
78                 // no new animation needed, let's just apply the value
79                 property.set(view, newEndValue);
80                 return;
81             }
82         }
83 
84         Float currentValue = property.get(view);
85         ValueAnimator animator = ValueAnimator.ofFloat(currentValue, newEndValue);
86         animator.addUpdateListener(
87                 animation -> property.set(view, (Float) animation.getAnimatedValue()));
88         Interpolator customInterpolator = properties.getCustomInterpolator(view, property);
89         Interpolator interpolator =  customInterpolator != null ? customInterpolator
90                 : Interpolators.FAST_OUT_SLOW_IN;
91         animator.setInterpolator(interpolator);
92         long newDuration = ViewState.cancelAnimatorAndGetNewDuration(properties.duration,
93                 previousAnimator);
94         animator.setDuration(newDuration);
95         if (properties.delay > 0 && (previousAnimator == null
96                 || previousAnimator.getAnimatedFraction() == 0)) {
97             animator.setStartDelay(properties.delay);
98         }
99         AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
100         if (listener != null) {
101             animator.addListener(listener);
102         }
103         // remove the tag when the animation is finished
104         animator.addListener(new AnimatorListenerAdapter() {
105             @Override
106             public void onAnimationEnd(Animator animation) {
107                 view.setTag(animatorTag, null);
108                 view.setTag(animationStartTag, null);
109                 view.setTag(animationEndTag, null);
110             }
111         });
112         ViewState.startAnimator(animator, listener);
113         view.setTag(animatorTag, animator);
114         view.setTag(animationStartTag, currentValue);
115         view.setTag(animationEndTag, newEndValue);
116     }
117 
applyImmediately(T view, AnimatableProperty property, float newValue)118     public static <T extends View> void applyImmediately(T view, AnimatableProperty property,
119             float newValue) {
120         cancelAnimation(view, property);
121         property.getProperty().set(view, newValue);
122     }
123 
cancelAnimation(View view, AnimatableProperty property)124     public static void cancelAnimation(View view, AnimatableProperty property) {
125         ValueAnimator animator = (ValueAnimator) view.getTag(property.getAnimatorTag());
126         if (animator != null) {
127             animator.cancel();
128         }
129     }
130 
isAnimating(View view, AnimatableProperty property)131     public static boolean isAnimating(View view, AnimatableProperty property) {
132         return view.getTag(property.getAnimatorTag()) != null;
133     }
134 }
135