Merge "Don't use overlay to transition ImageViews."
diff --git a/api/current.txt b/api/current.txt
index 09d8940..117d5fe 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -30541,6 +30541,12 @@
     method public void captureStartValues(android.transition.TransitionValues);
   }
 
+  public class ChangeImageTransform extends android.transition.Transition {
+    ctor public ChangeImageTransform();
+    method public void captureEndValues(android.transition.TransitionValues);
+    method public void captureStartValues(android.transition.TransitionValues);
+  }
+
   public class ChangeTransform extends android.transition.Transition {
     ctor public ChangeTransform();
     method public void captureEndValues(android.transition.TransitionValues);
@@ -30564,12 +30570,6 @@
     field public static final int OUT = 2; // 0x2
   }
 
-  public class MoveImage extends android.transition.Transition {
-    ctor public MoveImage();
-    method public void captureEndValues(android.transition.TransitionValues);
-    method public void captureStartValues(android.transition.TransitionValues);
-  }
-
   public final class Scene {
     ctor public Scene(android.view.ViewGroup);
     ctor public Scene(android.view.ViewGroup, android.view.View);
diff --git a/core/java/android/transition/ChangeImageTransform.java b/core/java/android/transition/ChangeImageTransform.java
new file mode 100644
index 0000000..b003690
--- /dev/null
+++ b/core/java/android/transition/ChangeImageTransform.java
@@ -0,0 +1,234 @@
+/*
+ * 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 android.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.TypeEvaluator;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.Property;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import java.util.Map;
+
+/**
+ * Transitions changes in ImageView {@link ImageView#setScaleType(ImageView.ScaleType)} as
+ * well as image scaling due to ImageView size changes. When combined with
+ * {@link android.transition.ChangeBounds}, an ImageView that changes size will
+ * scale smoothly.
+ */
+public class ChangeImageTransform extends Transition {
+
+    private static final String TAG = "ChangeScaleType";
+
+    private static final String PROPNAME_MATRIX = "android:changeScaleType:matrix";
+    private static final String PROPNAME_BOUNDS = "android:changeScaleType:bounds";
+
+    private static final String[] sTransitionProperties = {
+            PROPNAME_MATRIX,
+            PROPNAME_BOUNDS,
+    };
+
+    private static TypeEvaluator<Matrix> NULL_MATRIX_EVALUATOR = new TypeEvaluator<Matrix>() {
+        @Override
+        public Matrix evaluate(float fraction, Matrix startValue, Matrix endValue) {
+            return null;
+        }
+    };
+
+    private static Property<ImageView, Matrix> ANIMATED_TRANSFORM_PROPERTY
+            = new Property<ImageView, Matrix>(Matrix.class, "animatedTransform") {
+        @Override
+        public void set(ImageView object, Matrix value) {
+            object.animateTransform(value);
+        }
+
+        @Override
+        public Matrix get(ImageView object) {
+            return null;
+        }
+    };
+
+    private void captureValues(TransitionValues transitionValues) {
+        View view = transitionValues.view;
+        if (!(view instanceof ImageView) || view.getVisibility() != View.VISIBLE) {
+            return;
+        }
+        ImageView imageView = (ImageView) view;
+        Drawable drawable = imageView.getDrawable();
+        if (drawable == null) {
+            return;
+        }
+        Map<String, Object> values = transitionValues.values;
+
+        int left = view.getLeft();
+        int top = view.getTop();
+        int right = view.getRight();
+        int bottom = view.getBottom();
+
+        Rect bounds = new Rect(left, top, right, bottom);
+        values.put(PROPNAME_BOUNDS, bounds);
+        Matrix matrix;
+        ImageView.ScaleType scaleType = imageView.getScaleType();
+        if (scaleType == ImageView.ScaleType.FIT_XY) {
+            matrix = imageView.getImageMatrix();
+            if (!matrix.isIdentity()) {
+                matrix = new Matrix(matrix);
+            } else {
+                int drawableWidth = drawable.getIntrinsicWidth();
+                int drawableHeight = drawable.getIntrinsicHeight();
+                if (drawableWidth > 0 && drawableHeight > 0) {
+                    float scaleX = ((float) bounds.width()) / drawableWidth;
+                    float scaleY = ((float) bounds.height()) / drawableHeight;
+                    matrix = new Matrix();
+                    matrix.setScale(scaleX, scaleY);
+                } else {
+                    matrix = null;
+                }
+            }
+        } else {
+            matrix = new Matrix(imageView.getImageMatrix());
+        }
+        values.put(PROPNAME_MATRIX, matrix);
+    }
+
+    @Override
+    public void captureStartValues(TransitionValues transitionValues) {
+        captureValues(transitionValues);
+    }
+
+    @Override
+    public void captureEndValues(TransitionValues transitionValues) {
+        captureValues(transitionValues);
+    }
+
+    @Override
+    public String[] getTransitionProperties() {
+        return sTransitionProperties;
+    }
+
+    /**
+     * Creates an Animator for ImageViews moving, changing dimensions, and/or changing
+     * {@link android.widget.ImageView.ScaleType}.
+     *
+     * @param sceneRoot   The root of the transition hierarchy.
+     * @param startValues The values for a specific target in the start scene.
+     * @param endValues   The values for the target in the end scene.
+     * @return An Animator to move an ImageView or null if the View is not an ImageView,
+     * the Drawable changed, the View is not VISIBLE, or there was no change.
+     */
+    @Override
+    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
+            TransitionValues endValues) {
+        if (startValues == null || endValues == null) {
+            return null;
+        }
+        Rect startBounds = (Rect) startValues.values.get(PROPNAME_BOUNDS);
+        Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
+        if (startBounds == null || endBounds == null) {
+            return null;
+        }
+
+        Matrix startMatrix = (Matrix) startValues.values.get(PROPNAME_MATRIX);
+        Matrix endMatrix = (Matrix) endValues.values.get(PROPNAME_MATRIX);
+
+        boolean matricesEqual = (startMatrix == null && endMatrix == null) ||
+                (startMatrix != null && startMatrix.equals(endMatrix));
+
+        if (startBounds.equals(endBounds) && matricesEqual) {
+            return null;
+        }
+
+        ImageView imageView = (ImageView) endValues.view;
+        Drawable drawable = imageView.getDrawable();
+        int drawableWidth = drawable.getIntrinsicWidth();
+        int drawableHeight = drawable.getIntrinsicHeight();
+
+        ObjectAnimator animator;
+        if (drawableWidth == 0 || drawableHeight == 0) {
+            animator = createNullAnimator(imageView);
+        } else {
+            if (startMatrix == null) {
+                startMatrix = Matrix.IDENTITY_MATRIX;
+            }
+            if (endMatrix == null) {
+                endMatrix = Matrix.IDENTITY_MATRIX;
+            }
+            animator = createMatrixAnimator(imageView, startMatrix, endMatrix);
+        }
+        return animator;
+    }
+
+    private ObjectAnimator createNullAnimator(ImageView imageView) {
+        return ObjectAnimator.ofObject(imageView, ANIMATED_TRANSFORM_PROPERTY,
+                NULL_MATRIX_EVALUATOR, null, null);
+    }
+
+    private ObjectAnimator createMatrixAnimator(final ImageView imageView, Matrix startMatrix,
+            final Matrix endMatrix) {
+        ObjectAnimator animator = ObjectAnimator.ofObject(imageView, ANIMATED_TRANSFORM_PROPERTY,
+                new MatrixEvaluator(), startMatrix, endMatrix);
+        /*
+        AnimatorListenerAdapter listener = new AnimatorListenerAdapter() {
+            private Matrix mPausedMatrix;
+
+            @Override
+            public void onAnimationPause(Animator animation) {
+                if (mPausedMatrix == null) {
+                    mPausedMatrix = new Matrix();
+                }
+                Matrix imageMatrix = imageView.getImageMatrix();
+                mPausedMatrix.set(imageMatrix);
+                imageView.animateTransform(endMatrix);
+            }
+
+            @Override
+            public void onAnimationResume(Animator animation) {
+                imageView.animateTransform(mPausedMatrix);
+            }
+        };
+        animator.addPauseListener(listener);
+        */
+        return animator;
+    }
+
+    private static class MatrixEvaluator implements TypeEvaluator<Matrix> {
+
+        float[] mTempStartValues = new float[9];
+
+        float[] mTempEndValues = new float[9];
+
+        Matrix mTempMatrix = new Matrix();
+
+        @Override
+        public Matrix evaluate(float fraction, Matrix startValue, Matrix endValue) {
+            startValue.getValues(mTempStartValues);
+            endValue.getValues(mTempEndValues);
+            for (int i = 0; i < 9; i++) {
+                float diff = mTempEndValues[i] - mTempStartValues[i];
+                mTempEndValues[i] = mTempStartValues[i] + (fraction * diff);
+            }
+            mTempMatrix.setValues(mTempEndValues);
+            return mTempMatrix;
+        }
+    }
+
+}
diff --git a/core/java/android/transition/MatrixClippedDrawable.java b/core/java/android/transition/MatrixClippedDrawable.java
deleted file mode 100644
index ebaad59..0000000
--- a/core/java/android/transition/MatrixClippedDrawable.java
+++ /dev/null
@@ -1,300 +0,0 @@
-/*
- * 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 android.transition;
-
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.Matrix;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.util.Property;
-
-/**
- * Used in MoveImage to mock an ImageView as a Drawable to be scaled in the scene root Overlay.
- * @hide
- */
-class MatrixClippedDrawable extends Drawable implements Drawable.Callback {
-    private static final String TAG = "MatrixClippedDrawable";
-
-    private ClippedMatrixState mClippedMatrixState;
-
-    public static final Property<MatrixClippedDrawable, Rect> CLIP_PROPERTY
-            = new Property<MatrixClippedDrawable, Rect>(Rect.class, "clipRect") {
-
-        @Override
-        public Rect get(MatrixClippedDrawable object) {
-            return object.getClipRect();
-        }
-
-        @Override
-        public void set(MatrixClippedDrawable object, Rect value) {
-            object.setClipRect(value);
-        }
-    };
-
-    public static final Property<MatrixClippedDrawable, Matrix> MATRIX_PROPERTY
-            = new Property<MatrixClippedDrawable, Matrix>(Matrix.class, "matrix") {
-        @Override
-        public void set(MatrixClippedDrawable object, Matrix value) {
-            object.setMatrix(value);
-        }
-
-        @Override
-        public Matrix get(MatrixClippedDrawable object) {
-            return object.getMatrix();
-        }
-    };
-
-    public MatrixClippedDrawable(Drawable drawable) {
-        this(null, null);
-
-        mClippedMatrixState.mDrawable = drawable;
-
-        if (drawable != null) {
-            drawable.setCallback(this);
-        }
-    }
-
-    public void setMatrix(Matrix matrix) {
-        if (matrix == null) {
-            mClippedMatrixState.mMatrix = null;
-        } else {
-            if (mClippedMatrixState.mMatrix == null) {
-                mClippedMatrixState.mMatrix = new Matrix();
-            }
-            mClippedMatrixState.mMatrix.set(matrix);
-        }
-        invalidateSelf();
-    }
-
-    public Matrix getMatrix() {
-        return mClippedMatrixState.mMatrix;
-    }
-
-    public Rect getClipRect() {
-        return mClippedMatrixState.mClipRect;
-    }
-
-    public void setClipRect(Rect clipRect) {
-        if (clipRect == null) {
-            if (mClippedMatrixState.mClipRect != null) {
-                mClippedMatrixState.mClipRect = null;
-                invalidateSelf();
-            }
-        } else {
-            if (mClippedMatrixState.mClipRect == null) {
-                mClippedMatrixState.mClipRect = new Rect(clipRect);
-            } else {
-                mClippedMatrixState.mClipRect.set(clipRect);
-            }
-            invalidateSelf();
-        }
-    }
-
-    // overrides from Drawable.Callback
-
-    public void invalidateDrawable(Drawable who) {
-        final Drawable.Callback callback = getCallback();
-        if (callback != null) {
-            callback.invalidateDrawable(this);
-        }
-    }
-
-    public void scheduleDrawable(Drawable who, Runnable what, long when) {
-        final Drawable.Callback callback = getCallback();
-        if (callback != null) {
-            callback.scheduleDrawable(this, what, when);
-        }
-    }
-
-    public void unscheduleDrawable(Drawable who, Runnable what) {
-        final Drawable.Callback callback = getCallback();
-        if (callback != null) {
-            callback.unscheduleDrawable(this, what);
-        }
-    }
-
-    // overrides from Drawable
-
-    @Override
-    public int getChangingConfigurations() {
-        return super.getChangingConfigurations()
-                | mClippedMatrixState.mChangingConfigurations
-                | mClippedMatrixState.mDrawable.getChangingConfigurations();
-    }
-
-    @Override
-    public boolean getPadding(Rect padding) {
-        // XXX need to adjust padding!
-        return mClippedMatrixState.mDrawable.getPadding(padding);
-    }
-
-    @Override
-    public boolean setVisible(boolean visible, boolean restart) {
-        mClippedMatrixState.mDrawable.setVisible(visible, restart);
-        return super.setVisible(visible, restart);
-    }
-
-    @Override
-    public void setAlpha(int alpha) {
-        mClippedMatrixState.mDrawable.setAlpha(alpha);
-    }
-
-    @Override
-    public int getAlpha() {
-        return mClippedMatrixState.mDrawable.getAlpha();
-    }
-
-    @Override
-    public void setColorFilter(ColorFilter cf) {
-        mClippedMatrixState.mDrawable.setColorFilter(cf);
-    }
-
-    @Override
-    public int getOpacity() {
-        return mClippedMatrixState.mDrawable.getOpacity();
-    }
-
-    @Override
-    public boolean isStateful() {
-        return mClippedMatrixState.mDrawable.isStateful();
-    }
-
-    @Override
-    protected boolean onStateChange(int[] state) {
-        return mClippedMatrixState.mDrawable.setState(state);
-    }
-
-    @Override
-    protected boolean onLevelChange(int level) {
-        mClippedMatrixState.mDrawable.setLevel(level);
-        invalidateSelf();
-        return true;
-    }
-
-    @Override
-    protected void onBoundsChange(Rect bounds) {
-        super.setBounds(bounds);
-        if (mClippedMatrixState.mMatrix == null) {
-            mClippedMatrixState.mDrawable.setBounds(bounds);
-        } else {
-            int drawableWidth = mClippedMatrixState.mDrawable.getIntrinsicWidth();
-            int drawableHeight = mClippedMatrixState.mDrawable.getIntrinsicHeight();
-            mClippedMatrixState.mDrawable.setBounds(bounds.left, bounds.top,
-                    drawableWidth + bounds.left, drawableHeight + bounds.top);
-        }
-        invalidateSelf();
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        Rect bounds = getBounds();
-        int left = bounds.left;
-        int top = bounds.top;
-        int saveCount = canvas.getSaveCount();
-        canvas.save();
-        if (mClippedMatrixState.mClipRect != null) {
-            canvas.clipRect(mClippedMatrixState.mClipRect);
-        } else {
-            canvas.clipRect(bounds);
-        }
-
-        if (mClippedMatrixState != null && !mClippedMatrixState.mMatrix.isIdentity()) {
-            canvas.translate(left, top);
-            canvas.concat(mClippedMatrixState.mMatrix);
-            canvas.translate(-left, -top);
-        }
-        mClippedMatrixState.mDrawable.draw(canvas);
-        canvas.restoreToCount(saveCount);
-    }
-
-    @Override
-    public int getIntrinsicWidth() {
-        return mClippedMatrixState.mDrawable.getIntrinsicWidth();
-    }
-
-    @Override
-    public int getIntrinsicHeight() {
-        return mClippedMatrixState.mDrawable.getIntrinsicHeight();
-    }
-
-    @Override
-    public Drawable.ConstantState getConstantState() {
-        if (mClippedMatrixState.canConstantState()) {
-            mClippedMatrixState.mChangingConfigurations = getChangingConfigurations();
-            return mClippedMatrixState;
-        }
-        return null;
-    }
-
-    final static class ClippedMatrixState extends Drawable.ConstantState {
-        Drawable mDrawable;
-        Matrix mMatrix;
-        Rect mClipRect;
-
-        private boolean mCheckedConstantState;
-        private boolean mCanConstantState;
-        int mChangingConfigurations;
-
-        ClippedMatrixState(ClippedMatrixState orig, MatrixClippedDrawable owner, Resources res) {
-            if (orig != null) {
-                if (res != null) {
-                    mDrawable = orig.mDrawable.getConstantState().newDrawable(res);
-                } else {
-                    mDrawable = orig.mDrawable.getConstantState().newDrawable();
-                }
-                mDrawable.setCallback(owner);
-                mCheckedConstantState = mCanConstantState = true;
-                if (orig.mMatrix != null) {
-                    mMatrix = new Matrix(orig.mMatrix);
-                }
-                if (orig.mClipRect != null) {
-                    mClipRect = new Rect(orig.mClipRect);
-                }
-            }
-        }
-
-        @Override
-        public Drawable newDrawable() {
-            return new MatrixClippedDrawable(this, null);
-        }
-
-        @Override
-        public Drawable newDrawable(Resources res) {
-            return new MatrixClippedDrawable(this, res);
-        }
-
-        @Override
-        public int getChangingConfigurations() {
-            return mChangingConfigurations;
-        }
-
-        boolean canConstantState() {
-            if (!mCheckedConstantState) {
-                mCanConstantState = mDrawable.getConstantState() != null;
-                mCheckedConstantState = true;
-            }
-
-            return mCanConstantState;
-        }
-    }
-
-    private MatrixClippedDrawable(ClippedMatrixState state, Resources res) {
-        mClippedMatrixState = new ClippedMatrixState(state, this, res);
-    }
-
-}
diff --git a/core/java/android/transition/MoveImage.java b/core/java/android/transition/MoveImage.java
index 6f1b6f7..48b96ec 100644
--- a/core/java/android/transition/MoveImage.java
+++ b/core/java/android/transition/MoveImage.java
@@ -15,340 +15,17 @@
  */
 package android.transition;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.animation.RectEvaluator;
-import android.animation.TypeEvaluator;
-import android.animation.ValueAnimator;
-import android.graphics.Matrix;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
-import android.util.FloatMath;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewGroupOverlay;
-import android.view.ViewParent;
-import android.widget.ImageView;
-
-import java.util.ArrayList;
-import java.util.Map;
-
 /**
- * Transitions ImageViews, including size, scaleType, and matrix. The ImageView drawable
- * must remain the same between both start and end states, but the
- * {@link ImageView#setScaleType(android.widget.ImageView.ScaleType)} may
- * differ.
+ * TO BE REMOVED.
+ * Use ChangeImageTransform + ChangeBounds instead.
+ * @hide
  */
-public class MoveImage extends Transition {
-    private static final String TAG = "MoveImage";
-    private static final String PROPNAME_MATRIX = "android:moveImage:matrix";
-    private static final String PROPNAME_BOUNDS = "android:moveImage:bounds";
-    private static final String PROPNAME_CLIP = "android:moveImage:clip";
-    private static final String PROPNAME_DRAWABLE = "android:moveImage:drawable";
+public class MoveImage extends TransitionSet {
 
-    private int[] mTempLoc = new int[2];
-
-    private static final String[] sTransitionProperties = {
-            PROPNAME_MATRIX,
-            PROPNAME_BOUNDS,
-            PROPNAME_CLIP,
-            PROPNAME_DRAWABLE,
-    };
-
-    private void captureValues(TransitionValues transitionValues) {
-        View view = transitionValues.view;
-        if (!(view instanceof ImageView) || view.getVisibility() != View.VISIBLE) {
-            return;
-        }
-        ImageView imageView = (ImageView) view;
-        Drawable drawable = imageView.getDrawable();
-        if (drawable == null) {
-            return;
-        }
-        Map<String, Object> values = transitionValues.values;
-        values.put(PROPNAME_DRAWABLE, drawable);
-
-        ViewGroup parent = (ViewGroup) view.getParent();
-        parent.getLocationInWindow(mTempLoc);
-        int paddingLeft = view.getPaddingLeft();
-        int paddingTop = view.getPaddingTop();
-        int paddingRight = view.getPaddingRight();
-        int paddingBottom = view.getPaddingBottom();
-        int left = mTempLoc[0] + paddingLeft + view.getLeft() + Math.round(view.getTranslationX());
-        int top = mTempLoc[1] + paddingTop + view.getTop() + Math.round(view.getTranslationY());
-        int right = left + view.getWidth() - paddingRight - paddingLeft;
-        int bottom = top + view.getHeight() - paddingTop - paddingBottom;
-
-        Rect bounds = new Rect(left, top, right, bottom);
-        values.put(PROPNAME_BOUNDS, bounds);
-        Matrix matrix = getMatrix(imageView);
-        values.put(PROPNAME_MATRIX, matrix);
-        values.put(PROPNAME_CLIP, findClip(imageView));
-    }
-
-    @Override
-    public void captureStartValues(TransitionValues transitionValues) {
-        captureValues(transitionValues);
-    }
-
-    @Override
-    public void captureEndValues(TransitionValues transitionValues) {
-        captureValues(transitionValues);
-    }
-
-    @Override
-    public String[] getTransitionProperties() {
-        return sTransitionProperties;
-    }
-
-    /**
-     * Creates an Animator for ImageViews moving, changing dimensions, and/or changing
-     * {@link android.widget.ImageView.ScaleType}.
-     * @param sceneRoot The root of the transition hierarchy.
-     * @param startValues The values for a specific target in the start scene.
-     * @param endValues The values for the target in the end scene.
-     * @return An Animator to move an ImageView or null if the View is not an ImageView,
-     * the Drawable changed, the View is not VISIBLE, or there was no change.
-     */
-    @Override
-    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
-            TransitionValues endValues) {
-        if (startValues == null || endValues == null
-                || startValues.values.get(PROPNAME_BOUNDS) == null
-                || endValues.values.get(PROPNAME_BOUNDS) == null
-                || startValues.values.get(PROPNAME_DRAWABLE)
-                        != endValues.values.get(PROPNAME_DRAWABLE)) {
-            return null;
-        }
-        ArrayList<PropertyValuesHolder> changes = new ArrayList<PropertyValuesHolder>();
-
-        Matrix startMatrix = (Matrix) startValues.values.get(PROPNAME_MATRIX);
-        Matrix endMatrix = (Matrix) endValues.values.get(PROPNAME_MATRIX);
-
-        if (startMatrix != null && !startMatrix.equals(endMatrix)) {
-            changes.add(PropertyValuesHolder.ofObject(MatrixClippedDrawable.MATRIX_PROPERTY,
-                    new MatrixEvaluator(), startMatrix, endMatrix));
-        }
-
-        sceneRoot.getLocationInWindow(mTempLoc);
-        int rootX = mTempLoc[0];
-        int rootY = mTempLoc[1];
-        final ImageView imageView = (ImageView) endValues.view;
-
-        Drawable drawable = imageView.getDrawable();
-
-        Rect startBounds = new Rect((Rect) startValues.values.get(PROPNAME_BOUNDS));
-        Rect endBounds = new Rect((Rect) endValues.values.get(PROPNAME_BOUNDS));
-        startBounds.offset(-rootX, -rootY);
-        endBounds.offset(-rootX, -rootY);
-
-        if (!startBounds.equals(endBounds)) {
-            changes.add(PropertyValuesHolder.ofObject("bounds", new RectEvaluator(new Rect()),
-                    startBounds, endBounds));
-        }
-
-        Rect startClip = (Rect) startValues.values.get(PROPNAME_CLIP);
-        Rect endClip = (Rect) endValues.values.get(PROPNAME_CLIP);
-        if (startClip != null || endClip != null) {
-            startClip = nonNullClip(startClip, sceneRoot, rootX, rootY);
-            endClip = nonNullClip(endClip, sceneRoot, rootX, rootY);
-
-            expandClip(startBounds, startMatrix, startClip, endClip);
-            expandClip(endBounds, endMatrix, endClip, startClip);
-            boolean clipped = !startClip.contains(startBounds) || !endClip.contains(endBounds);
-            if (!clipped) {
-                startClip = null;
-            } else if (!startClip.equals(endClip)) {
-                changes.add(PropertyValuesHolder.ofObject(MatrixClippedDrawable.CLIP_PROPERTY,
-                        new RectEvaluator(), startClip, endClip));
-            }
-        }
-
-        if (changes.isEmpty()) {
-            return null;
-        }
-
-        drawable = drawable.getConstantState().newDrawable();
-        final MatrixClippedDrawable matrixClippedDrawable = new MatrixClippedDrawable(drawable);
-        final ImageView overlayImage = new ImageView(imageView.getContext());
-        final ViewGroupOverlay overlay = sceneRoot.getOverlay();
-        overlay.add(overlayImage);
-        overlayImage.setLeft(0);
-        overlayImage.setTop(0);
-        overlayImage.setRight(sceneRoot.getWidth());
-        overlayImage.setBottom(sceneRoot.getBottom());
-        overlayImage.setScaleType(ImageView.ScaleType.MATRIX);
-        overlayImage.setImageDrawable(matrixClippedDrawable);
-        matrixClippedDrawable.setMatrix(startMatrix);
-        matrixClippedDrawable.setBounds(startBounds);
-        matrixClippedDrawable.setClipRect(startClip);
-
-        imageView.setVisibility(View.INVISIBLE);
-        ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(matrixClippedDrawable,
-                changes.toArray(new PropertyValuesHolder[changes.size()]));
-
-        AnimatorListenerAdapter listener = new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                imageView.setVisibility(View.VISIBLE);
-                overlay.remove(overlayImage);
-            }
-
-            @Override
-            public void onAnimationPause(Animator animation) {
-                imageView.setVisibility(View.VISIBLE);
-                overlayImage.setVisibility(View.INVISIBLE);
-            }
-
-            @Override
-            public void onAnimationResume(Animator animation) {
-                imageView.setVisibility(View.INVISIBLE);
-                overlayImage.setVisibility(View.VISIBLE);
-            }
-
-            @Override
-            public void onAnimationCancel(Animator animation) {
-                onAnimationEnd(animation);
-            }
-        };
-
-        animator.addListener(listener);
-        animator.addPauseListener(listener);
-
-        return animator;
-    }
-
-    private static Rect nonNullClip(Rect clip, ViewGroup sceneRoot, int rootX, int rootY) {
-        if (clip != null) {
-            clip = new Rect(clip);
-            clip.offset(-rootX, -rootY);
-        } else {
-            clip = new Rect(0, 0, sceneRoot.getWidth(), sceneRoot.getHeight());
-        }
-        return clip;
-    }
-
-    private static void expandClip(Rect bounds, Matrix matrix, Rect clip, Rect otherClip) {
-        RectF boundsF = new RectF(bounds);
-        if (matrix != null) {
-            matrix.mapRect(boundsF);
-        }
-        clip.left = expandMinDimension(boundsF.left, clip.left, otherClip.left);
-        clip.top = expandMinDimension(boundsF.top, clip.top, otherClip.top);
-        clip.right = expandMaxDimension(boundsF.right, clip.right, otherClip.right);
-        clip.bottom = expandMaxDimension(boundsF.bottom, clip.bottom, otherClip.bottom);
-    }
-
-    private static int expandMinDimension(float boundsDimension, int clipDimension,
-            int otherClipDimension) {
-        if (clipDimension > boundsDimension) {
-            // Already clipped in that dimension, return the clipped value
-            return clipDimension;
-        }
-        return Math.min(clipDimension, otherClipDimension);
-    }
-
-    private static int expandMaxDimension(float boundsDimension, int clipDimension,
-            int otherClipDimension) {
-        return -expandMinDimension(-boundsDimension, -clipDimension, -otherClipDimension);
-    }
-
-    private static Matrix getMatrix(ImageView imageView) {
-        Drawable drawable = imageView.getDrawable();
-        int drawableWidth = drawable.getIntrinsicWidth();
-        int drawableHeight = drawable.getIntrinsicHeight();
-        ImageView.ScaleType scaleType = imageView.getScaleType();
-        Matrix matrix;
-        if (drawableWidth <= 0 || drawableHeight <= 0) {
-            matrix = null;
-        } else if (scaleType == ImageView.ScaleType.FIT_XY) {
-            matrix = new Matrix();
-            float scaleX = imageView.getWidth();
-            scaleX /= drawableWidth;
-            float scaleY = imageView.getHeight();
-            scaleY /= drawableHeight;
-            matrix.setScale(scaleX, scaleY);
-        } else {
-            matrix = new Matrix(imageView.getImageMatrix());
-        }
-        return matrix;
-    }
-
-    private Rect findClip(ImageView imageView) {
-        if (imageView.getCropToPadding()) {
-            Rect clip = getClip(imageView);
-            clip.left += imageView.getPaddingLeft();
-            clip.right -= imageView.getPaddingRight();
-            clip.top += imageView.getPaddingTop();
-            clip.bottom -= imageView.getPaddingBottom();
-            return clip;
-        } else {
-            View view = imageView;
-            ViewParent viewParent;
-            while ((viewParent = view.getParent()) instanceof ViewGroup) {
-                ViewGroup viewGroup = (ViewGroup) viewParent;
-                if (viewGroup.getClipChildren()) {
-                    Rect clip = getClip(view);
-                    return clip;
-                }
-                view = viewGroup;
-            }
-        }
-        return null;
-    }
-
-    private Rect getClip(View clipView) {
-        Rect clipBounds = clipView.getClipBounds();
-        if (clipBounds == null) {
-            clipBounds = new Rect(clipView.getLeft(), clipView.getTop(),
-                    clipView.getRight(), clipView.getBottom());
-        }
-
-        ViewParent parent = clipView.getParent();
-        if (parent instanceof ViewGroup) {
-            ViewGroup parentViewGroup = (ViewGroup) parent;
-            parentViewGroup.getLocationInWindow(mTempLoc);
-            clipBounds.offset(mTempLoc[0], mTempLoc[1]);
-        }
-
-        return clipBounds;
-    }
-
-    @Override
-    public Transition clone() {
-        MoveImage clone = (MoveImage) super.clone();
-        clone.mTempLoc = new int[2];
-        return clone;
-    }
-
-    private static class MatrixEvaluator implements TypeEvaluator<Matrix> {
-        static final Matrix sIdentity = new Matrix();
-        float[] mTempStartValues = new float[9];
-        float[] mTempEndValues = new float[9];
-        Matrix mTempMatrix = new Matrix();
-
-        @Override
-        public Matrix evaluate(float fraction, Matrix startValue, Matrix endValue) {
-            if (startValue == null && endValue == null) {
-                return null;
-            }
-            if (startValue == null) {
-                startValue = sIdentity;
-            } else if (endValue == null) {
-                endValue = sIdentity;
-            }
-            startValue.getValues(mTempStartValues);
-            endValue.getValues(mTempEndValues);
-            for (int i = 0; i < 9; i++) {
-                float diff = mTempEndValues[i] - mTempStartValues[i];
-                mTempEndValues[i] = mTempStartValues[i] + (fraction * diff);
-            }
-            mTempMatrix.setValues(mTempEndValues);
-            return mTempMatrix;
-        }
+    public MoveImage() {
+        addTransition(new ChangeBounds());
+        addTransition(new ChangeClipBounds());
+        addTransition(new ChangeTransform());
+        addTransition(new ChangeImageTransform());
     }
 }
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index 8818128..52e6691 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -68,8 +68,8 @@
  *
  * <p>This TransitionSet contains {@link android.transition.Explode} for visibility,
  * {@link android.transition.ChangeBounds}, {@link android.transition.ChangeTransform},
- * and {@link android.transition.ChangeClipBounds} for non-<code>ImageView</code>s and
- * {@link android.transition.MoveImage} for <code>ImageView</code>s:</p>
+ * and {@link android.transition.ChangeClipBounds} and
+ * {@link android.transition.ChangeImageTransform}:</p>
  *
  * {@sample development/samples/ApiDemos/res/transition/explode_move_together.xml MultipleTransform}
  *
diff --git a/core/java/android/transition/TransitionInflater.java b/core/java/android/transition/TransitionInflater.java
index 9e43201..e0c3cae 100644
--- a/core/java/android/transition/TransitionInflater.java
+++ b/core/java/android/transition/TransitionInflater.java
@@ -158,6 +158,9 @@
             } else if ("moveImage".equals(name)) {
                 transition = new MoveImage();
                 newTransition = true;
+            } else if ("changeImageTransform".equals(name)) {
+                transition = new ChangeImageTransform();
+                newTransition = true;
             } else if ("changeTransform".equals(name)) {
                 transition = new ChangeTransform();
                 newTransition = true;
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 0120875..0a44d23 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -1435,7 +1435,7 @@
     /**
      * Sets the Transition that will be used for shared elements transferred into the content
      * Scene. Typical Transitions will affect size and location, such as
-     * {@link android.transition.MoveImage} and {@link android.transition.ChangeBounds}. A null
+     * {@link android.transition.ChangeBounds}. A null
      * value will cause transferred shared elements to blink to the final position.
      * Requires {@link #FEATURE_CONTENT_TRANSITIONS}.
      * @param transition The Transition to use for shared elements transferred into the content
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 5d578ca..e97177d 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -1117,6 +1117,20 @@
         }
     }
 
+    /** @hide */
+    public void animateTransform(Matrix matrix) {
+        if (matrix == null) {
+            mDrawable.setBounds(0, 0, getWidth(), getHeight());
+        } else {
+            mDrawable.setBounds(0, 0, mDrawableWidth, mDrawableHeight);
+            if (mDrawMatrix == null) {
+                mDrawMatrix = new Matrix();
+            }
+            mDrawMatrix.set(matrix);
+        }
+        invalidate();
+    }
+
     @Override
     protected void onDraw(Canvas canvas) {
         super.onDraw(canvas);
diff --git a/core/res/res/transition/move.xml b/core/res/res/transition/move.xml
index d4863ee..56e1938 100644
--- a/core/res/res/transition/move.xml
+++ b/core/res/res/transition/move.xml
@@ -14,17 +14,8 @@
      limitations under the License.
 -->
 <transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
-    <transitionSet>
-        <changeBounds/>
-        <changeTransform/>
-        <changeClipBounds/>
-        <targets>
-            <target android:excludeClass="android.widget.ImageView"/>
-        </targets>
-    </transitionSet>
-    <moveImage>
-        <targets>
-            <target android:targetClass="android.widget.ImageView"/>
-        </targets>
-    </moveImage>
+    <changeBounds/>
+    <changeTransform/>
+    <changeClipBounds/>
+    <changeImageTransform/>
 </transitionSet>