Fix minor transition bugs and add capabilities

Some view changes require more flexible transitions than the
defaults provided by Crossfade and TextChange - this change supplies some
of that flexibility.

Also, starting a new transition on a hierarchy undergoing a transition
caused the first to get canceled, then the start values to be retrieved.
The new transition should actually get the start values from the intermediate
state of the views, so we now cancel the previous transition only after the
start values have been captured.

Issue #9756349 Transitions: Crossfade does not handle interruption/reverse correctly
Issue #9295863 Transitions: Add behavior API/flags to various transitions
Issue #9275859 Transitions: Improve mechanism for transition interruption

Change-Id: I5a8c5a12466ddcab9e84e4880930563fa1216f3b
diff --git a/api/current.txt b/api/current.txt
index a5f05fa..14983f7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -28856,6 +28856,7 @@
     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_OUT_IN = 2; // 0x2
     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
@@ -28903,6 +28904,11 @@
   public class TextChange extends android.view.transition.Transition {
     ctor public TextChange();
     method protected void captureValues(android.view.transition.TransitionValues, boolean);
+    method public void setChangeBehavior(int);
+    field public static final int CHANGE_BEHAVIOR_IN = 2; // 0x2
+    field public static final int CHANGE_BEHAVIOR_KEEP = 0; // 0x0
+    field public static final int CHANGE_BEHAVIOR_OUT = 1; // 0x1
+    field public static final int CHANGE_BEHAVIOR_OUT_IN = 3; // 0x3
   }
 
   public abstract class Transition implements java.lang.Cloneable {
diff --git a/core/java/android/view/transition/Crossfade.java b/core/java/android/view/transition/Crossfade.java
index 7cfba70..18bb57f 100644
--- a/core/java/android/view/transition/Crossfade.java
+++ b/core/java/android/view/transition/Crossfade.java
@@ -24,6 +24,7 @@
 import android.animation.ValueAnimator;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
@@ -79,6 +80,15 @@
      * @see #setFadeBehavior(int)
      */
     public static final int FADE_BEHAVIOR_REVEAL = 1;
+    /**
+     * Flag specifying that the fading animation should first fade
+     * out the original representation completely and then fade in the
+     * new one. This effect may be more suitable than the other
+     * fade behaviors for views with.
+     *
+     * @see #setFadeBehavior(int)
+     */
+    public static final int FADE_BEHAVIOR_OUT_IN = 2;
 
     /**
      * Flag specifying that the transition should not animate any
@@ -112,7 +122,7 @@
      * transition is run.
      */
     public void setFadeBehavior(int fadeBehavior) {
-        if (fadeBehavior >= FADE_BEHAVIOR_CROSSFADE && fadeBehavior <= FADE_BEHAVIOR_REVEAL) {
+        if (fadeBehavior >= FADE_BEHAVIOR_CROSSFADE && fadeBehavior <= FADE_BEHAVIOR_OUT_IN) {
             mFadeBehavior = fadeBehavior;
         }
     }
@@ -145,6 +155,7 @@
         if (startValues == null || endValues == null) {
             return null;
         }
+        final boolean useParentOverlay = mFadeBehavior != FADE_BEHAVIOR_REVEAL;
         final View view = endValues.view;
         Map<String, Object> startVals = startValues.values;
         Map<String, Object> endVals = endValues.values;
@@ -159,8 +170,8 @@
                     " for start, end: " + startBitmap + ", " + endBitmap);
         }
         if (startDrawable != null && endDrawable != null && !startBitmap.sameAs(endBitmap)) {
-            ViewOverlay overlay = (mFadeBehavior == FADE_BEHAVIOR_REVEAL) ?
-                    view.getOverlay() : ((ViewGroup) view.getParent()).getOverlay();
+            ViewOverlay overlay = useParentOverlay ?
+                    ((ViewGroup) view.getParent()).getOverlay() : view.getOverlay();
             if (mFadeBehavior == FADE_BEHAVIOR_REVEAL) {
                 overlay.add(endDrawable);
             }
@@ -169,7 +180,13 @@
             // gradually fading out the start drawable. So it's not really a cross-fade, but rather
             // a reveal of the end scene over time. Also, animate the bounds of both drawables
             // to mimic the change in the size of the view itself between scenes.
-            ObjectAnimator anim = ObjectAnimator.ofInt(startDrawable, "alpha", 0);
+            ObjectAnimator anim;
+            if (mFadeBehavior == FADE_BEHAVIOR_OUT_IN) {
+                // Fade out completely halfway through the transition
+                anim = ObjectAnimator.ofInt(startDrawable, "alpha", 255, 0, 0);
+            } else {
+                anim = ObjectAnimator.ofInt(startDrawable, "alpha", 0);
+            }
             anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                 @Override
                 public void onAnimationUpdate(ValueAnimator animation) {
@@ -178,7 +195,10 @@
                 }
             });
             ObjectAnimator anim1 = null;
-            if (mFadeBehavior == FADE_BEHAVIOR_CROSSFADE) {
+            if (mFadeBehavior == FADE_BEHAVIOR_OUT_IN) {
+                // start fading in halfway through the transition
+                anim1 = ObjectAnimator.ofFloat(view, View.ALPHA, 0, 0, 1);
+            } else if (mFadeBehavior == FADE_BEHAVIOR_CROSSFADE) {
                 anim1 = ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1);
             }
             if (Transition.DBG) {
@@ -188,8 +208,8 @@
             anim.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
-                    ViewOverlay overlay = (mFadeBehavior == FADE_BEHAVIOR_REVEAL) ?
-                            view.getOverlay() : ((ViewGroup) view.getParent()).getOverlay();
+                    ViewOverlay overlay = useParentOverlay ?
+                            ((ViewGroup) view.getParent()).getOverlay() : view.getOverlay();
                     overlay.remove(startDrawable);
                     if (mFadeBehavior == FADE_BEHAVIOR_REVEAL) {
                         overlay.remove(endDrawable);
@@ -227,7 +247,7 @@
     protected void captureValues(TransitionValues values, boolean start) {
         View view = values.view;
         Rect bounds = new Rect(0, 0, view.getWidth(), view.getHeight());
-        if (mFadeBehavior == FADE_BEHAVIOR_CROSSFADE) {
+        if (mFadeBehavior != FADE_BEHAVIOR_REVEAL) {
             bounds.offset(view.getLeft(), view.getTop());
         }
         values.values.put(PROPNAME_BOUNDS, bounds);
diff --git a/core/java/android/view/transition/TextChange.java b/core/java/android/view/transition/TextChange.java
index f033e63..04ff707 100644
--- a/core/java/android/view/transition/TextChange.java
+++ b/core/java/android/view/transition/TextChange.java
@@ -18,7 +18,9 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
+import android.graphics.Color;
 import android.util.ArrayMap;
 import android.view.ViewGroup;
 import android.widget.TextView;
@@ -34,15 +36,69 @@
  */
 public class TextChange extends Transition {
     private static final String PROPNAME_TEXT = "android:textchange:text";
+    private static final String PROPNAME_TEXT_COLOR = "android:textchange:textColor";
 
-    // TODO: think about other options we could have here, like cross-fading the text, or fading
-    // it out/in. These could be parameters to supply to the constructors (and xml attributes).
+    private int mChangeBehavior = CHANGE_BEHAVIOR_KEEP;
+
+    /**
+     * Flag specifying that the text in affected/changing TextView targets will keep
+     * their original text during the transition, setting it to the final text when
+     * the transition ends. This is the default behavior.
+     *
+     * @see #setChangeBehavior(int)
+     */
+    public static final int CHANGE_BEHAVIOR_KEEP = 0;
+    /**
+     * Flag specifying that the text changing animation should first fade
+     * out the original text completely. The new text is set on the target
+     * view at the end of the fade-out animation. This transition is typically
+     * used with a later {@link #CHANGE_BEHAVIOR_IN} transition, allowing more
+     * flexibility than the {@link #CHANGE_BEHAVIOR_OUT_IN} by allowing other
+     * transitions to be run sequentially or in parallel with these fades.
+     *
+     * @see #setChangeBehavior(int)
+     */
+    public static final int CHANGE_BEHAVIOR_OUT = 1;
+    /**
+     * Flag specifying that the text changing animation should fade in the
+     * end text into the affected target view(s). This transition is typically
+     * used in conjunction with an earlier {@link #CHANGE_BEHAVIOR_OUT}
+     * transition, possibly with other transitions running as well, such as
+     * a sequence to fade out, then resize the view, then fade in.
+     *
+     * @see #setChangeBehavior(int)
+     */
+    public static final int CHANGE_BEHAVIOR_IN = 2;
+    /**
+     * Flag specifying that the text changing animation should first fade
+     * out the original text completely and then fade in the
+     * new text.
+     *
+     * @see #setChangeBehavior(int)
+     */
+    public static final int CHANGE_BEHAVIOR_OUT_IN = 3;
+
+    /**
+     * Sets the type of changing animation that will be run, one of
+     * {@link #CHANGE_BEHAVIOR_KEEP} and {@link #CHANGE_BEHAVIOR_OUT_IN}.
+     *
+     * @param changeBehavior The type of fading animation to use when this
+     * transition is run.
+     */
+    public void setChangeBehavior(int changeBehavior) {
+        if (changeBehavior >= CHANGE_BEHAVIOR_KEEP && changeBehavior <= CHANGE_BEHAVIOR_OUT_IN) {
+            mChangeBehavior = changeBehavior;
+        }
+    }
 
     @Override
     protected void captureValues(TransitionValues values, boolean start) {
         if (values.view instanceof TextView) {
             TextView textview = (TextView) values.view;
             values.values.put(PROPNAME_TEXT, textview.getText());
+            if (mChangeBehavior > CHANGE_BEHAVIOR_KEEP) {
+                values.values.put(PROPNAME_TEXT_COLOR, textview.getCurrentTextColor());
+            }
         }
     }
 
@@ -59,13 +115,60 @@
         final String endText = (String) endVals.get(PROPNAME_TEXT);
         if (!startText.equals(endText)) {
             view.setText(startText);
-            ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
-            anim.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    view.setText(endText);
+            Animator anim;
+            if (mChangeBehavior == CHANGE_BEHAVIOR_KEEP) {
+                anim = ValueAnimator.ofFloat(0, 1);
+                anim.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        view.setText(endText);
+                    }
+                });
+            } else {
+                // Fade out start text
+                final int startColor = (Integer) startVals.get(PROPNAME_TEXT_COLOR);
+                final int endColor = (Integer) endVals.get(PROPNAME_TEXT_COLOR);
+                ValueAnimator outAnim = null, inAnim = null;
+                if (mChangeBehavior == CHANGE_BEHAVIOR_OUT_IN ||
+                        mChangeBehavior == CHANGE_BEHAVIOR_OUT) {
+                    outAnim = ValueAnimator.ofInt(255, 0);
+                    outAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                        @Override
+                        public void onAnimationUpdate(ValueAnimator animation) {
+                            int currAlpha = (Integer) animation.getAnimatedValue();
+                            view.setTextColor(currAlpha << 24 | Color.red(startColor) << 16 |
+                                    Color.green(startColor) << 8 | Color.red(startColor));
+                        }
+                    });
+                    outAnim.addListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            view.setText(endText);
+                        }
+                    });
                 }
-            });
+                if (mChangeBehavior == CHANGE_BEHAVIOR_OUT_IN ||
+                        mChangeBehavior == CHANGE_BEHAVIOR_IN) {
+                    inAnim = ValueAnimator.ofInt(0, 255);
+                    inAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                        @Override
+                        public void onAnimationUpdate(ValueAnimator animation) {
+                            int currAlpha = (Integer) animation.getAnimatedValue();
+                            view.setTextColor(currAlpha << 24 | Color.red(endColor) << 16 |
+                                    Color.green(endColor) << 8 | Color.red(endColor));
+                        }
+                    });
+                }
+                if (outAnim != null && inAnim != null) {
+                    anim = new AnimatorSet();
+                    ((AnimatorSet) anim).playSequentially(outAnim, inAnim);
+                } else if (outAnim != null) {
+                    anim = outAnim;
+                } else {
+                    // Must be an in-only animation
+                    anim = inAnim;
+                }
+            }
             return anim;
         }
         return null;
diff --git a/core/java/android/view/transition/Transition.java b/core/java/android/view/transition/Transition.java
index 2cffd26..6d5e61a 100644
--- a/core/java/android/view/transition/Transition.java
+++ b/core/java/android/view/transition/Transition.java
@@ -509,6 +509,15 @@
      * false otherwise
      */
     void captureValues(ViewGroup sceneRoot, boolean start) {
+        if (start) {
+            mStartValues.viewValues.clear();
+            mStartValues.idValues.clear();
+            mStartValues.itemIdValues.clear();
+        } else {
+            mEndValues.viewValues.clear();
+            mEndValues.idValues.clear();
+            mEndValues.itemIdValues.clear();
+        }
         if (mTargetIds != null && mTargetIds.length > 0 ||
                 mTargets != null && mTargets.length > 0) {
             if (mTargetIds != null) {
@@ -774,13 +783,6 @@
                     v.setHasTransientState(false);
                 }
             }
-            mStartValues.viewValues.clear();
-            mStartValues.idValues.clear();
-            mStartValues.itemIdValues.clear();
-            mEndValues.viewValues.clear();
-            mEndValues.idValues.clear();
-            mEndValues.itemIdValues.clear();
-            mCurrentAnimators.clear();
         }
     }
 
diff --git a/core/java/android/view/transition/TransitionManager.java b/core/java/android/view/transition/TransitionManager.java
index 59b07b1..b200a6d 100644
--- a/core/java/android/view/transition/TransitionManager.java
+++ b/core/java/android/view/transition/TransitionManager.java
@@ -182,16 +182,17 @@
 
     private static void sceneChangeSetup(ViewGroup sceneRoot, Transition transition) {
 
-        Transition runningTransition = sRunningTransitions.get(sceneRoot);
-        if (runningTransition != null) {
-            runningTransition.cancelTransition();
-        }
-
         // Capture current values
+        Transition runningTransition = sRunningTransitions.get(sceneRoot);
+
         if (transition != null) {
             transition.captureValues(sceneRoot, true);
         }
 
+        if (runningTransition != null) {
+            runningTransition.cancelTransition();
+        }
+
         // Notify previous scene that it is being exited
         Scene previousScene = sceneRoot.getCurrentScene();
         if (previousScene != null) {
diff --git a/tests/TransitionTests/AndroidManifest.xml b/tests/TransitionTests/AndroidManifest.xml
index 9a399d0..35e7b69 100644
--- a/tests/TransitionTests/AndroidManifest.xml
+++ b/tests/TransitionTests/AndroidManifest.xml
@@ -240,6 +240,13 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+        <activity android:label="CrossfadeMultiple"
+                  android:name=".CrossfadeMultiple">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
 
     </application>
 
diff --git a/tests/TransitionTests/res/drawable-nodpi/self_portrait_square_400.jpg b/tests/TransitionTests/res/drawable-nodpi/self_portrait_square_400.jpg
new file mode 100644
index 0000000..3923fd1
--- /dev/null
+++ b/tests/TransitionTests/res/drawable-nodpi/self_portrait_square_400.jpg
Binary files differ
diff --git a/tests/TransitionTests/res/layout/crossfade_multiple.xml b/tests/TransitionTests/res/layout/crossfade_multiple.xml
new file mode 100644
index 0000000..ca32ecb
--- /dev/null
+++ b/tests/TransitionTests/res/layout/crossfade_multiple.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+    <RadioGroup android:orientation="horizontal"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content">
+        <RadioButton android:layout_width="wrap_content"
+                     android:layout_height="wrap_content"
+                     android:onClick="changeTransitionType"
+                     android:id="@+id/reveal"
+                     android:text="@string/reveal"/>
+        <RadioButton android:layout_width="wrap_content"
+                     android:layout_height="wrap_content"
+                     android:onClick="changeTransitionType"
+                     android:id="@+id/crossfade"
+                     android:text="@string/crossfade"/>
+        <RadioButton android:layout_width="wrap_content"
+                     android:layout_height="wrap_content"
+                     android:onClick="changeTransitionType"
+                     android:id="@+id/inout"
+                     android:text="@string/inout"/>
+        <RadioButton android:layout_width="wrap_content"
+                     android:layout_height="wrap_content"
+                     android:onClick="changeTransitionType"
+                     android:id="@+id/textfade1"
+                     android:text="@string/textfade1"/>
+        <RadioButton android:layout_width="wrap_content"
+                     android:layout_height="wrap_content"
+                     android:onClick="changeTransitionType"
+                     android:id="@+id/textfade2"
+                     android:text="@string/textfade2"/>
+    </RadioGroup>
+    <LinearLayout android:orientation="horizontal"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content">
+        <Button
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:onClick="sendMessage"
+                android:text="@string/state1"
+                android:id="@+id/button1"/>
+        <Button
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:onClick="sendMessage"
+                android:text="@string/state2"
+                android:id="@+id/button2"/>
+        <Button
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:onClick="sendMessage"
+                android:text="@string/state3"
+                android:id="@+id/button3"/>
+    </LinearLayout>
+
+    <LinearLayout android:orientation="vertical"
+                  android:id="@+id/container"
+                  android:layout_width="match_parent"
+                  android:layout_height="match_parent">
+        <Button
+                android:layout_width="100dip"
+                android:layout_height="100dip"
+                android:textSize="40dip"
+                android:text="@string/a"
+                android:id="@+id/button"/>
+        <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textSize="40dip"
+                android:text="@string/state1"
+                android:id="@+id/textview"/>
+        <ImageView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:src="@drawable/self_portrait_square_100"
+                android:onClick="sendMessage"
+                android:id="@+id/imageview"/>
+    </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/values/strings.xml b/tests/TransitionTests/res/values/strings.xml
index 9b80a26..9cf7a94 100644
--- a/tests/TransitionTests/res/values/strings.xml
+++ b/tests/TransitionTests/res/values/strings.xml
@@ -49,4 +49,12 @@
     <string name="button3">Button 3</string>
     <string name="button4">Button 4</string>
     <string name="button5">Button 5</string>
+    <string name="a">A</string>
+    <string name="b">B</string>
+    <string name="c">C</string>
+    <string name="reveal">Reveal</string>
+    <string name="crossfade">Crossfade</string>
+    <string name="inout">In/Out</string>
+    <string name="textfade1">T1</string>
+    <string name="textfade2">T2</string>
 </resources>
diff --git a/tests/TransitionTests/src/com/android/transitiontests/CrossfadeMultiple.java b/tests/TransitionTests/src/com/android/transitiontests/CrossfadeMultiple.java
new file mode 100644
index 0000000..b82dbd8
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/CrossfadeMultiple.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2013 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 com.android.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Crossfade;
+import android.view.transition.Move;
+import android.view.transition.TextChange;
+import android.view.transition.Transition;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import static android.widget.LinearLayout.LayoutParams;
+
+public class CrossfadeMultiple extends Activity {
+    ViewGroup mSceneRoot;
+    static int mCurrentScene;
+    TransitionManager mTransitionManager;
+    Transition mTransition;
+    ImageView mImageView;
+    TextView mTextView;
+    Button mButton;
+    Crossfade mCrossfade;
+    TransitionGroup mCrossfadeGroup;
+    TransitionGroup mTextChangeGroup1, mTextChangeGroup2;
+    TransitionGroup mInOutGroup;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.crossfade_multiple);
+
+        ViewGroup container = (ViewGroup) findViewById(R.id.container);
+        mSceneRoot = container;
+
+        mButton = (Button) findViewById(R.id.button);
+        mImageView = (ImageView) findViewById(R.id.imageview);
+        mTextView = (TextView) findViewById(R.id.textview);
+
+        mCrossfade = new Crossfade();
+        mCrossfade.setTargetIds(R.id.button, R.id.textview, R.id.imageview);
+
+        mCrossfadeGroup = new TransitionGroup();
+        mCrossfadeGroup.setDuration(300);
+        mCrossfadeGroup.addTransitions(mCrossfade, new Move());
+        mTransition = mCrossfadeGroup;
+
+        mInOutGroup = new TransitionGroup();
+        Crossfade inOut = new Crossfade();
+        inOut.setDuration(300);
+        inOut.setFadeBehavior(Crossfade.FADE_BEHAVIOR_OUT_IN);
+        Move move = new Move();
+        move.setStartDelay(150);
+        move.setDuration(0);
+        mInOutGroup.addTransitions(inOut, move);
+
+        mTextChangeGroup1 = new TransitionGroup();
+        TextChange textChangeInOut = new TextChange();
+        textChangeInOut.setChangeBehavior(TextChange.CHANGE_BEHAVIOR_OUT_IN);
+        mTextChangeGroup1.addTransitions(textChangeInOut, new Move());
+
+        mTextChangeGroup2 = new TransitionGroup();
+        mTextChangeGroup2.setOrdering(TransitionGroup.SEQUENTIALLY);
+        TextChange textChangeOut = new TextChange();
+        textChangeOut.setChangeBehavior(TextChange.CHANGE_BEHAVIOR_OUT);
+        TextChange textChangeIn = new TextChange();
+        textChangeIn.setChangeBehavior(TextChange.CHANGE_BEHAVIOR_IN);
+        mTextChangeGroup2.addTransitions(textChangeOut, new Move(), textChangeIn);
+    }
+
+    public void sendMessage(View view) {
+        TransitionManager.beginDelayedTransition(mSceneRoot, mTransition);
+        int id = view.getId();
+        LayoutParams params = null;
+        switch (id) {
+            case R.id.button1:
+                params = new LayoutParams(200, 200);
+                mButton.setText("A");
+                mTextView.setText("1111111");
+                mImageView.setImageResource(R.drawable.self_portrait_square_100);
+                break;
+            case R.id.button2:
+                params = new LayoutParams(400, 200);
+                mButton.setText("B");
+                mTextView.setText("2222222");
+                mImageView.setImageResource(R.drawable.self_portrait_square_200);
+                break;
+            case R.id.button3:
+                params = new LayoutParams(200, 400);
+                mButton.setText("C");
+                mTextView.setText("3333333");
+                mImageView.setImageResource(R.drawable.self_portrait_square_400);
+                break;
+        }
+        mButton.setLayoutParams(params);
+    }
+
+    public void changeTransitionType(View view) {
+        int id = view.getId();
+        switch (id) {
+            case R.id.reveal:
+                mCrossfade.setFadeBehavior(Crossfade.FADE_BEHAVIOR_REVEAL);
+                mTransition = mCrossfadeGroup;
+                break;
+            case R.id.crossfade:
+                mCrossfade.setFadeBehavior(Crossfade.FADE_BEHAVIOR_CROSSFADE);
+                mTransition = mCrossfadeGroup;
+                break;
+            case R.id.inout:
+                mTransition = mInOutGroup;
+                break;
+            case R.id.textfade1:
+                mTransition = mTextChangeGroup1;
+                break;
+            case R.id.textfade2:
+                mTransition = mTextChangeGroup2;
+                break;
+        }
+    }
+}