1 // Copyright 2013 Google Inc. All Rights Reserved.
2 
3 package com.android.deskclock.widget.sgv;
4 
5 import android.R.interpolator;
6 import android.animation.Animator;
7 import android.animation.Animator.AnimatorListener;
8 import android.animation.AnimatorListenerAdapter;
9 import android.animation.ObjectAnimator;
10 import android.content.Context;
11 import android.graphics.Point;
12 import android.view.View;
13 import android.view.WindowManager;
14 import android.view.animation.AnimationUtils;
15 import android.view.animation.Interpolator;
16 
17 
18 import java.util.List;
19 
20 public class SgvAnimationHelper {
21 
22     /**
23      * Supported entrance animations for views in the {@link StaggeredGridView}.
24      */
25     public enum AnimationIn {
26         NONE,
27         // Fly in all views from the bottom of the screen
28         FLY_UP_ALL_VIEWS,
29         // New views expand into view from height 0.  Existing views are updated and translated
30         // to their new positions if appropriate.
31         EXPAND_NEW_VIEWS,
32         // New views expand into view from height 0.  Existing views are updated and translated
33         // to their new positions if appropriate.  This animation is done for all views
34         // simultaneously without a cascade effect.
35         EXPAND_NEW_VIEWS_NO_CASCADE,
36         // New views are flown in from the bottom.  Existing views are updated and translated
37         // to their new positions if appropriate.
38         FLY_IN_NEW_VIEWS,
39         // New views are slid in from the side.  Existing views are updated and translated
40         // to their new positions if appropriate.
41         SLIDE_IN_NEW_VIEWS,
42         // Fade in all new views
43         FADE,
44     }
45 
46     /**
47      * Supported exit animations for views in the {@link StaggeredGridView}.
48      */
49     public enum AnimationOut {
50         NONE,
51         // Stale views are faded out of view.  Existing views are then updated and translated
52         // to their new positions if appropriate.
53         FADE,
54         // Stale views are dropped to the bottom of the screen.  Existing views are then updated
55         // and translated to their new positions if appropriate.
56         FLY_DOWN,
57         // Stale views are slid to the side of the screen.  Existing views are then updated
58         // and translated to their new positions if appropriate.
59         SLIDE,
60         // Stale views are collapsed to height 0.  Existing views are then updated
61         // and translated to their new positions if appropriate.
62         COLLAPSE
63     }
64 
65     private static Interpolator sDecelerateQuintInterpolator;
66 
67     private static final int ANIMATION_LONG_SCREEN_SIZE = 1600;
68     private static final int ANIMATION_MED_SCREEN_SIZE = 1200;
69 
70     // Duration of an individual animation when the children of the grid are laid out again. These
71     // are measured in milliseconds and based on the height of the screen.
72     private static final int ANIMATION_SHORT_DURATION = 400;
73     private static final int ANIMATION_MED_DURATION = 450;
74     private static final int ANIMATION_LONG_DURATION = 500;
75 
76     public static final float ANIMATION_ROTATION_DEGREES = 25.0f;
77 
78     /**
79      * Duration of an individual animation when the children of the grid are laid out again.
80      * This is measured in milliseconds.
81      */
82     private static int sAnimationDuration = ANIMATION_MED_DURATION;
83 
initialize(Context context)84     public static void initialize(Context context) {
85         sDecelerateQuintInterpolator = AnimationUtils.loadInterpolator(context,
86                 interpolator.decelerate_quint);
87 
88         final Point size = new Point();
89         ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).
90                 getDefaultDisplay().getSize(size);
91         final int screenHeight = size.y;
92 
93         if (screenHeight >= ANIMATION_LONG_SCREEN_SIZE) {
94             sAnimationDuration = ANIMATION_LONG_DURATION;
95         } else if (screenHeight >= ANIMATION_MED_SCREEN_SIZE) {
96             sAnimationDuration = ANIMATION_MED_DURATION;
97         } else {
98             sAnimationDuration = ANIMATION_SHORT_DURATION;
99         }
100     }
101 
getDefaultAnimationDuration()102     public static int getDefaultAnimationDuration() {
103         return sAnimationDuration;
104     }
105 
getDefaultAnimationInterpolator()106     public static Interpolator getDefaultAnimationInterpolator() {
107         return sDecelerateQuintInterpolator;
108     }
109 
110     /**
111      * Add animations to translate a view's X-translation.  {@link AnimatorListener} can be null.
112      */
addXTranslationAnimators(List<Animator> animators, final View view, int startTranslation, final int endTranslation, int animationDelay, AnimatorListener listener)113     private static void addXTranslationAnimators(List<Animator> animators,
114             final View view, int startTranslation, final int endTranslation, int animationDelay,
115             AnimatorListener listener) {
116         // We used to skip the animation if startTranslation == endTranslation,
117         // but to add a recycle view listener, we need at least one animation
118         view.setTranslationX(startTranslation);
119         final ObjectAnimator translateAnimatorX = ObjectAnimator.ofFloat(view,
120                 View.TRANSLATION_X, startTranslation, endTranslation);
121         translateAnimatorX.setInterpolator(sDecelerateQuintInterpolator);
122         translateAnimatorX.setDuration(sAnimationDuration);
123         translateAnimatorX.setStartDelay(animationDelay);
124         if (listener != null) {
125             translateAnimatorX.addListener(listener);
126         }
127 
128         animators.add(translateAnimatorX);
129     }
130 
131     /**
132      * Add animations to translate a view's Y-translation.  {@link AnimatorListener} can be null.
133      */
addYTranslationAnimators(List<Animator> animators, final View view, int startTranslation, final int endTranslation, int animationDelay, AnimatorListener listener)134     private static void addYTranslationAnimators(List<Animator> animators,
135             final View view, int startTranslation, final int endTranslation, int animationDelay,
136             AnimatorListener listener) {
137         // We used to skip the animation if startTranslation == endTranslation,
138         // but to add a recycle view listener, we need at least one animation
139         view.setTranslationY(startTranslation);
140         final ObjectAnimator translateAnimatorY = ObjectAnimator.ofFloat(view,
141                 View.TRANSLATION_Y, startTranslation, endTranslation);
142         translateAnimatorY.setInterpolator(sDecelerateQuintInterpolator);
143         translateAnimatorY.setDuration(sAnimationDuration);
144         translateAnimatorY.setStartDelay(animationDelay);
145 
146         if (listener != null) {
147             translateAnimatorY.addListener(listener);
148         }
149 
150         animators.add(translateAnimatorY);
151     }
152 
153     /**
154      * Translate a view to the specified translation values, and animate the translations to 0.
155      */
addXYTranslationAnimators(List<Animator> animators, final View view, int xTranslation, int yTranslation, int animationDelay)156     public static void addXYTranslationAnimators(List<Animator> animators, final View view,
157             int xTranslation, int yTranslation, int animationDelay) {
158         addXTranslationAnimators(animators, view, xTranslation, 0, animationDelay, null);
159         addYTranslationAnimators(animators, view, yTranslation, 0, animationDelay, null);
160     }
161 
162     /**
163      * Translate a view to the specified translation values, while rotating to the specified
164      * rotation value.
165      */
addTranslationRotationAnimators(List<Animator> animators, final View view, int xTranslation, int yTranslation, float rotation, int animationDelay)166     public static void addTranslationRotationAnimators(List<Animator> animators, final View view,
167             int xTranslation, int yTranslation, float rotation, int animationDelay) {
168         addXYTranslationAnimators(animators, view, xTranslation, yTranslation, animationDelay);
169 
170         view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
171         view.setRotation(rotation);
172 
173         final ObjectAnimator rotateAnimatorY = ObjectAnimator.ofFloat(view,
174                 View.ROTATION, view.getRotation(), 0.0f);
175         rotateAnimatorY.setInterpolator(sDecelerateQuintInterpolator);
176         rotateAnimatorY.setDuration(sAnimationDuration);
177         rotateAnimatorY.setStartDelay(animationDelay);
178         rotateAnimatorY.addListener(new AnimatorListenerAdapter() {
179             private boolean mIsCanceled = false;
180 
181             @Override
182             public void onAnimationCancel(Animator animation) {
183                 mIsCanceled = true;
184             }
185 
186             @Override
187             public void onAnimationEnd(Animator animation) {
188                 if (!mIsCanceled) {
189                     view.setRotation(0);
190                 }
191 
192                 view.setLayerType(View.LAYER_TYPE_NONE, null);
193             }
194         });
195 
196         animators.add(rotateAnimatorY);
197     }
198 
199     /**
200      * Expand a view into view by scaling up vertically from 0.
201      */
addExpandInAnimators(List<Animator> animators, final View view, int animationDelay)202     public static void addExpandInAnimators(List<Animator> animators, final View view,
203             int animationDelay) {
204         view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
205         view.setScaleY(0);
206 
207         final ObjectAnimator scaleAnimatorY = ObjectAnimator.ofFloat(view,
208                 View.SCALE_Y, view.getScaleY(), 1.0f);
209         scaleAnimatorY.setInterpolator(sDecelerateQuintInterpolator);
210         scaleAnimatorY.setDuration(sAnimationDuration);
211         scaleAnimatorY.setStartDelay(animationDelay);
212         scaleAnimatorY.addListener(new AnimatorListenerAdapter() {
213             @Override
214             public void onAnimationEnd(Animator animation) {
215                 view.setScaleY(1.0f);
216                 view.setLayerType(View.LAYER_TYPE_NONE, null);
217             }
218         });
219 
220         animators.add(scaleAnimatorY);
221     }
222 
223     /**
224      * Collapse a view out by scaling it from its current scaled value to 0.
225      */
addCollapseOutAnimators(List<Animator> animators, final View view, int animationDelay)226     public static void addCollapseOutAnimators(List<Animator> animators, final View view,
227             int animationDelay) {
228         view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
229 
230         final ObjectAnimator scaleAnimatorY = ObjectAnimator.ofFloat(view, View.SCALE_Y,
231                 view.getScaleY(), 0);
232         scaleAnimatorY.setInterpolator(sDecelerateQuintInterpolator);
233         scaleAnimatorY.setDuration(sAnimationDuration);
234         scaleAnimatorY.setStartDelay(animationDelay);
235         scaleAnimatorY.addListener(new AnimatorListenerAdapter() {
236             @Override
237             public void onAnimationEnd(Animator animation) {
238                 view.setScaleY(0);
239                 view.setLayerType(View.LAYER_TYPE_NONE, null);
240             }
241         });
242 
243         animators.add(scaleAnimatorY);
244     }
245 
246     /**
247      * Collapse a view out by scaling it from its current scaled value to 0.
248      * The animators are expected to run immediately without a start delay.
249      */
addCollapseOutAnimators(List<Animator> animators, final View view)250     public static void addCollapseOutAnimators(List<Animator> animators, final View view) {
251         addCollapseOutAnimators(animators, view, 0 /* animation delay */);
252     }
253 
254 
255 
256     /**
257      * Fly a view out by moving it vertically off the bottom of the screen.
258      */
addFlyOutAnimators(List<Animator> animators, final View view, int startTranslation, int endTranslation, int animationDelay)259     public static void addFlyOutAnimators(List<Animator> animators,
260             final View view, int startTranslation, int endTranslation, int animationDelay) {
261         addYTranslationAnimators(animators, view, startTranslation, endTranslation,
262                 animationDelay, null);
263     }
264 
addFlyOutAnimators(List<Animator> animators, final View view, int startTranslation, int endTranslation)265     public static void addFlyOutAnimators(List<Animator> animators, final View view,
266             int startTranslation, int endTranslation) {
267         addFlyOutAnimators(animators, view, startTranslation,
268                 endTranslation, 0 /* animation delay */);
269     }
270 
addSlideInFromRightAnimators(List<Animator> animators, final View view, int startTranslation, int animationDelay)271     public static void addSlideInFromRightAnimators(List<Animator> animators, final View view,
272             int startTranslation, int animationDelay) {
273         addXTranslationAnimators(animators, view, startTranslation, 0, animationDelay, null);
274         addFadeAnimators(animators, view, 0, 1.0f, animationDelay);
275     }
276 
277     /**
278      * Slide a view out of view from the start to end position, fading the view out as it
279      * approaches the end position.
280      */
addSlideOutAnimators(List<Animator> animators, final View view, int startTranslation, int endTranslation, int animationDelay)281     public static void addSlideOutAnimators(List<Animator> animators, final View view,
282             int startTranslation, int endTranslation, int animationDelay) {
283         addFadeAnimators(animators, view, view.getAlpha(), 0, animationDelay);
284         addXTranslationAnimators(animators, view, startTranslation, endTranslation,
285                 animationDelay, null);
286     }
287 
288     /**
289      * Slide a view out of view from the start to end position, fading the view out as it
290      * approaches the end position.  The animators are expected to run immediately without a
291      * start delay.
292      */
addSlideOutAnimators(List<Animator> animators, final View view, int startTranslation, int endTranslation)293     public static void addSlideOutAnimators(List<Animator> animators, final View view,
294             int startTranslation, int endTranslation) {
295         addSlideOutAnimators(animators, view, startTranslation,
296                 endTranslation, 0 /* animation delay */);
297     }
298 
299     /**
300      * Add animations to fade a view from the specified start alpha value to end value.
301      */
addFadeAnimators(List<Animator> animators, final View view, float startAlpha, final float endAlpha, int animationDelay)302     public static void addFadeAnimators(List<Animator> animators, final View view,
303             float startAlpha, final float endAlpha, int animationDelay) {
304         if (startAlpha == endAlpha) {
305             return;
306         }
307 
308         view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
309         view.setAlpha(startAlpha);
310 
311         final ObjectAnimator fadeAnimator = ObjectAnimator.ofFloat(view, View.ALPHA,
312                 view.getAlpha(), endAlpha);
313         fadeAnimator.setInterpolator(sDecelerateQuintInterpolator);
314         fadeAnimator.setDuration(sAnimationDuration);
315         fadeAnimator.setStartDelay(animationDelay);
316         fadeAnimator.addListener(new AnimatorListenerAdapter() {
317             @Override
318             public void onAnimationEnd(Animator animation) {
319                 view.setAlpha(endAlpha);
320                 view.setLayerType(View.LAYER_TYPE_NONE, null);
321             }
322         });
323         animators.add(fadeAnimator);
324     }
325 
326     /**
327      * Add animations to fade a view from the specified start alpha value to end value.
328      * The animators are expected to run immediately without a start delay.
329      */
addFadeAnimators(List<Animator> animators, final View view, float startAlpha, final float endAlpha)330     public static void addFadeAnimators(List<Animator> animators, final View view,
331             float startAlpha, final float endAlpha) {
332         addFadeAnimators(animators, view, startAlpha, endAlpha, 0 /* animation delay */);
333     }
334 }
335