Fix CrossFade transition

Small fixes in CrossFade to handle more general situations.
Also, added API to CrossFade to enable different behaviors during
the transition.

Change-Id: I12d17ab8f8f96be370c070373112ad8a62df9115
diff --git a/api/current.txt b/api/current.txt
index b7c1bbd..b35d37b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -28197,7 +28197,15 @@
   public class Crossfade extends android.view.transition.Transition {
     ctor public Crossfade();
     method protected void captureValues(android.view.transition.TransitionValues, boolean);
+    method public int getFadeBehavior();
+    method public int getResizeBehavior();
     method protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
+    method public void setFadeBehavior(int);
+    method public void setResizeBehavior(int);
+    field public static final int FADE_BEHAVIOR_CROSSFADE = 0; // 0x0
+    field public static final int FADE_BEHAVIOR_REVEAL = 1; // 0x1
+    field public static final int RESIZE_BEHAVIOR_NONE = 0; // 0x0
+    field public static final int RESIZE_BEHAVIOR_SCALE = 1; // 0x1
   }
 
   public class Fade extends android.view.transition.Visibility {
diff --git a/core/java/android/view/transition/Crossfade.java b/core/java/android/view/transition/Crossfade.java
index 7a55b0d..a40d0bf 100644
--- a/core/java/android/view/transition/Crossfade.java
+++ b/core/java/android/view/transition/Crossfade.java
@@ -26,12 +26,12 @@
 import android.graphics.Rect;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
-import android.util.ArrayMap;
 import android.util.Log;
 import android.view.SurfaceView;
 import android.view.TextureView;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewOverlay;
 
 import java.util.Map;
 
@@ -54,13 +54,97 @@
 
     private static RectEvaluator sRectEvaluator = new RectEvaluator();
 
+    private int mFadeBehavior = FADE_BEHAVIOR_REVEAL;
+    private int mResizeBehavior = RESIZE_BEHAVIOR_SCALE;
+
+    /**
+     * Flag specifying that the fading animation should cross-fade
+     * between the old and new representation of all affected target
+     * views. This means that the old representation will fade out
+     * while the new one fades in. This effect may work well on views
+     * without solid backgrounds, such as TextViews.
+     *
+     * @see #setFadeBehavior(int)
+     */
+    public static final int FADE_BEHAVIOR_CROSSFADE = 0;
+    /**
+     * Flag specifying that the fading animation should reveal the
+     * new representation of all affected target views. This means
+     * that the old representation will fade out, gradually
+     * revealing the new representation, which remains opaque
+     * the whole time. This effect may work well on views
+     * with solid backgrounds, such as ImageViews.
+     *
+     * @see #setFadeBehavior(int)
+     */
+    public static final int FADE_BEHAVIOR_REVEAL = 1;
+
+    /**
+     * Flag specifying that the transition should not animate any
+     * changes in size between the old and new target views.
+     * This means that no scaling will take place as a result of
+     * this transition
+     *
+     * @see #setResizeBehavior(int)
+     */
+    public static final int RESIZE_BEHAVIOR_NONE = 0;
+    /**
+     * Flag specifying that the transition should animate any
+     * changes in size between the old and new target views.
+     * This means that the animation will scale the start/end
+     * representations of affected views from the starting size
+     * to the ending size over the course of the animation.
+     * This effect may work well on images, but is not recommended
+     * for text.
+     *
+     * @see #setResizeBehavior(int)
+     */
+    public static final int RESIZE_BEHAVIOR_SCALE = 1;
+
+    // TODO: Add fade/resize behaviors to xml resources
+
+    /**
+     * Sets the type of fading animation that will be run, one of
+     * {@link #FADE_BEHAVIOR_CROSSFADE} and {@link #FADE_BEHAVIOR_REVEAL}.
+     *
+     * @param fadeBehavior The type of fading animation to use when this
+     * transition is run.
+     */
+    public void setFadeBehavior(int fadeBehavior) {
+        if (fadeBehavior >= FADE_BEHAVIOR_CROSSFADE && fadeBehavior <= FADE_BEHAVIOR_REVEAL) {
+            mFadeBehavior = fadeBehavior;
+        }
+    }
+
+    public int getFadeBehavior() {
+        return mFadeBehavior;
+    }
+
+    /**
+     * Sets the type of resizing behavior that will be used during the
+     * transition animation, one of {@link #RESIZE_BEHAVIOR_NONE} and
+     * {@link #RESIZE_BEHAVIOR_SCALE}.
+     *
+     * @param resizeBehavior The type of resizing behavior to use when this
+     * transition is run.
+     */
+    public void setResizeBehavior(int resizeBehavior) {
+        if (resizeBehavior >= RESIZE_BEHAVIOR_NONE && resizeBehavior <= RESIZE_BEHAVIOR_SCALE) {
+            mResizeBehavior = resizeBehavior;
+        }
+    }
+
+    public int getResizeBehavior() {
+        return mResizeBehavior;
+    }
+
     @Override
     protected boolean prePlay(ViewGroup sceneRoot, TransitionValues startValues,
             TransitionValues endValues) {
         if (startValues == null || endValues == null) {
             return false;
         }
-        final View view = startValues.view;
+        final View view = endValues.view;
         Map<String, Object> startVals = startValues.values;
         Map<String, Object> endVals = endValues.values;
         Bitmap startBitmap = (Bitmap) startVals.get(PROPNAME_BITMAP);
@@ -72,8 +156,12 @@
                     " for start, end: " + startBitmap + ", " + endBitmap);
         }
         if (startDrawable != null && endDrawable != null && !startBitmap.sameAs(endBitmap)) {
-            view.getOverlay().add(endDrawable);
-            view.getOverlay().add(startDrawable);
+            ViewOverlay overlay = (mFadeBehavior == FADE_BEHAVIOR_REVEAL) ?
+                    view.getOverlay() : ((ViewGroup) view.getParent()).getOverlay();
+            if (mFadeBehavior == FADE_BEHAVIOR_REVEAL) {
+                overlay.add(endDrawable);
+            }
+            overlay.add(startDrawable);
             return true;
         } else {
             return false;
@@ -107,6 +195,10 @@
                 view.invalidate(startDrawable.getBounds());
             }
         });
+        ObjectAnimator anim1 = null;
+        if (mFadeBehavior == FADE_BEHAVIOR_CROSSFADE) {
+            anim1 = ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1);
+        }
         if (Transition.DBG) {
             Log.d(LOG_TAG, "Crossfade: created anim " + anim + " for start, end values " +
                     startValues + ", " + endValues);
@@ -114,23 +206,34 @@
         anim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
-                view.getOverlay().remove(startDrawable);
-                view.getOverlay().remove(endDrawable);
+                ViewOverlay overlay = (mFadeBehavior == FADE_BEHAVIOR_REVEAL) ?
+                        view.getOverlay() : ((ViewGroup) view.getParent()).getOverlay();
+                overlay.remove(startDrawable);
+                if (mFadeBehavior == FADE_BEHAVIOR_REVEAL) {
+                    overlay.remove(endDrawable);
+                }
             }
         });
         AnimatorSet set = new AnimatorSet();
         set.playTogether(anim);
-        if (!startBounds.equals(endBounds)) {
+        if (anim1 != null) {
+            set.playTogether(anim1);
+        }
+        if (mResizeBehavior == RESIZE_BEHAVIOR_SCALE && !startBounds.equals(endBounds)) {
             if (Transition.DBG) {
                 Log.d(LOG_TAG, "animating from startBounds to endBounds: " +
                         startBounds + ", " + endBounds);
             }
             Animator anim2 = ObjectAnimator.ofObject(startDrawable, "bounds",
                     sRectEvaluator, startBounds, endBounds);
-            Animator anim3 = ObjectAnimator.ofObject(endDrawable, "bounds",
-                    sRectEvaluator, startBounds, endBounds);
             set.playTogether(anim2);
-            set.playTogether(anim3);
+            if (mResizeBehavior == RESIZE_BEHAVIOR_SCALE) {
+                // TODO: How to handle resizing with a CROSSFADE (vs. REVEAL) effect
+                // when we are animating the view directly?
+                Animator anim3 = ObjectAnimator.ofObject(endDrawable, "bounds",
+                        sRectEvaluator, startBounds, endBounds);
+                set.playTogether(anim3);
+            }
         }
         return set;
     }
@@ -138,8 +241,11 @@
     @Override
     protected void captureValues(TransitionValues values, boolean start) {
         View view = values.view;
-        values.values.put(PROPNAME_BOUNDS, new Rect(0, 0,
-                view.getWidth(), view.getHeight()));
+        Rect bounds = new Rect(0, 0, view.getWidth(), view.getHeight());
+        if (mFadeBehavior == FADE_BEHAVIOR_CROSSFADE) {
+            bounds.offset(view.getLeft(), view.getTop());
+        }
+        values.values.put(PROPNAME_BOUNDS, bounds);
 
         if (Transition.DBG) {
             Log.d(LOG_TAG, "Captured bounds " + values.values.get(PROPNAME_BOUNDS) + ": start = " +
@@ -157,7 +263,7 @@
         // TODO: I don't have resources, can't call the non-deprecated method?
         BitmapDrawable drawable = new BitmapDrawable(bitmap);
         // TODO: lrtb will be wrong if the view has transXY set
-        drawable.setBounds(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
+        drawable.setBounds(bounds);
         values.values.put(PROPNAME_DRAWABLE, drawable);
     }
 
diff --git a/tests/TransitionTests/src/com/android/transitiontests/CrossFadeDemo.java b/tests/TransitionTests/src/com/android/transitiontests/CrossFadeDemo.java
index 2fcdc30..d5f6a28 100644
--- a/tests/TransitionTests/src/com/android/transitiontests/CrossFadeDemo.java
+++ b/tests/TransitionTests/src/com/android/transitiontests/CrossFadeDemo.java
@@ -45,6 +45,8 @@
         mScene2 = new Scene(mSceneRoot, R.layout.crossfade_1, this);
 
         Crossfade crossfade = new Crossfade();
+        crossfade.setFadeBehavior(Crossfade.FADE_BEHAVIOR_CROSSFADE);
+        crossfade.setResizeBehavior(Crossfade.RESIZE_BEHAVIOR_NONE);
         crossfade.setTargetIds(R.id.textview, R.id.textview1, R.id.textview2);
         mTransitionManager = new TransitionManager();
         TransitionGroup moveCrossFade = new TransitionGroup();