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