1 /*
2  * Copyright (C) 2018 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 package com.android.launcher3.anim;
17 
18 import static com.android.launcher3.LauncherAnimUtils.VIEW_BACKGROUND_COLOR;
19 import static com.android.launcher3.anim.AnimatorPlaybackController.addAnimationHoldersRecur;
20 
21 import android.animation.Animator;
22 import android.animation.Animator.AnimatorListener;
23 import android.animation.AnimatorSet;
24 import android.animation.ObjectAnimator;
25 import android.animation.TimeInterpolator;
26 import android.animation.ValueAnimator;
27 import android.graphics.drawable.ColorDrawable;
28 import android.util.FloatProperty;
29 import android.util.IntProperty;
30 import android.view.View;
31 
32 import com.android.launcher3.anim.AnimatorPlaybackController.Holder;
33 
34 import java.util.ArrayList;
35 import java.util.function.Consumer;
36 
37 /**
38  * Utility class to keep track of a running animation.
39  *
40  * This class allows attaching end callbacks to an animation is intended to be used with
41  * {@link com.android.launcher3.anim.AnimatorPlaybackController}, since in that case
42  * AnimationListeners are not properly dispatched.
43  *
44  * TODO: Find a better name
45  */
46 public class PendingAnimation implements PropertySetter {
47 
48     private final ArrayList<Holder> mAnimHolders = new ArrayList<>();
49     private final AnimatorSet mAnim;
50     private final long mDuration;
51 
52     private ValueAnimator mProgressAnimator;
53 
PendingAnimation(long duration)54     public PendingAnimation(long  duration) {
55         mDuration = duration;
56         mAnim = new AnimatorSet();
57     }
58 
getDuration()59     public long getDuration() {
60         return mDuration;
61     }
62 
63     /**
64      * Utility method to sent an interpolator on an animation and add it to the list
65      */
add(Animator anim, TimeInterpolator interpolator, SpringProperty springProperty)66     public void add(Animator anim, TimeInterpolator interpolator, SpringProperty springProperty) {
67         anim.setInterpolator(interpolator);
68         add(anim, springProperty);
69     }
70 
add(Animator anim)71     public void add(Animator anim) {
72         add(anim, SpringProperty.DEFAULT);
73     }
74 
add(Animator a, SpringProperty springProperty)75     public void add(Animator a, SpringProperty springProperty) {
76         mAnim.play(a.setDuration(mDuration));
77         addAnimationHoldersRecur(a, mDuration, springProperty, mAnimHolders);
78     }
79 
80     @Override
setViewAlpha(View view, float alpha, TimeInterpolator interpolator)81     public void setViewAlpha(View view, float alpha, TimeInterpolator interpolator) {
82         if (view == null || view.getAlpha() == alpha) {
83             return;
84         }
85         ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.ALPHA, alpha);
86         anim.addListener(new AlphaUpdateListener(view));
87         anim.setInterpolator(interpolator);
88         add(anim);
89     }
90 
91     @Override
setViewBackgroundColor(View view, int color, TimeInterpolator interpolator)92     public void setViewBackgroundColor(View view, int color, TimeInterpolator interpolator) {
93         if (view == null || (view.getBackground() instanceof ColorDrawable
94                 && ((ColorDrawable) view.getBackground()).getColor() == color)) {
95             return;
96         }
97         ObjectAnimator anim = ObjectAnimator.ofArgb(view, VIEW_BACKGROUND_COLOR, color);
98         anim.setInterpolator(interpolator);
99         add(anim);
100     }
101 
102     @Override
setFloat(T target, FloatProperty<T> property, float value, TimeInterpolator interpolator)103     public <T> void setFloat(T target, FloatProperty<T> property, float value,
104             TimeInterpolator interpolator) {
105         if (property.get(target) == value) {
106             return;
107         }
108         Animator anim = ObjectAnimator.ofFloat(target, property, value);
109         anim.setDuration(mDuration).setInterpolator(interpolator);
110         add(anim);
111     }
112 
addFloat(T target, FloatProperty<T> property, float from, float to, TimeInterpolator interpolator)113     public <T> void addFloat(T target, FloatProperty<T> property, float from, float to,
114             TimeInterpolator interpolator) {
115         Animator anim = ObjectAnimator.ofFloat(target, property, from, to);
116         anim.setInterpolator(interpolator);
117         add(anim);
118     }
119 
120     @Override
setInt(T target, IntProperty<T> property, int value, TimeInterpolator interpolator)121     public <T> void setInt(T target, IntProperty<T> property, int value,
122             TimeInterpolator interpolator) {
123         if (property.get(target) == value) {
124             return;
125         }
126         Animator anim = ObjectAnimator.ofInt(target, property, value);
127         anim.setInterpolator(interpolator);
128         add(anim);
129     }
130 
131     /**
132      * Adds a callback to be run on every frame of the animation
133      */
addOnFrameCallback(Runnable runnable)134     public void addOnFrameCallback(Runnable runnable) {
135         addOnFrameListener(anim -> runnable.run());
136     }
137 
138     /**
139      * Adds a listener to be run on every frame of the animation
140      */
addOnFrameListener(ValueAnimator.AnimatorUpdateListener listener)141     public void addOnFrameListener(ValueAnimator.AnimatorUpdateListener listener) {
142         if (mProgressAnimator == null) {
143             mProgressAnimator = ValueAnimator.ofFloat(0, 1);
144         }
145 
146         mProgressAnimator.addUpdateListener(listener);
147     }
148 
149     /**
150      * @see AnimatorSet#addListener(AnimatorListener)
151      */
addListener(Animator.AnimatorListener listener)152     public void addListener(Animator.AnimatorListener listener) {
153         mAnim.addListener(listener);
154     }
155 
156     /**
157      * Creates and returns the underlying AnimatorSet
158      */
buildAnim()159     public AnimatorSet buildAnim() {
160         // Add progress animation to the end, so that frame callback is called after all the other
161         // animation update.
162         if (mProgressAnimator != null) {
163             add(mProgressAnimator);
164             mProgressAnimator = null;
165         }
166         if (mAnimHolders.isEmpty()) {
167             // Add a placeholder animation to that the duration is respected
168             add(ValueAnimator.ofFloat(0, 1).setDuration(mDuration));
169         }
170         return mAnim;
171     }
172 
173     /**
174      * Creates a controller for this animation
175      */
createPlaybackController()176     public AnimatorPlaybackController createPlaybackController() {
177         return new AnimatorPlaybackController(buildAnim(), mDuration, mAnimHolders);
178     }
179 
180     /**
181      * Add a listener of receiving the success/failure callback in the end.
182      */
addEndListener(Consumer<Boolean> listener)183     public void addEndListener(Consumer<Boolean> listener) {
184         if (mProgressAnimator == null) {
185             mProgressAnimator = ValueAnimator.ofFloat(0, 1);
186         }
187         mProgressAnimator.addListener(AnimatorListeners.forEndCallback(listener));
188     }
189 }
190