michael@0: /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: package org.mozilla.gecko.animation; michael@0: michael@0: import android.graphics.Matrix; michael@0: import android.graphics.RectF; michael@0: import android.os.Build; michael@0: import android.view.View; michael@0: import android.view.ViewGroup; michael@0: import android.view.animation.Animation; michael@0: import android.view.animation.AnimationUtils; michael@0: import android.view.animation.Transformation; michael@0: michael@0: import java.lang.ref.WeakReference; michael@0: import java.util.WeakHashMap; michael@0: michael@0: class AnimatorProxy { michael@0: private static final WeakHashMap PROXIES = michael@0: new WeakHashMap(); michael@0: michael@0: private static interface AnimatorProxyImpl { michael@0: public float getAlpha(); michael@0: public void setAlpha(float alpha); michael@0: michael@0: public float getTranslationX(); michael@0: public void setTranslationX(float translationX); michael@0: michael@0: public float getTranslationY(); michael@0: public void setTranslationY(float translationY); michael@0: michael@0: public View getView(); michael@0: } michael@0: michael@0: private AnimatorProxyImpl mImpl; michael@0: michael@0: private AnimatorProxy(AnimatorProxyImpl impl) { michael@0: mImpl = impl; michael@0: } michael@0: michael@0: public static AnimatorProxy create(View view) { michael@0: AnimatorProxy proxy = PROXIES.get(view); michael@0: boolean needsAnimationProxy = (Build.VERSION.SDK_INT < 11); michael@0: michael@0: // If the view's animation proxy has been overridden from somewhere else, we need to michael@0: // create a new AnimatorProxy for the view. michael@0: if (proxy == null || (needsAnimationProxy && proxy.mImpl != view.getAnimation())) { michael@0: AnimatorProxyImpl impl = (needsAnimationProxy ? new AnimatorProxyPreHC(view) : michael@0: new AnimatorProxyPostHC(view)); michael@0: michael@0: proxy = new AnimatorProxy(impl); michael@0: PROXIES.put(view, proxy); michael@0: } michael@0: michael@0: return proxy; michael@0: } michael@0: michael@0: public int getWidth() { michael@0: View view = mImpl.getView(); michael@0: if (view != null) michael@0: return view.getWidth(); michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: public void setWidth(int width) { michael@0: View view = mImpl.getView(); michael@0: if (view != null) { michael@0: ViewGroup.LayoutParams lp = view.getLayoutParams(); michael@0: lp.width = width; michael@0: view.setLayoutParams(lp); michael@0: } michael@0: } michael@0: michael@0: public int getHeight() { michael@0: View view = mImpl.getView(); michael@0: if (view != null) michael@0: return view.getHeight(); michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: public void setHeight(int height) { michael@0: View view = mImpl.getView(); michael@0: if (view != null) { michael@0: ViewGroup.LayoutParams lp = view.getLayoutParams(); michael@0: lp.height = height; michael@0: view.setLayoutParams(lp); michael@0: } michael@0: } michael@0: michael@0: public int getScrollX() { michael@0: View view = mImpl.getView(); michael@0: if (view != null) michael@0: return view.getScrollX(); michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: public int getScrollY() { michael@0: View view = mImpl.getView(); michael@0: if (view != null) michael@0: return view.getScrollY(); michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: public void scrollTo(int scrollX, int scrollY) { michael@0: View view = mImpl.getView(); michael@0: if (view != null) michael@0: view.scrollTo(scrollX, scrollY); michael@0: } michael@0: michael@0: public float getAlpha() { michael@0: return mImpl.getAlpha(); michael@0: } michael@0: michael@0: public void setAlpha(float alpha) { michael@0: mImpl.setAlpha(alpha); michael@0: } michael@0: michael@0: public float getTranslationX() { michael@0: return mImpl.getTranslationX(); michael@0: } michael@0: michael@0: public void setTranslationX(float translationX) { michael@0: mImpl.setTranslationX(translationX); michael@0: } michael@0: michael@0: public float getTranslationY() { michael@0: return mImpl.getTranslationY(); michael@0: } michael@0: michael@0: public void setTranslationY(float translationY) { michael@0: mImpl.setTranslationY(translationY); michael@0: } michael@0: michael@0: /* michael@0: * AnimatorProxyPreHC uses the technique used by the NineOldAndroids described here: michael@0: * http://jakewharton.com/advanced-pre-honeycomb-animation/ michael@0: * michael@0: * Some of this code is based on Jake Wharton's AnimatorProxy released as part of michael@0: * the NineOldAndroids library under the Apache License 2.0. michael@0: */ michael@0: private static class AnimatorProxyPreHC extends Animation implements AnimatorProxyImpl { michael@0: private WeakReference mViewRef; michael@0: michael@0: private final RectF mBefore; michael@0: private final RectF mAfter; michael@0: private final Matrix mTempMatrix; michael@0: michael@0: private float mAlpha; michael@0: private float mTranslationX; michael@0: private float mTranslationY; michael@0: michael@0: public AnimatorProxyPreHC(View view) { michael@0: mBefore = new RectF(); michael@0: mAfter = new RectF(); michael@0: mTempMatrix = new Matrix(); michael@0: michael@0: mAlpha = 1; michael@0: mTranslationX = 0; michael@0: mTranslationY = 0; michael@0: michael@0: loadCurrentTransformation(view); michael@0: michael@0: setDuration(0); michael@0: setFillAfter(true); michael@0: view.setAnimation(this); michael@0: michael@0: mViewRef = new WeakReference(view); michael@0: } michael@0: michael@0: private void loadCurrentTransformation(View view) { michael@0: Animation animation = view.getAnimation(); michael@0: if (animation == null) michael@0: return; michael@0: michael@0: Transformation transformation = new Transformation(); michael@0: float[] matrix = new float[9]; michael@0: michael@0: animation.getTransformation(AnimationUtils.currentAnimationTimeMillis(), transformation); michael@0: transformation.getMatrix().getValues(matrix); michael@0: michael@0: mAlpha = transformation.getAlpha(); michael@0: mTranslationX = matrix[Matrix.MTRANS_X]; michael@0: mTranslationY = matrix[Matrix.MTRANS_Y]; michael@0: } michael@0: michael@0: private void prepareForUpdate() { michael@0: View view = mViewRef.get(); michael@0: if (view != null) michael@0: computeRect(mBefore, view); michael@0: } michael@0: michael@0: private void computeRect(final RectF r, View view) { michael@0: final float w = view.getWidth(); michael@0: final float h = view.getHeight(); michael@0: michael@0: r.set(0, 0, w, h); michael@0: michael@0: final Matrix m = mTempMatrix; michael@0: m.reset(); michael@0: transformMatrix(m, view); michael@0: mTempMatrix.mapRect(r); michael@0: michael@0: r.offset(view.getLeft(), view.getTop()); michael@0: } michael@0: michael@0: private void transformMatrix(Matrix m, View view) { michael@0: m.postTranslate(mTranslationX, mTranslationY); michael@0: } michael@0: michael@0: private void invalidateAfterUpdate() { michael@0: View view = mViewRef.get(); michael@0: if (view == null || view.getParent() == null) michael@0: return; michael@0: michael@0: final RectF after = mAfter; michael@0: computeRect(after, view); michael@0: after.union(mBefore); michael@0: michael@0: ((View)view.getParent()).invalidate( michael@0: (int) Math.floor(after.left), michael@0: (int) Math.floor(after.top), michael@0: (int) Math.ceil(after.right), michael@0: (int) Math.ceil(after.bottom)); michael@0: } michael@0: michael@0: @Override michael@0: public float getAlpha() { michael@0: return mAlpha; michael@0: } michael@0: michael@0: @Override michael@0: public void setAlpha(float alpha) { michael@0: if (mAlpha == alpha) michael@0: return; michael@0: michael@0: mAlpha = alpha; michael@0: michael@0: View view = mViewRef.get(); michael@0: if (view != null) michael@0: view.invalidate(); michael@0: } michael@0: michael@0: @Override michael@0: public float getTranslationX() { michael@0: return mTranslationX; michael@0: } michael@0: michael@0: @Override michael@0: public void setTranslationX(float translationX) { michael@0: if (mTranslationX == translationX) michael@0: return; michael@0: michael@0: prepareForUpdate(); michael@0: mTranslationX = translationX; michael@0: invalidateAfterUpdate(); michael@0: } michael@0: michael@0: @Override michael@0: public float getTranslationY() { michael@0: return mTranslationY; michael@0: } michael@0: michael@0: @Override michael@0: public void setTranslationY(float translationY) { michael@0: if (mTranslationY == translationY) michael@0: return; michael@0: michael@0: prepareForUpdate(); michael@0: mTranslationY = translationY; michael@0: invalidateAfterUpdate(); michael@0: } michael@0: michael@0: @Override michael@0: public View getView() { michael@0: return mViewRef.get(); michael@0: } michael@0: michael@0: @Override michael@0: protected void applyTransformation(float interpolatedTime, Transformation t) { michael@0: View view = mViewRef.get(); michael@0: if (view != null) { michael@0: t.setAlpha(mAlpha); michael@0: transformMatrix(t.getMatrix(), view); michael@0: } michael@0: } michael@0: } michael@0: michael@0: private static class AnimatorProxyPostHC implements AnimatorProxyImpl { michael@0: private WeakReference mViewRef; michael@0: michael@0: public AnimatorProxyPostHC(View view) { michael@0: mViewRef = new WeakReference(view); michael@0: } michael@0: michael@0: @Override michael@0: public float getAlpha() { michael@0: View view = mViewRef.get(); michael@0: if (view != null) michael@0: return view.getAlpha(); michael@0: michael@0: return 1; michael@0: } michael@0: michael@0: @Override michael@0: public void setAlpha(float alpha) { michael@0: View view = mViewRef.get(); michael@0: if (view != null) michael@0: view.setAlpha(alpha); michael@0: } michael@0: michael@0: @Override michael@0: public float getTranslationX() { michael@0: View view = mViewRef.get(); michael@0: if (view != null) michael@0: return view.getTranslationX(); michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: @Override michael@0: public void setTranslationX(float translationX) { michael@0: View view = mViewRef.get(); michael@0: if (view != null) michael@0: view.setTranslationX(translationX); michael@0: } michael@0: michael@0: @Override michael@0: public float getTranslationY() { michael@0: View view = mViewRef.get(); michael@0: if (view != null) michael@0: return view.getTranslationY(); michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: @Override michael@0: public void setTranslationY(float translationY) { michael@0: View view = mViewRef.get(); michael@0: if (view != null) michael@0: view.setTranslationY(translationY); michael@0: } michael@0: michael@0: @Override michael@0: public View getView() { michael@0: return mViewRef.get(); michael@0: } michael@0: } michael@0: } michael@0: