1/*
2 * Copyright (C) 2014 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
17package com.android.deskclock;
18
19import android.animation.ArgbEvaluator;
20import android.animation.ObjectAnimator;
21import android.animation.PropertyValuesHolder;
22import android.animation.ValueAnimator;
23import android.graphics.drawable.Drawable;
24import android.support.v4.graphics.drawable.DrawableCompat;
25import android.util.Property;
26import android.view.View;
27import android.view.animation.Interpolator;
28import android.widget.ImageView;
29
30import java.lang.reflect.InvocationTargetException;
31import java.lang.reflect.Method;
32
33public class AnimatorUtils {
34
35    public static final long ANIM_DURATION_SHORT = 266L;  // 8/30 frames long
36
37    public static final Interpolator DECELERATE_ACCELERATE_INTERPOLATOR = new Interpolator() {
38        @Override
39        public float getInterpolation(float x) {
40            return 0.5f + 4.0f * (x - 0.5f) * (x - 0.5f) * (x - 0.5f);
41        }
42    };
43
44    public static final Property<View, Integer> BACKGROUND_ALPHA =
45            new Property<View, Integer>(Integer.class, "background.alpha") {
46        @Override
47        public Integer get(View view) {
48            return view.getBackground().getAlpha();
49        }
50
51        @Override
52        public void set(View view, Integer value) {
53            view.getBackground().setAlpha(value);
54        }
55    };
56
57    public static final Property<ImageView, Integer> DRAWABLE_ALPHA =
58            new Property<ImageView, Integer>(Integer.class, "drawable.alpha") {
59        @Override
60        public Integer get(ImageView view) {
61            return view.getDrawable().getAlpha();
62        }
63
64        @Override
65        public void set(ImageView view, Integer value) {
66            view.getDrawable().setAlpha(value);
67        }
68    };
69
70    public static final Property<ImageView, Integer> DRAWABLE_TINT =
71            new Property<ImageView, Integer>(Integer.class, "drawable.tint") {
72        @Override
73        public Integer get(ImageView view) {
74            return null;
75        }
76
77        @Override
78        public void set(ImageView view, Integer value) {
79            // Ensure the drawable is wrapped using DrawableCompat.
80            final Drawable drawable = view.getDrawable();
81            final Drawable wrappedDrawable = DrawableCompat.wrap(drawable);
82            if (wrappedDrawable != drawable) {
83                view.setImageDrawable(wrappedDrawable);
84            }
85            // Set the new tint value via DrawableCompat.
86            DrawableCompat.setTint(wrappedDrawable, value);
87        }
88    };
89
90    public static final ArgbEvaluator ARGB_EVALUATOR = new ArgbEvaluator();
91
92    private static Method sAnimateValue;
93    private static boolean sTryAnimateValue = true;
94
95    public static void setAnimatedFraction(ValueAnimator animator, float fraction) {
96        if (Utils.isLMR1OrLater()) {
97            animator.setCurrentFraction(fraction);
98            return;
99        }
100
101        if (sTryAnimateValue) {
102            // try to set the animated fraction directly so that it isn't affected by the
103            // internal animator scale or time (b/17938711)
104            try {
105                if (sAnimateValue == null) {
106                    sAnimateValue = ValueAnimator.class
107                            .getDeclaredMethod("animateValue", float.class);
108                    sAnimateValue.setAccessible(true);
109                }
110
111                sAnimateValue.invoke(animator, fraction);
112                return;
113            } catch (NoSuchMethodException | InvocationTargetException
114                    | IllegalAccessException e) {
115                // something went wrong, don't try that again
116                LogUtils.e("Unable to use animateValue directly", e);
117                sTryAnimateValue = false;
118            }
119        }
120
121        // if that doesn't work then just fall back to setting the current play time
122        animator.setCurrentPlayTime(Math.round(fraction * animator.getDuration()));
123    }
124
125    public static void reverse(ValueAnimator... animators) {
126        for (ValueAnimator animator : animators) {
127            final float fraction = animator.getAnimatedFraction();
128            if (fraction > 0.0f) {
129                animator.reverse();
130                setAnimatedFraction(animator, 1.0f - fraction);
131            }
132        }
133    }
134
135    public static void cancel(ValueAnimator... animators) {
136        for (ValueAnimator animator : animators) {
137            animator.cancel();
138        }
139    }
140
141    public static ValueAnimator getScaleAnimator(View view, float... values) {
142        return ObjectAnimator.ofPropertyValuesHolder(view,
143                PropertyValuesHolder.ofFloat(View.SCALE_X, values),
144                PropertyValuesHolder.ofFloat(View.SCALE_Y, values));
145    }
146}
147