package com.nineoldandroids.view.animation;
import android.graphics.Camera;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.os.Build;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import java.lang.ref.WeakReference;
import java.util.WeakHashMap;
/**
* A proxy class to allow for modifying post-3.0 view properties on all pre-3.0
* platforms. <strong>DO NOT</strong> wrap your views with this class if you
* are using {@code ObjectAnimator} as it will handle that itself.
*/
public final class AnimatorProxy extends Animation {
/** Whether or not the current running platform needs to be proxied. */
public static final boolean NEEDS_PROXY = Integer.valueOf(Build.VERSION.SDK).intValue() < Build.VERSION_CODES.HONEYCOMB;
private static final WeakHashMap<View, AnimatorProxy> PROXIES =
new WeakHashMap<View, AnimatorProxy>();
/**
* Create a proxy to allow for modifying post-3.0 view properties on all
* pre-3.0 platforms. <strong>DO NOT</strong> wrap your views if you are
* using {@code ObjectAnimator} as it will handle that itself.
*
* @param view View to wrap.
* @return Proxy to post-3.0 properties.
*/
public static AnimatorProxy wrap(View view) {
AnimatorProxy proxy = PROXIES.get(view);
// This checks if the proxy already exists and whether it still is the animation of the given view
if (proxy == null || proxy != view.getAnimation()) {
proxy = new AnimatorProxy(view);
PROXIES.put(view, proxy);
}
return proxy;
}
private final WeakReference<View> mView;
private final Camera mCamera = new Camera();
private boolean mHasPivot;
private float mAlpha = 1;
private float mPivotX;
private float mPivotY;
private float mRotationX;
private float mRotationY;
private float mRotationZ;
private float mScaleX = 1;
private float mScaleY = 1;
private float mTranslationX;
private float mTranslationY;
private final RectF mBefore = new RectF();
private final RectF mAfter = new RectF();
private final Matrix mTempMatrix = new Matrix();
private AnimatorProxy(View view) {
setDuration(0); //perform transformation immediately
setFillAfter(true); //persist transformation beyond duration
view.setAnimation(this);
mView = new WeakReference<View>(view);
}
public float getAlpha() {
return mAlpha;
}
public void setAlpha(float alpha) {
if (mAlpha != alpha) {
mAlpha = alpha;
View view = mView.get();
if (view != null) {
view.invalidate();
}
}
}
public float getPivotX() {
return mPivotX;
}
public void setPivotX(float pivotX) {
if (!mHasPivot || mPivotX != pivotX) {
prepareForUpdate();
mHasPivot = true;
mPivotX = pivotX;
invalidateAfterUpdate();
}
}
public float getPivotY() {
return mPivotY;
}
public void setPivotY(float pivotY) {
if (!mHasPivot || mPivotY != pivotY) {
prepareForUpdate();
mHasPivot = true;
mPivotY = pivotY;
invalidateAfterUpdate();
}
}
public float getRotation() {
return mRotationZ;
}
public void setRotation(float rotation) {
if (mRotationZ != rotation) {
prepareForUpdate();
mRotationZ = rotation;
invalidateAfterUpdate();
}
}
public float getRotationX() {
return mRotationX;
}
public void setRotationX(float rotationX) {
if (mRotationX != rotationX) {
prepareForUpdate();
mRotationX = rotationX;
invalidateAfterUpdate();
}
}
public float getRotationY() {
return mRotationY;
}
public void setRotationY(float rotationY) {
if (mRotationY != rotationY) {
prepareForUpdate();
mRotationY = rotationY;
invalidateAfterUpdate();
}
}
public float getScaleX() {
return mScaleX;
}
public void setScaleX(float scaleX) {
if (mScaleX != scaleX) {
prepareForUpdate();
mScaleX = scaleX;
invalidateAfterUpdate();
}
}
public float getScaleY() {
return mScaleY;
}
public void setScaleY(float scaleY) {
if (mScaleY != scaleY) {
prepareForUpdate();
mScaleY = scaleY;
invalidateAfterUpdate();
}
}
public int getScrollX() {
View view = mView.get();
if (view == null) {
return 0;
}
return view.getScrollX();
}
public void setScrollX(int value) {
View view = mView.get();
if (view != null) {
view.scrollTo(value, view.getScrollY());
}
}
public int getScrollY() {
View view = mView.get();
if (view == null) {
return 0;
}
return view.getScrollY();
}
public void setScrollY(int value) {
View view = mView.get();
if (view != null) {
view.scrollTo(view.getScrollX(), value);
}
}
public float getTranslationX() {
return mTranslationX;
}
public void setTranslationX(float translationX) {
if (mTranslationX != translationX) {
prepareForUpdate();
mTranslationX = translationX;
invalidateAfterUpdate();
}
}
public float getTranslationY() {
return mTranslationY;
}
public void setTranslationY(float translationY) {
if (mTranslationY != translationY) {
prepareForUpdate();
mTranslationY = translationY;
invalidateAfterUpdate();
}
}
public float getX() {
View view = mView.get();
if (view == null) {
return 0;
}
return view.getLeft() + mTranslationX;
}
public void setX(float x) {
View view = mView.get();
if (view != null) {
setTranslationX(x - view.getLeft());
}
}
public float getY() {
View view = mView.get();
if (view == null) {
return 0;
}
return view.getTop() + mTranslationY;
}
public void setY(float y) {
View view = mView.get();
if (view != null) {
setTranslationY(y - view.getTop());
}
}
private void prepareForUpdate() {
View view = mView.get();
if (view != null) {
computeRect(mBefore, view);
}
}
private void invalidateAfterUpdate() {
View view = mView.get();
if (view == null || view.getParent() == null) {
return;
}
final RectF after = mAfter;
computeRect(after, view);
after.union(mBefore);
((View)view.getParent()).invalidate(
(int) Math.floor(after.left),
(int) Math.floor(after.top),
(int) Math.ceil(after.right),
(int) Math.ceil(after.bottom));
}
private void computeRect(final RectF r, View view) {
// compute current rectangle according to matrix transformation
final float w = view.getWidth();
final float h = view.getHeight();
// use a rectangle at 0,0 to make sure we don't run into issues with scaling
r.set(0, 0, w, h);
final Matrix m = mTempMatrix;
m.reset();
transformMatrix(m, view);
mTempMatrix.mapRect(r);
r.offset(view.getLeft(), view.getTop());
// Straighten coords if rotations flipped them
if (r.right < r.left) {
final float f = r.right;
r.right = r.left;
r.left = f;
}
if (r.bottom < r.top) {
final float f = r.top;
r.top = r.bottom;
r.bottom = f;
}
}
private void transformMatrix(Matrix m, View view) {
final float w = view.getWidth();
final float h = view.getHeight();
final boolean hasPivot = mHasPivot;
final float pX = hasPivot ? mPivotX : w / 2f;
final float pY = hasPivot ? mPivotY : h / 2f;
final float rX = mRotationX;
final float rY = mRotationY;
final float rZ = mRotationZ;
if ((rX != 0) || (rY != 0) || (rZ != 0)) {
final Camera camera = mCamera;
camera.save();
camera.rotateX(rX);
camera.rotateY(rY);
camera.rotateZ(-rZ);
camera.getMatrix(m);
camera.restore();
m.preTranslate(-pX, -pY);
m.postTranslate(pX, pY);
}
final float sX = mScaleX;
final float sY = mScaleY;
if ((sX != 1.0f) || (sY != 1.0f)) {
m.postScale(sX, sY);
final float sPX = -(pX / w) * ((sX * w) - w);
final float sPY = -(pY / h) * ((sY * h) - h);
m.postTranslate(sPX, sPY);
}
m.postTranslate(mTranslationX, mTranslationY);
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
View view = mView.get();
if (view != null) {
t.setAlpha(mAlpha);
transformMatrix(t.getMatrix(), view);
}
}
}