1 /*
2  * Copyright (C) 2022 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.launcher3.anim;
18 
19 import static com.android.launcher3.LauncherAnimUtils.VIEW_BACKGROUND_COLOR;
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 androidx.annotation.NonNull;
33 
34 import java.util.function.Consumer;
35 
36 /**
37  * Extension of {@link PropertySetter} which applies the property through an animation
38  */
39 public class AnimatedPropertySetter extends PropertySetter {
40 
41     protected final AnimatorSet mAnim = new AnimatorSet();
42     protected ValueAnimator mProgressAnimator;
43 
44     @Override
setViewAlpha(View view, float alpha, TimeInterpolator interpolator)45     public Animator setViewAlpha(View view, float alpha, TimeInterpolator interpolator) {
46         if (view == null) {
47             return NO_OP;
48         }
49 
50         // Short-circuit if the view already has this alpha value, but make sure the visibility is
51         // set correctly for the requested alpha.
52         if (Float.compare(view.getAlpha(), alpha) == 0) {
53             AlphaUpdateListener.updateVisibility(view);
54             return NO_OP;
55         }
56 
57         ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.ALPHA, alpha);
58         anim.addListener(new AlphaUpdateListener(view));
59         anim.setInterpolator(interpolator);
60         add(anim);
61         return anim;
62     }
63 
64     @Override
setViewBackgroundColor(View view, int color, TimeInterpolator interpolator)65     public Animator setViewBackgroundColor(View view, int color, TimeInterpolator interpolator) {
66         if (view == null || (view.getBackground() instanceof ColorDrawable
67                 && ((ColorDrawable) view.getBackground()).getColor() == color)) {
68             return NO_OP;
69         }
70         ObjectAnimator anim = ObjectAnimator.ofArgb(view, VIEW_BACKGROUND_COLOR, color);
71         anim.setInterpolator(interpolator);
72         add(anim);
73         return anim;
74     }
75 
76     @Override
setFloat(T target, FloatProperty<T> property, float value, TimeInterpolator interpolator)77     public <T> Animator setFloat(T target, FloatProperty<T> property, float value,
78             TimeInterpolator interpolator) {
79         if (property.get(target) == value) {
80             return NO_OP;
81         }
82         Animator anim = ObjectAnimator.ofFloat(target, property, value);
83         anim.setInterpolator(interpolator);
84         add(anim);
85         return anim;
86     }
87 
88     @Override
setInt(T target, IntProperty<T> property, int value, TimeInterpolator interpolator)89     public <T> Animator setInt(T target, IntProperty<T> property, int value,
90             TimeInterpolator interpolator) {
91         if (property.get(target) == value) {
92             return NO_OP;
93         }
94         Animator anim = ObjectAnimator.ofInt(target, property, value);
95         anim.setInterpolator(interpolator);
96         add(anim);
97         return anim;
98     }
99 
100     @NonNull
101     @Override
setColor(T target, IntProperty<T> property, int value, TimeInterpolator interpolator)102     public <T> Animator setColor(T target, IntProperty<T> property, int value,
103             TimeInterpolator interpolator) {
104         if (property.get(target) == value) {
105             return NO_OP;
106         }
107         Animator anim = ObjectAnimator.ofArgb(target, property, value);
108         anim.setInterpolator(interpolator);
109         add(anim);
110         return anim;
111     }
112 
113     /**
114      * Adds a callback to be run on every frame of the animation
115      */
addOnFrameCallback(Runnable runnable)116     public void addOnFrameCallback(Runnable runnable) {
117         addOnFrameListener(anim -> runnable.run());
118     }
119 
120     /**
121      * Adds a listener to be run on every frame of the animation
122      */
addOnFrameListener(ValueAnimator.AnimatorUpdateListener listener)123     public void addOnFrameListener(ValueAnimator.AnimatorUpdateListener listener) {
124         if (mProgressAnimator == null) {
125             mProgressAnimator = ValueAnimator.ofFloat(0, 1);
126         }
127 
128         mProgressAnimator.addUpdateListener(listener);
129     }
130 
131     @Override
addEndListener(Consumer<Boolean> listener)132     public void addEndListener(Consumer<Boolean> listener) {
133         if (mProgressAnimator == null) {
134             mProgressAnimator = ValueAnimator.ofFloat(0, 1);
135         }
136         mProgressAnimator.addListener(AnimatorListeners.forEndCallback(listener));
137     }
138 
139     /**
140      * @see AnimatorSet#addListener(AnimatorListener)
141      */
addListener(Animator.AnimatorListener listener)142     public void addListener(Animator.AnimatorListener listener) {
143         mAnim.addListener(listener);
144     }
145 
146     @Override
add(Animator a)147     public void add(Animator a) {
148         mAnim.play(a);
149     }
150 
151     /**
152      * Creates and returns the underlying AnimatorSet
153      */
154     @NonNull
buildAnim()155     public AnimatorSet buildAnim() {
156         // Add progress animation to the end, so that frame callback is called after all the other
157         // animation update.
158         if (mProgressAnimator != null) {
159             add(mProgressAnimator);
160             mProgressAnimator = null;
161         }
162         return mAnim;
163     }
164 }
165