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 android.view;
18
19import android.animation.TimeInterpolator;
20import android.view.ViewPropertyAnimator.NameValuesHolder;
21import android.view.animation.Interpolator;
22import android.view.animation.LinearInterpolator;
23
24import com.android.internal.view.animation.FallbackLUTInterpolator;
25
26import java.util.ArrayList;
27
28
29/**
30 * This is a RenderThread driven backend for ViewPropertyAnimator.
31 */
32class ViewPropertyAnimatorRT {
33
34    private static final Interpolator sLinearInterpolator = new LinearInterpolator();
35
36    private final View mView;
37
38    private RenderNodeAnimator mAnimators[] = new RenderNodeAnimator[RenderNodeAnimator.LAST_VALUE + 1];
39
40    ViewPropertyAnimatorRT(View view) {
41        mView = view;
42    }
43
44    /**
45     * @return true if ViewPropertyAnimatorRT handled the animation,
46     *         false if ViewPropertyAnimator needs to handle it
47     */
48    public boolean startAnimation(ViewPropertyAnimator parent) {
49        cancelAnimators(parent.mPendingAnimations);
50        if (!canHandleAnimator(parent)) {
51            return false;
52        }
53        doStartAnimation(parent);
54        return true;
55    }
56
57    public void cancelAll() {
58        for (int i = 0; i < mAnimators.length; i++) {
59            if (mAnimators[i] != null) {
60                mAnimators[i].cancel();
61                mAnimators[i] = null;
62            }
63        }
64    }
65
66    private void doStartAnimation(ViewPropertyAnimator parent) {
67        int size = parent.mPendingAnimations.size();
68
69        long startDelay = parent.getStartDelay();
70        long duration = parent.getDuration();
71        TimeInterpolator interpolator = parent.getInterpolator();
72        if (interpolator == null) {
73            // Documented to be LinearInterpolator in ValueAnimator.setInterpolator
74            interpolator = sLinearInterpolator;
75        }
76        if (!RenderNodeAnimator.isNativeInterpolator(interpolator)) {
77            interpolator = new FallbackLUTInterpolator(interpolator, duration);
78        }
79        for (int i = 0; i < size; i++) {
80            NameValuesHolder holder = parent.mPendingAnimations.get(i);
81            int property = RenderNodeAnimator.mapViewPropertyToRenderProperty(holder.mNameConstant);
82
83            final float finalValue = holder.mFromValue + holder.mDeltaValue;
84            RenderNodeAnimator animator = new RenderNodeAnimator(property, finalValue);
85            animator.setStartDelay(startDelay);
86            animator.setDuration(duration);
87            animator.setInterpolator(interpolator);
88            animator.setTarget(mView);
89            animator.start();
90
91            mAnimators[property] = animator;
92        }
93
94        parent.mPendingAnimations.clear();
95    }
96
97    private boolean canHandleAnimator(ViewPropertyAnimator parent) {
98        // TODO: Can we eliminate this entirely?
99        // If RenderNode.animatorProperties() can be toggled to point at staging
100        // instead then RNA can be used as the animators for software as well
101        // as the updateListener fallback paths. If this can be toggled
102        // at the top level somehow, combined with requiresUiRedraw, we could
103        // ensure that RT does not self-animate, allowing for safe driving of
104        // the animators from the UI thread using the same mechanisms
105        // ViewPropertyAnimator does, just with everything sitting on a single
106        // animator subsystem instead of multiple.
107
108        if (parent.getUpdateListener() != null) {
109            return false;
110        }
111        if (parent.getListener() != null) {
112            // TODO support
113            return false;
114        }
115        if (!mView.isHardwareAccelerated()) {
116            // TODO handle this maybe?
117            return false;
118        }
119        if (parent.hasActions()) {
120            return false;
121        }
122        // Here goes nothing...
123        return true;
124    }
125
126    private void cancelAnimators(ArrayList<NameValuesHolder> mPendingAnimations) {
127        int size = mPendingAnimations.size();
128        for (int i = 0; i < size; i++) {
129            NameValuesHolder holder = mPendingAnimations.get(i);
130            int property = RenderNodeAnimator.mapViewPropertyToRenderProperty(holder.mNameConstant);
131            if (mAnimators[property] != null) {
132                mAnimators[property].cancel();
133                mAnimators[property] = null;
134            }
135        }
136    }
137
138}
139