/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.glview.view; import java.util.ArrayList; import java.util.List; import android.util.SparseArray; import android.util.SparseIntArray; import com.glview.animation.Animator; import com.glview.animation.ObjectAnimator; import com.glview.animation.TimeInterpolator; import com.glview.view.ViewPropertyAnimator.NameValuesHolder; import com.glview.view.animation.Interpolator; import com.glview.view.animation.LinearInterpolator; /** * This is a RenderThread driven backend for ViewPropertyAnimator. */ class ViewPropertyAnimatorRT { // Keep in sync with enum RenderProperty in Animator.h public static final int TRANSLATION_X = 0; public static final int TRANSLATION_Y = 1; public static final int TRANSLATION_Z = 2; public static final int SCALE_X = 3; public static final int SCALE_Y = 4; public static final int ROTATION = 5; public static final int ROTATION_X = 6; public static final int ROTATION_Y = 7; public static final int X = 8; public static final int Y = 9; public static final int Z = 10; public static final int ALPHA = 11; // The last value in the enum, used for array size initialization public static final int LAST_VALUE = ALPHA; // ViewPropertyAnimator uses a mask for its values, we need to remap them // to the enum values here. RenderPropertyAnimator can't use the mask values // directly as internally it uses a lookup table so it needs the values to // be sequential starting from 0 private static final SparseIntArray sViewPropertyAnimatorMap = new SparseIntArray(15) {{ put(ViewPropertyAnimator.TRANSLATION_X, TRANSLATION_X); put(ViewPropertyAnimator.TRANSLATION_Y, TRANSLATION_Y); put(ViewPropertyAnimator.TRANSLATION_Z, TRANSLATION_Z); put(ViewPropertyAnimator.SCALE_X, SCALE_X); put(ViewPropertyAnimator.SCALE_Y, SCALE_Y); put(ViewPropertyAnimator.ROTATION, ROTATION); put(ViewPropertyAnimator.ROTATION_X, ROTATION_X); put(ViewPropertyAnimator.ROTATION_Y, ROTATION_Y); put(ViewPropertyAnimator.X, X); put(ViewPropertyAnimator.Y, Y); put(ViewPropertyAnimator.Z, Z); put(ViewPropertyAnimator.ALPHA, ALPHA); }}; private static final SparseArray<String> sViewPropertyNameAnimatorMap = new SparseArray<String>() {{ put(ViewPropertyAnimator.TRANSLATION_X, "translationX"); put(ViewPropertyAnimator.TRANSLATION_Y, "translationY"); put(ViewPropertyAnimator.TRANSLATION_Z, "translationZ"); put(ViewPropertyAnimator.SCALE_X, "scaleX"); put(ViewPropertyAnimator.SCALE_Y, "scaleY"); put(ViewPropertyAnimator.ROTATION, "rotation"); put(ViewPropertyAnimator.ROTATION_X, "rotationX"); put(ViewPropertyAnimator.ROTATION_Y, "rotationY"); put(ViewPropertyAnimator.X, "x"); put(ViewPropertyAnimator.Y, "y"); put(ViewPropertyAnimator.Z, "z"); put(ViewPropertyAnimator.ALPHA, "alpha"); }}; private static final Interpolator sLinearInterpolator = new LinearInterpolator(); private final View mView; private Animator mAnimators[] = new Animator[LAST_VALUE + 1]; ViewPropertyAnimatorRT(View view) { mView = view; } /** * @return true if ViewPropertyAnimatorRT handled the animation, * false if ViewPropertyAnimator needs to handle it */ public boolean startAnimation(ViewPropertyAnimator parent) { cancelAnimators(parent.mPendingAnimations); if (!canHandleAnimator(parent)) { return false; } doStartAnimation(parent); return true; } public void cancelAll() { if (!mView.isAttachedToWindow()) { return; } List<Animator> animators = new ArrayList<Animator>(); for (int i = 0; i < mAnimators.length; i++) { if (mAnimators[i] != null && mAnimators[i].isStarted()) { animators.add(mAnimators[i]); } mAnimators[i] = null; } if (animators.size() > 0) { mView.getViewRootImpl().stopRTAnimation(animators); } } private void doStartAnimation(final ViewPropertyAnimator parent) { int size = parent.mPendingAnimations.size(); long startDelay = parent.getStartDelay(); long duration = parent.getDuration(); TimeInterpolator interpolator = parent.getInterpolator(); if (interpolator == null) { // Documented to be LinearInterpolator in ValueAnimator.setInterpolator interpolator = sLinearInterpolator; } List<Animator> animators = new ArrayList<Animator>(); for (int i = 0; i < size; i++) { NameValuesHolder holder = parent.mPendingAnimations.get(i); int property = sViewPropertyAnimatorMap.get(holder.mNameConstant); final float finalValue = holder.mFromValue + holder.mDeltaValue; Animator animator = ObjectAnimator.ofFloat(mView.mRenderNode, sViewPropertyNameAnimatorMap.get(holder.mNameConstant), finalValue); animator.setStartDelay(startDelay); animator.setDuration(duration); animator.setInterpolator(interpolator); mAnimators[property] = animator; animators.add(animator); } if (animators.size() > 0) { mView.getViewRootImpl().startRTAnimation(animators); } parent.mPendingAnimations.clear(); } private boolean canHandleAnimator(ViewPropertyAnimator parent) { // TODO: Can we eliminate this entirely? // If RenderNode.animatorProperties() can be toggled to point at staging // instead then RNA can be used as the animators for software as well // as the updateListener fallback paths. If this can be toggled // at the top level somehow, combined with requiresUiRedraw, we could // ensure that RT does not self-animate, allowing for safe driving of // the animators from the UI thread using the same mechanisms // ViewPropertyAnimator does, just with everything sitting on a single // animator subsystem instead of multiple. if (parent.getUpdateListener() != null) { return false; } if (parent.getListener() != null) { // TODO support return false; } /*if (!mView.isHardwareAccelerated()) { // TODO handle this maybe? return false; }*/ if (parent.hasActions()) { return false; } if (!mView.isAttachedToWindow()) { return false; } // Here goes nothing... return true; } private void cancelAnimators(final ArrayList<NameValuesHolder> mPendingAnimations) { if (!mView.isAttachedToWindow()) { return; } final int size = mPendingAnimations.size(); List<Animator> animators = new ArrayList<Animator>(); for (int i = 0; i < size; i++) { NameValuesHolder holder = mPendingAnimations.get(i); int property = sViewPropertyAnimatorMap.get(holder.mNameConstant); if (mAnimators[property] != null && mAnimators[property].isStarted()) { animators.add(mAnimators[property]); } mAnimators[property] = null; } if (animators.size() > 0) { mView.getViewRootImpl().stopRTAnimation(animators); } } }