summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.txt145
-rw-r--r--core/java/android/view/View.java30
-rw-r--r--core/java/android/view/transition/AutoTransition.java34
-rw-r--r--core/java/android/view/transition/Crossfade.java163
-rw-r--r--core/java/android/view/transition/Fade.java209
-rw-r--r--core/java/android/view/transition/Move.java299
-rw-r--r--core/java/android/view/transition/Recolor.java118
-rw-r--r--core/java/android/view/transition/Rotate.java67
-rw-r--r--core/java/android/view/transition/Scene.java191
-rw-r--r--core/java/android/view/transition/Slide.java70
-rw-r--r--core/java/android/view/transition/TextChange.java90
-rw-r--r--core/java/android/view/transition/Transition.java911
-rw-r--r--core/java/android/view/transition/TransitionGroup.java292
-rw-r--r--core/java/android/view/transition/TransitionInflater.java392
-rw-r--r--core/java/android/view/transition/TransitionManager.java261
-rw-r--r--core/java/android/view/transition/TransitionValues.java60
-rw-r--r--core/java/android/view/transition/Visibility.java243
-rw-r--r--core/java/android/view/transition/package.html25
-rw-r--r--core/res/res/values/attrs.xml22
-rw-r--r--core/res/res/values/public.xml4
-rw-r--r--tests/TransitionTests/Android.mk18
-rw-r--r--tests/TransitionTests/AndroidManifest.xml211
-rw-r--r--tests/TransitionTests/res/drawable-hdpi/icon.pngbin0 -> 4147 bytes
-rw-r--r--tests/TransitionTests/res/drawable-ldpi/icon.pngbin0 -> 1723 bytes
-rw-r--r--tests/TransitionTests/res/drawable-mdpi/icon.pngbin0 -> 2574 bytes
-rw-r--r--tests/TransitionTests/res/drawable-nodpi/arrow_thumbnail.pngbin0 -> 2214 bytes
-rw-r--r--tests/TransitionTests/res/drawable-nodpi/self_portrait_square.jpgbin0 -> 6380 bytes
-rw-r--r--tests/TransitionTests/res/drawable-nodpi/self_portrait_square_100.jpgbin0 -> 6643 bytes
-rw-r--r--tests/TransitionTests/res/drawable-nodpi/self_portrait_square_200.jpgbin0 -> 13430 bytes
-rw-r--r--tests/TransitionTests/res/layout/activity_login.xml86
-rw-r--r--tests/TransitionTests/res/layout/button_template.xml9
-rw-r--r--tests/TransitionTests/res/layout/changing_text_1.xml39
-rw-r--r--tests/TransitionTests/res/layout/changing_text_2.xml39
-rw-r--r--tests/TransitionTests/res/layout/clipping_text_1.xml22
-rw-r--r--tests/TransitionTests/res/layout/clipping_text_2.xml21
-rw-r--r--tests/TransitionTests/res/layout/contact_collapsed.xml51
-rw-r--r--tests/TransitionTests/res/layout/contact_expanded.xml41
-rw-r--r--tests/TransitionTests/res/layout/contacts_list.xml11
-rw-r--r--tests/TransitionTests/res/layout/crossfade.xml28
-rw-r--r--tests/TransitionTests/res/layout/crossfade_1.xml29
-rw-r--r--tests/TransitionTests/res/layout/fading_test.xml37
-rw-r--r--tests/TransitionTests/res/layout/fading_test_scene_2.xml34
-rw-r--r--tests/TransitionTests/res/layout/fading_test_simple.xml21
-rw-r--r--tests/TransitionTests/res/layout/fading_test_simple2.xml23
-rw-r--r--tests/TransitionTests/res/layout/incorrect_password.xml37
-rw-r--r--tests/TransitionTests/res/layout/instance_targets.xml38
-rw-r--r--tests/TransitionTests/res/layout/list_view_add_remove.xml14
-rw-r--r--tests/TransitionTests/res/layout/login_password.xml86
-rw-r--r--tests/TransitionTests/res/layout/main.xml11
-rw-r--r--tests/TransitionTests/res/layout/new_user.xml92
-rw-r--r--tests/TransitionTests/res/layout/overlay_test.xml22
-rw-r--r--tests/TransitionTests/res/layout/reparenting.xml17
-rw-r--r--tests/TransitionTests/res/layout/resources_test_layout.xml3
-rw-r--r--tests/TransitionTests/res/layout/results_screen.xml66
-rw-r--r--tests/TransitionTests/res/layout/search_screen.xml25
-rw-r--r--tests/TransitionTests/res/layout/success.xml36
-rw-r--r--tests/TransitionTests/res/layout/surface_texture_views.xml15
-rw-r--r--tests/TransitionTests/res/layout/unique_id_test.xml9
-rw-r--r--tests/TransitionTests/res/layout/username_taken.xml37
-rw-r--r--tests/TransitionTests/res/scene/incorrect_password_scene.xml2
-rw-r--r--tests/TransitionTests/res/scene/login_scene.xml2
-rw-r--r--tests/TransitionTests/res/scene/my_scene.xml3
-rw-r--r--tests/TransitionTests/res/scene/new_user_scene.xml2
-rw-r--r--tests/TransitionTests/res/scene/password_scene.xml2
-rw-r--r--tests/TransitionTests/res/scene/results_scene.xml3
-rw-r--r--tests/TransitionTests/res/scene/search_scene.xml3
-rw-r--r--tests/TransitionTests/res/scene/success_scene.xml2
-rw-r--r--tests/TransitionTests/res/scene/username_taken_scene.xml2
-rw-r--r--tests/TransitionTests/res/transition/colorizer_transition.xml6
-rw-r--r--tests/TransitionTests/res/transition/fader.xml1
-rw-r--r--tests/TransitionTests/res/transition/login_slider_transition.xml22
-rw-r--r--tests/TransitionTests/res/transition/login_transition_mgr.xml39
-rw-r--r--tests/TransitionTests/res/transition/mover.xml1
-rw-r--r--tests/TransitionTests/res/transition/mover_fader.xml5
-rw-r--r--tests/TransitionTests/res/transition/my_transition.xml20
-rw-r--r--tests/TransitionTests/res/transition/my_transition_mgr.xml6
-rw-r--r--tests/TransitionTests/res/values-v11/styles.xml11
-rw-r--r--tests/TransitionTests/res/values-v14/styles.xml12
-rw-r--r--tests/TransitionTests/res/values/strings.xml42
-rw-r--r--tests/TransitionTests/res/values/styles.xml20
-rw-r--r--tests/TransitionTests/src/com/android/transitiontests/ChangingText.java63
-rw-r--r--tests/TransitionTests/src/com/android/transitiontests/ClippingText.java66
-rw-r--r--tests/TransitionTests/src/com/android/transitiontests/ContactsExpansion.java123
-rw-r--r--tests/TransitionTests/src/com/android/transitiontests/CrossFadeDemo.java67
-rw-r--r--tests/TransitionTests/src/com/android/transitiontests/Demo0.java60
-rw-r--r--tests/TransitionTests/src/com/android/transitiontests/Demo1.java88
-rw-r--r--tests/TransitionTests/src/com/android/transitiontests/Demo2.java79
-rw-r--r--tests/TransitionTests/src/com/android/transitiontests/Demo3.java65
-rw-r--r--tests/TransitionTests/src/com/android/transitiontests/Demo4.java74
-rw-r--r--tests/TransitionTests/src/com/android/transitiontests/Demo5.java54
-rw-r--r--tests/TransitionTests/src/com/android/transitiontests/FadingTest.java74
-rw-r--r--tests/TransitionTests/src/com/android/transitiontests/HitRectBug.java92
-rw-r--r--tests/TransitionTests/src/com/android/transitiontests/InstanceTargets.java69
-rw-r--r--tests/TransitionTests/src/com/android/transitiontests/ListViewAddRemove.java167
-rw-r--r--tests/TransitionTests/src/com/android/transitiontests/ListViewAddRemoveNoTransition.java104
-rw-r--r--tests/TransitionTests/src/com/android/transitiontests/LoginActivity.java105
-rw-r--r--tests/TransitionTests/src/com/android/transitiontests/LoginActivityFromResources.java94
-rw-r--r--tests/TransitionTests/src/com/android/transitiontests/OverlayTest.java55
-rw-r--r--tests/TransitionTests/src/com/android/transitiontests/Reparenting.java76
-rw-r--r--tests/TransitionTests/src/com/android/transitiontests/ResourceLoadingTest.java76
-rw-r--r--tests/TransitionTests/src/com/android/transitiontests/ScenesTestAutoTargets.java66
-rw-r--r--tests/TransitionTests/src/com/android/transitiontests/ScenesTestAutoTransition.java57
-rw-r--r--tests/TransitionTests/src/com/android/transitiontests/ScenesTestAutoTransition2.java55
-rw-r--r--tests/TransitionTests/src/com/android/transitiontests/ScenesTestv21.java75
-rw-r--r--tests/TransitionTests/src/com/android/transitiontests/SequenceTest.java74
-rw-r--r--tests/TransitionTests/src/com/android/transitiontests/SequenceTestSimple.java71
-rw-r--r--tests/TransitionTests/src/com/android/transitiontests/SurfaceAndTextureViews.java195
-rw-r--r--tests/TransitionTests/src/com/android/transitiontests/UniqueIds.java86
-rw-r--r--tools/aapt/Resource.cpp51
109 files changed, 7441 insertions, 0 deletions
diff --git a/api/current.txt b/api/current.txt
index 5b6e3a1..c1a02da 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -512,6 +512,7 @@ package android {
field public static final int freezesText = 16843116; // 0x101016c
field public static final int fromAlpha = 16843210; // 0x10101ca
field public static final int fromDegrees = 16843187; // 0x10101b3
+ field public static final int fromScene = 16843737; // 0x10103d9
field public static final int fromXDelta = 16843206; // 0x10101c6
field public static final int fromXScale = 16843202; // 0x10101c2
field public static final int fromYDelta = 16843208; // 0x10101c8
@@ -1012,6 +1013,7 @@ package android {
field public static final int targetActivity = 16843266; // 0x1010202
field public static final int targetClass = 16842799; // 0x101002f
field public static final int targetDescriptions = 16843680; // 0x10103a0
+ field public static final int targetID = 16843736; // 0x10103d8
field public static final int targetPackage = 16842785; // 0x1010021
field public static final int targetSdkVersion = 16843376; // 0x1010270
field public static final int taskAffinity = 16842770; // 0x1010012
@@ -1100,6 +1102,7 @@ package android {
field public static final int titleTextStyle = 16843512; // 0x10102f8
field public static final int toAlpha = 16843211; // 0x10101cb
field public static final int toDegrees = 16843188; // 0x10101b4
+ field public static final int toScene = 16843738; // 0x10103da
field public static final int toXDelta = 16843207; // 0x10101c7
field public static final int toXScale = 16843203; // 0x10101c3
field public static final int toYDelta = 16843209; // 0x10101c9
@@ -1114,6 +1117,7 @@ package android {
field public static final int transcriptMode = 16843008; // 0x1010100
field public static final int transformPivotX = 16843552; // 0x1010320
field public static final int transformPivotY = 16843553; // 0x1010321
+ field public static final int transition = 16843739; // 0x10103db
field public static final int translationX = 16843554; // 0x1010322
field public static final int translationY = 16843555; // 0x1010323
field public static final int type = 16843169; // 0x10101a1
@@ -25876,6 +25880,7 @@ package android.view {
method public java.lang.CharSequence getContentDescription();
method public final android.content.Context getContext();
method protected android.view.ContextMenu.ContextMenuInfo getContextMenuInfo();
+ method public android.view.transition.Scene getCurrentScene();
method public static int getDefaultSize(int, int);
method public android.view.Display getDisplay();
method public final int[] getDrawableState();
@@ -28023,6 +28028,146 @@ package android.view.textservice {
}
+package android.view.transition {
+
+ public class AutoTransition extends android.view.transition.TransitionGroup {
+ ctor public AutoTransition();
+ }
+
+ public class Crossfade extends android.view.transition.Transition {
+ ctor public Crossfade();
+ method protected void captureValues(android.view.transition.TransitionValues, boolean);
+ method protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
+ }
+
+ public class Fade extends android.view.transition.Visibility {
+ ctor public Fade();
+ ctor public Fade(int);
+ field public static final int IN = 1; // 0x1
+ field public static final int OUT = 2; // 0x2
+ }
+
+ public class Move extends android.view.transition.Transition {
+ ctor public Move();
+ method protected void captureValues(android.view.transition.TransitionValues, boolean);
+ method protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
+ method public void setReparent(boolean);
+ method public void setResizeClip(boolean);
+ }
+
+ public class Recolor extends android.view.transition.Transition {
+ ctor public Recolor();
+ method protected void captureValues(android.view.transition.TransitionValues, boolean);
+ method protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
+ }
+
+ public class Rotate extends android.view.transition.Transition {
+ ctor public Rotate();
+ method protected void captureValues(android.view.transition.TransitionValues, boolean);
+ method protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
+ }
+
+ public final class Scene {
+ ctor public Scene(android.view.ViewGroup);
+ ctor public Scene(android.view.ViewGroup, int, android.content.Context);
+ ctor public Scene(android.view.ViewGroup, android.view.ViewGroup);
+ method public void enter();
+ method public void exit();
+ method public android.view.ViewGroup getSceneRoot();
+ method public void setEnterAction(java.lang.Runnable);
+ method public void setExitAction(java.lang.Runnable);
+ }
+
+ public class Slide extends android.view.transition.Visibility {
+ ctor public Slide();
+ }
+
+ public class TextChange extends android.view.transition.Transition {
+ ctor public TextChange();
+ method protected void captureValues(android.view.transition.TransitionValues, boolean);
+ method protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
+ }
+
+ public abstract class Transition {
+ ctor public Transition();
+ method public void addListener(android.view.transition.Transition.TransitionListener);
+ method protected void cancelTransition();
+ method protected abstract void captureValues(android.view.transition.TransitionValues, boolean);
+ method public long getDuration();
+ method public android.animation.TimeInterpolator getInterpolator();
+ method public java.util.ArrayList<android.view.transition.Transition.TransitionListener> getListeners();
+ method public long getStartDelay();
+ method public int[] getTargetIds();
+ method public android.view.View[] getTargets();
+ method protected void onTransitionCancel();
+ method protected void onTransitionEnd();
+ method protected void onTransitionStart();
+ method protected abstract android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
+ method protected boolean prePlay(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
+ method public void removeListener(android.view.transition.Transition.TransitionListener);
+ method public android.view.transition.Transition setDuration(long);
+ method public void setInterpolator(android.animation.TimeInterpolator);
+ method public void setStartDelay(long);
+ method public android.view.transition.Transition setTargetIds(int...);
+ method public android.view.transition.Transition setTargets(android.view.View...);
+ }
+
+ public static abstract interface Transition.TransitionListener {
+ method public abstract void onTransitionCancel(android.view.transition.Transition);
+ method public abstract void onTransitionEnd(android.view.transition.Transition);
+ method public abstract void onTransitionStart(android.view.transition.Transition);
+ }
+
+ public class TransitionGroup extends android.view.transition.Transition {
+ ctor public TransitionGroup();
+ ctor public TransitionGroup(int);
+ method public void addTransitions(android.view.transition.Transition...);
+ method protected void captureValues(android.view.transition.TransitionValues, boolean);
+ method protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
+ method public void removeTransition(android.view.transition.Transition);
+ method public void setOrdering(int);
+ field public static final int SEQUENTIALLY = 1; // 0x1
+ field public static final int TOGETHER = 0; // 0x0
+ }
+
+ public class TransitionInflater {
+ method public static android.view.transition.TransitionInflater from(android.content.Context);
+ method public android.view.transition.Scene inflateScene(int, android.view.ViewGroup);
+ method public android.view.transition.Transition inflateTransition(int);
+ method public android.view.transition.TransitionManager inflateTransitionManager(int, android.view.ViewGroup);
+ }
+
+ public class TransitionManager {
+ ctor public TransitionManager();
+ method public android.view.transition.Transition getDefaultTransition();
+ method public static void go(android.view.transition.Scene);
+ method public static void go(android.view.transition.Scene, android.view.transition.Transition);
+ method public static void go(android.view.ViewGroup, java.lang.Runnable);
+ method public static void go(android.view.ViewGroup, java.lang.Runnable, android.view.transition.Transition);
+ method public void setDefaultTransition(android.view.transition.Transition);
+ method public void setTransition(android.view.transition.Scene, android.view.transition.Transition);
+ method public void setTransition(android.view.transition.Scene, android.view.transition.Scene, android.view.transition.Transition);
+ method public void transitionTo(android.view.transition.Scene);
+ }
+
+ public class TransitionValues {
+ ctor public TransitionValues();
+ field public final java.util.HashMap values;
+ field public android.view.View view;
+ }
+
+ public abstract class Visibility extends android.view.transition.Transition {
+ ctor public Visibility();
+ method protected android.animation.Animator appear(android.view.ViewGroup, android.view.View, int, android.view.View, int);
+ method protected void captureValues(android.view.transition.TransitionValues, boolean);
+ method protected android.animation.Animator disappear(android.view.ViewGroup, android.view.View, int, android.view.View, int);
+ method protected android.animation.Animator play(android.view.ViewGroup, android.view.transition.TransitionValues, android.view.transition.TransitionValues);
+ method protected boolean preAppear(android.view.ViewGroup, android.view.View, int, android.view.View, int);
+ method protected boolean preDisappear(android.view.ViewGroup, android.view.View, int, android.view.View, int);
+ }
+
+}
+
package android.webkit {
public class ConsoleMessage {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 2903b6f..7aa2cbf 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -73,6 +73,7 @@ import android.view.animation.Transformation;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
+import android.view.transition.Scene;
import android.widget.ScrollBarDrawable;
import static android.os.Build.VERSION_CODES.*;
@@ -1572,6 +1573,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
protected Object mTag;
+ private Scene mCurrentScene = null;
+
// for mPrivateFlags:
/** {@hide} */
static final int PFLAG_WANTS_FOCUS = 0x00000001;
@@ -12037,6 +12040,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mCurrentAnimation = null;
+ mCurrentScene = null;
+
resetAccessibilityStateChanged();
}
@@ -17758,6 +17763,31 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * Set the current Scene that this view is in. The current scene is set only
+ * on the root view of a scene, not for every view in that hierarchy. This
+ * information is used by Scene to determine whether there is a previous
+ * scene which should be exited before the new scene is entered.
+ *
+ * @param scene The new scene being set on the view
+ *
+ * @hide
+ */
+ public void setCurrentScene(Scene scene) {
+ mCurrentScene = scene;
+ }
+
+ /**
+ * Gets the current {@link Scene} set on this view. A scene is set on a view
+ * only if that view is the scene root.
+ *
+ * @return The current Scene set on this view. A value of null indicates that
+ * no Scene is current set.
+ */
+ public Scene getCurrentScene() {
+ return mCurrentScene;
+ }
+
+ /**
* Interface definition for a callback to be invoked when a hardware key event is
* dispatched to this view. The callback will be invoked before the key event is
* given to the view. This is only useful for hardware keyboards; a software input
diff --git a/core/java/android/view/transition/AutoTransition.java b/core/java/android/view/transition/AutoTransition.java
new file mode 100644
index 0000000..d94cf2c
--- /dev/null
+++ b/core/java/android/view/transition/AutoTransition.java
@@ -0,0 +1,34 @@
+/*
+ * 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 android.view.transition;
+
+/**
+ * Utility class for creating a default transition that automatically fades,
+ * moves, and resizes views during a scene change.
+ */
+public class AutoTransition extends TransitionGroup {
+
+ /**
+ * Constructs an AutoTransition object, which is a TransitionGroup which
+ * first fades out disappearing targets, then moves and resizes existing
+ * targets, and finally fades in appearing targets.
+ *
+ */
+ public AutoTransition() {
+ setOrdering(SEQUENTIALLY);
+ addTransitions(new Fade(Fade.OUT), new Move(), new Fade(Fade.IN));
+ }
+}
diff --git a/core/java/android/view/transition/Crossfade.java b/core/java/android/view/transition/Crossfade.java
new file mode 100644
index 0000000..babf58f
--- /dev/null
+++ b/core/java/android/view/transition/Crossfade.java
@@ -0,0 +1,163 @@
+/*
+ * 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 android.view.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.RectEvaluator;
+import android.animation.ValueAnimator;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+import android.view.SurfaceView;
+import android.view.TextureView;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.HashMap;
+
+/**
+ * This transition captures bitmap representations of target views before and
+ * after the scene change and fades between them.
+ *
+ * <p>Note: This transition is not compatible with {@link TextureView}
+ * or {@link SurfaceView}.</p>
+ */
+public class Crossfade extends Transition {
+ // TODO: Add a hook that lets a Transition call user code to query whether it should run on
+ // a given target view. This would save bitmap comparisons in this transition, for example.
+
+ private static final String LOG_TAG = "Crossfade";
+
+ private static final String PROPNAME_BITMAP = "android:crossfade:bitmap";
+ private static final String PROPNAME_DRAWABLE = "android:crossfade:drawable";
+ private static final String PROPNAME_BOUNDS = "android:crossfade:bounds";
+
+ private static RectEvaluator sRectEvaluator = new RectEvaluator();
+
+ @Override
+ protected boolean prePlay(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null) {
+ return false;
+ }
+ final View view = startValues.view;
+ HashMap<String, Object> startVals = startValues.values;
+ HashMap<String, Object> endVals = endValues.values;
+ Bitmap startBitmap = (Bitmap) startVals.get(PROPNAME_BITMAP);
+ Bitmap endBitmap = (Bitmap) endVals.get(PROPNAME_BITMAP);
+ Drawable startDrawable = (Drawable) startVals.get(PROPNAME_DRAWABLE);
+ Drawable endDrawable = (Drawable) endVals.get(PROPNAME_DRAWABLE);
+ if (Transition.DBG) {
+ Log.d(LOG_TAG, "StartBitmap.sameAs(endBitmap) = " + startBitmap.sameAs(endBitmap) +
+ " for start, end: " + startBitmap + ", " + endBitmap);
+ }
+ if (startDrawable != null && endDrawable != null && !startBitmap.sameAs(endBitmap)) {
+ view.getOverlay().add(endDrawable);
+ view.getOverlay().add(startDrawable);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ protected Animator play(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null) {
+ return null;
+ }
+ HashMap<String, Object> startVals = startValues.values;
+ HashMap<String, Object> endVals = endValues.values;
+
+ final View view = endValues.view;
+ Rect startBounds = (Rect) startVals.get(PROPNAME_BOUNDS);
+ Rect endBounds = (Rect) endVals.get(PROPNAME_BOUNDS);
+ final BitmapDrawable startDrawable = (BitmapDrawable) startVals.get(PROPNAME_DRAWABLE);
+ final BitmapDrawable endDrawable = (BitmapDrawable) endVals.get(PROPNAME_DRAWABLE);
+
+ // The transition works by placing the end drawable under the start drawable and
+ // 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);
+ anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ // TODO: some way to auto-invalidate views based on drawable changes? callbacks?
+ view.invalidate(startDrawable.getBounds());
+ }
+ });
+ if (Transition.DBG) {
+ Log.d(LOG_TAG, "Crossfade: created anim " + anim + " for start, end values " +
+ startValues + ", " + endValues);
+ }
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ view.getOverlay().remove(startDrawable);
+ view.getOverlay().remove(endDrawable);
+ }
+ });
+ AnimatorSet set = new AnimatorSet();
+ set.playTogether(anim);
+ if (!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);
+ }
+ return set;
+ }
+
+ @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()));
+
+ if (Transition.DBG) {
+ Log.d(LOG_TAG, "Captured bounds " + values.values.get(PROPNAME_BOUNDS) + ": start = " +
+ start);
+ }
+ Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),
+ Bitmap.Config.ARGB_8888);
+ if (view instanceof TextureView) {
+ bitmap = ((TextureView) view).getBitmap();
+ } else {
+ Canvas c = new Canvas(bitmap);
+ view.draw(c);
+ }
+ values.values.put(PROPNAME_BITMAP, bitmap);
+ // 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());
+ values.values.put(PROPNAME_DRAWABLE, drawable);
+ }
+
+}
diff --git a/core/java/android/view/transition/Fade.java b/core/java/android/view/transition/Fade.java
new file mode 100644
index 0000000..8e4909d
--- /dev/null
+++ b/core/java/android/view/transition/Fade.java
@@ -0,0 +1,209 @@
+/*
+ * 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 android.view.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * This transition tracks changes to the visibility of target views in the
+ * start and end scenes and fades views in or out when they become visible
+ * or non-visible. Visibility is determined by both the
+ * {@link View#setVisibility(int)} state of the view as well as whether it
+ * is parented in the current view hierarchy.
+ */
+public class Fade extends Visibility {
+
+ private static final String LOG_TAG = "Fade";
+
+ /**
+ * Fading mode used in {@link #Fade(int)} to make the transition
+ * operate on targets that are appearing. Maybe be combined with
+ * {@link #OUT} to fade both in and out.
+ */
+ public static final int IN = 0x1;
+ /**
+ * Fading mode used in {@link #Fade(int)} to make the transition
+ * operate on targets that are disappearing. Maybe be combined with
+ * {@link #IN} to fade both in and out.
+ */
+ public static final int OUT = 0x2;
+
+ private int mFadingMode;
+
+ /**
+ * Constructs a Fade transition that will fade targets in and out.
+ */
+ public Fade() {
+ this(IN | OUT);
+ }
+
+ /**
+ * Constructs a Fade transition that will fade targets in
+ * and/or out, according to the value of fadingMode.
+ *
+ * @param fadingMode The behavior of this transition, a combination of
+ * {@link #IN} and {@link #OUT}.
+ */
+ public Fade(int fadingMode) {
+ mFadingMode = fadingMode;
+ }
+
+ /**
+ * Utility method to handle creating and running the Animator.
+ */
+ private Animator runAnimation(View view, float startAlpha, float endAlpha,
+ Animator.AnimatorListener listener) {
+ final ObjectAnimator anim = ObjectAnimator.ofFloat(view, "alpha", startAlpha, endAlpha);
+ if (listener != null) {
+ anim.addListener(listener);
+ }
+ // TODO: Maybe extract a method into Transition to run an animation that handles the
+ // duration/startDelay stuff for all subclasses.
+ return anim;
+ }
+
+ @Override
+ protected boolean preAppear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) {
+ if ((mFadingMode & IN) != IN) {
+ return false;
+ }
+ endView.setAlpha(0);
+ return true;
+ }
+
+ @Override
+ protected Animator appear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) {
+ if ((mFadingMode & IN) != IN) {
+ return null;
+ }
+ // TODO: hack - retain original value from before preAppear
+ return runAnimation(endView, 0, 1, null);
+ // TODO: end listener to make sure we end at 1 no matter what
+ }
+
+ @Override
+ protected boolean preDisappear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) {
+ if ((mFadingMode & OUT) != OUT) {
+ return false;
+ }
+ if (Transition.DBG) {
+ Log.d(LOG_TAG, "Fade.predisappear: startView, startVis, endView, endVis = " +
+ startView + ", " + startVisibility + ", " + endView + ", " + endVisibility);
+ }
+ View view;
+ View overlayView = null;
+ View viewToKeep = null;
+ if (endView == null) {
+ // view was removed: add the start view to the Overlay
+ view = startView;
+ overlayView = view;
+ } else {
+ // visibility change
+ if (endVisibility == View.INVISIBLE) {
+ view = endView;
+ viewToKeep = view;
+ } else {
+ // Becoming GONE
+ if (startView == endView) {
+ view = endView;
+ viewToKeep = view;
+ } else {
+ view = startView;
+ overlayView = view;
+ }
+ }
+ }
+ // TODO: add automatic facility to Visibility superclass for keeping views around
+ if (overlayView != null) {
+ // TODO: Need to do this for general case of adding to overlay
+ sceneRoot.getOverlay().add(overlayView);
+ return true;
+ }
+ if (viewToKeep != null) {
+ // TODO: find a different way to do this, like just changing the view to be
+ // VISIBLE for the duration of the transition
+ viewToKeep.setVisibility((View.VISIBLE));
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected Animator disappear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) {
+ if ((mFadingMode & OUT) != OUT) {
+ return null;
+ }
+ if (Transition.DBG) {
+ Log.d(LOG_TAG, "Fade.disappear: startView, startVis, endView, endVis = " +
+ startView + ", " + startVisibility + ", " + endView + ", " + endVisibility);
+ }
+ View view;
+ View overlayView = null;
+ View viewToKeep = null;
+ final int finalVisibility = endVisibility;
+ if (endView == null) {
+ // view was removed: add the start view to the Overlay
+ view = startView;
+ overlayView = view;
+ } else {
+ // visibility change
+ if (endVisibility == View.INVISIBLE) {
+ view = endView;
+ viewToKeep = view;
+ } else {
+ // Becoming GONE
+ if (startView == endView) {
+ view = endView;
+ viewToKeep = view;
+ } else {
+ view = startView;
+ overlayView = view;
+ }
+ }
+ }
+ // TODO: add automatic facility to Visibility superclass for keeping views around
+ final float startAlpha = view.getAlpha();
+ float endAlpha = 0;
+ final View finalView = view;
+ final View finalOverlayView = overlayView;
+ final View finalViewToKeep = viewToKeep;
+ final ViewGroup finalSceneRoot = sceneRoot;
+ final Animator.AnimatorListener endListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ finalView.setAlpha(startAlpha);
+ // TODO: restore view offset from overlay repositioning
+ if (finalViewToKeep != null) {
+ finalViewToKeep.setVisibility(finalVisibility);
+ }
+ if (finalOverlayView != null) {
+ finalSceneRoot.getOverlay().remove(finalOverlayView);
+ }
+ }
+ };
+ return runAnimation(view, startAlpha, endAlpha, endListener);
+ }
+
+} \ No newline at end of file
diff --git a/core/java/android/view/transition/Move.java b/core/java/android/view/transition/Move.java
new file mode 100644
index 0000000..3bd57bd
--- /dev/null
+++ b/core/java/android/view/transition/Move.java
@@ -0,0 +1,299 @@
+/*
+ * 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 android.view.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.RectEvaluator;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.HashMap;
+
+/**
+ * This transition captures the layout bounds of target views before and after
+ * the scene change and animates those changes during the transition.
+ */
+public class Move extends Transition {
+
+ private static final String PROPNAME_BOUNDS = "android:move:bounds";
+ private static final String PROPNAME_PARENT = "android:move:parent";
+ private static final String PROPNAME_WINDOW_X = "android:move:windowX";
+ private static final String PROPNAME_WINDOW_Y = "android:move:windowY";
+ int[] tempLocation = new int[2];
+ boolean mResizeClip = false;
+ boolean mReparent = false;
+
+ private static RectEvaluator sRectEvaluator = new RectEvaluator();
+
+ public void setResizeClip(boolean resizeClip) {
+ mResizeClip = resizeClip;
+ }
+
+ /**
+ * Setting this flag tells Move to track the before/after parent
+ * of every view using this transition. The flag is not enabled by
+ * default because it requires the parent instances to be the same
+ * in the two scenes or else all parents must use ids to allow
+ * the transition to determine which parents are the same.
+ *
+ * @param reparent true if the transition should track the parent
+ * container of target views and animate parent changes.
+ */
+ public void setReparent(boolean reparent) {
+ mReparent = reparent;
+ }
+
+ @Override
+ protected void captureValues(TransitionValues values, boolean start) {
+ View view = values.view;
+ values.values.put(PROPNAME_BOUNDS, new Rect(view.getLeft(), view.getTop(),
+ view.getRight(), view.getBottom()));
+ values.values.put(PROPNAME_PARENT, values.view.getParent());
+ values.view.getLocationInWindow(tempLocation);
+ values.values.put(PROPNAME_WINDOW_X, tempLocation[0]);
+ values.values.put(PROPNAME_WINDOW_Y, tempLocation[1]);
+ }
+
+ @Override
+ protected Animator play(final ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null) {
+ return null;
+ }
+ final View view = endValues.view;
+ if (view.getParent() == null) {
+ // TODO: Might want to make it possible to Move an disappearing view.
+ // This workaround is here because if a parallel Fade is not running on the view
+ // Then it won't get added to the hierarchy and the animator below will not fire,
+ // causing the transition to not end
+ return null;
+ }
+ // TODO: need to handle non-VG case?
+ ViewGroup startParent = (ViewGroup) startValues.values.get(PROPNAME_PARENT);
+ ViewGroup endParent = (ViewGroup) endValues.values.get(PROPNAME_PARENT);
+ if (startParent == null || endParent == null) {
+ return null;
+ }
+ boolean parentsEqual = (startParent == endParent) ||
+ (startParent.getId() == endParent.getId());
+ if (!mReparent || parentsEqual) {
+ // Common case - view belongs to the same layout before/after. Just animate its bounds
+ Rect startBounds = (Rect) startValues.values.get(PROPNAME_BOUNDS);
+ Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
+ int startLeft = startBounds.left;
+ int endLeft = endBounds.left;
+ int startTop = startBounds.top;
+ int endTop = endBounds.top;
+ int startRight = startBounds.right;
+ int endRight = endBounds.right;
+ int startBottom = startBounds.bottom;
+ int endBottom = endBounds.bottom;
+ int startWidth = startRight - startLeft;
+ int startHeight = startBottom - startTop;
+ int endWidth = endRight - endLeft;
+ int endHeight = endBottom - endTop;
+ int numChanges = 0;
+ if (startWidth != 0 && startHeight != 0 && endWidth != 0 && endHeight != 0) {
+ if (startLeft != endLeft) ++numChanges;
+ if (startTop != endTop) ++numChanges;
+ if (startRight != endRight) ++numChanges;
+ if (startBottom != endBottom) ++numChanges;
+ }
+ if (numChanges > 0) {
+ if (!mResizeClip) {
+ PropertyValuesHolder pvh[] = new PropertyValuesHolder[numChanges];
+ int pvhIndex = 0;
+ if (startLeft != endLeft) {
+ pvh[pvhIndex++] = PropertyValuesHolder.ofInt("left", startLeft, endLeft);
+ }
+ if (startTop != endTop) {
+ pvh[pvhIndex++] = PropertyValuesHolder.ofInt("top", startTop, endTop);
+ }
+ if (startRight != endRight) {
+ pvh[pvhIndex++] = PropertyValuesHolder.ofInt("right",
+ startRight, endRight);
+ }
+ if (startBottom != endBottom) {
+ pvh[pvhIndex++] = PropertyValuesHolder.ofInt("bottom",
+ startBottom, endBottom);
+ }
+ ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(view, pvh);
+ if (view.getParent() instanceof ViewGroup) {
+ final ViewGroup parent = (ViewGroup) view.getParent();
+ parent.suppressLayout(true);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ parent.suppressLayout(false);
+ }
+ });
+ }
+ return anim;
+ } else {
+ // Animate location with translationX/Y and size with clip bounds
+ float transXDelta = endLeft - startLeft;
+ float transYDelta = endTop - startTop;
+ int widthDelta = endWidth - startWidth;
+ int heightDelta = endHeight - startHeight;
+ numChanges = 0;
+ if (transXDelta != 0) numChanges++;
+ if (transYDelta != 0) numChanges++;
+ if (widthDelta != 0 || heightDelta != 0) numChanges++;
+ PropertyValuesHolder pvh[] = new PropertyValuesHolder[numChanges];
+ int pvhIndex = 0;
+ if (transXDelta != 0) {
+ pvh[pvhIndex++] = PropertyValuesHolder.ofFloat("translationX",
+ view.getTranslationX(), 0);
+ }
+ if (transYDelta != 0) {
+ pvh[pvhIndex++] = PropertyValuesHolder.ofFloat("translationY",
+ view.getTranslationY(), 0);
+ }
+ if (widthDelta != 0 || heightDelta != 0) {
+ Rect tempStartBounds = new Rect(0, 0, startWidth, startHeight);
+ Rect tempEndBounds = new Rect(0, 0, endWidth, endHeight);
+ pvh[pvhIndex++] = PropertyValuesHolder.ofObject("clipBounds",
+ sRectEvaluator, tempStartBounds, tempEndBounds);
+ }
+ ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(view, pvh);
+ if (view.getParent() instanceof ViewGroup) {
+ final ViewGroup parent = (ViewGroup) view.getParent();
+ parent.suppressLayout(true);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ parent.suppressLayout(false);
+ }
+ });
+ }
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ view.setClipBounds(null);
+ }
+ });
+ return anim;
+ }
+ }
+ } else {
+ return (ObjectAnimator) endValues.values.get("drawableAnim");
+ }
+ return null;
+ }
+
+ @Override
+ protected boolean prePlay(final ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null) {
+ return false;
+ }
+ HashMap<String, Object> startParentVals = startValues.values;
+ HashMap<String, Object> endParentVals = endValues.values;
+ ViewGroup startParent = (ViewGroup) startParentVals.get(PROPNAME_PARENT);
+ ViewGroup endParent = (ViewGroup) endParentVals.get(PROPNAME_PARENT);
+ if (startParent == null || endParent == null) {
+ return false;
+ }
+ final View view = endValues.view;
+ boolean parentsEqual = (startParent == endParent) ||
+ (startParent.getId() == endParent.getId());
+ // TODO: Might want reparenting to be separate/subclass transition, or at least
+ // triggered by a property on Move. Otherwise, we're forcing the requirement that
+ // all parents in layouts have IDs to avoid layout-inflation resulting in a side-effect
+ // of reparenting the views.
+ if (!mReparent || parentsEqual) {
+ Rect startBounds = (Rect) startValues.values.get(PROPNAME_BOUNDS);
+ Rect endBounds = (Rect) endValues.values.get(PROPNAME_BOUNDS);
+ int startLeft = startBounds.left;
+ int endLeft = endBounds.left;
+ int startTop = startBounds.top;
+ int endTop = endBounds.top;
+ int startRight = startBounds.right;
+ int endRight = endBounds.right;
+ int startBottom = startBounds.bottom;
+ int endBottom = endBounds.bottom;
+ int startWidth = startRight - startLeft;
+ int startHeight = startBottom - startTop;
+ int endWidth = endRight - endLeft;
+ int endHeight = endBottom - endTop;
+ int numChanges = 0;
+ if (startWidth != 0 && startHeight != 0 && endWidth != 0 && endHeight != 0) {
+ if (startLeft != endLeft) ++numChanges;
+ if (startTop != endTop) ++numChanges;
+ if (startRight != endRight) ++numChanges;
+ if (startBottom != endBottom) ++numChanges;
+ }
+ if (numChanges > 0) {
+ if (!mResizeClip) {
+ if (startLeft != endLeft) view.setLeft(startLeft);
+ if (startTop != endTop) view.setTop(startTop);
+ if (startRight != endRight) view.setRight(startRight);
+ if (startBottom != endBottom) view.setBottom(startBottom);
+ } else {
+ if (startWidth != endWidth) view.setRight(endLeft +
+ Math.max(startWidth, endWidth));
+ if (startHeight != endHeight) view.setBottom(endTop +
+ Math.max(startHeight, endHeight));
+ // TODO: don't clobber TX/TY
+ if (startLeft != endLeft) view.setTranslationX(startLeft - endLeft);
+ if (startTop != endTop) view.setTranslationY(startTop - endTop);
+ }
+ return true;
+ }
+ } else {
+ int startX = (Integer) startValues.values.get(PROPNAME_WINDOW_X);
+ int startY = (Integer) startValues.values.get(PROPNAME_WINDOW_Y);
+ int endX = (Integer) endValues.values.get(PROPNAME_WINDOW_X);
+ int endY = (Integer) endValues.values.get(PROPNAME_WINDOW_Y);
+ // TODO: also handle size changes: check bounds and animate size changes
+ if (startX != endX || startY != endY) {
+ sceneRoot.getLocationInWindow(tempLocation);
+ Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(),
+ Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+ view.draw(canvas);
+ final BitmapDrawable drawable = new BitmapDrawable(bitmap);
+ view.setVisibility(View.INVISIBLE);
+ sceneRoot.getOverlay().add(drawable);
+ Rect startBounds = new Rect(startX - tempLocation[0], startY - tempLocation[1],
+ startX - tempLocation[0] + view.getWidth(),
+ startY - tempLocation[1] + view.getHeight());
+ Rect endBounds = new Rect(endX - tempLocation[0], endY - tempLocation[1],
+ endX - tempLocation[0] + view.getWidth(),
+ endY - tempLocation[1] + view.getHeight());
+ ObjectAnimator anim = ObjectAnimator.ofObject(drawable, "bounds",
+ sRectEvaluator, startBounds, endBounds);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ sceneRoot.getOverlay().remove(drawable);
+ view.setVisibility(View.VISIBLE);
+ }
+ });
+ endParentVals.put("drawableAnim", anim);
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/core/java/android/view/transition/Recolor.java b/core/java/android/view/transition/Recolor.java
new file mode 100644
index 0000000..7048be9
--- /dev/null
+++ b/core/java/android/view/transition/Recolor.java
@@ -0,0 +1,118 @@
+/*
+ * 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 android.view.transition;
+
+import android.animation.Animator;
+import android.animation.ArgbEvaluator;
+import android.animation.ObjectAnimator;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import java.util.HashMap;
+
+/**
+ * This transition tracks changes during scene changes to the
+ * {@link View#setBackground(android.graphics.drawable.Drawable) background}
+ * property of its target views (when the background is a
+ * {@link ColorDrawable}, as well as the
+ * {@link TextView#setTextColor(android.content.res.ColorStateList)
+ * color} of the text for target TextViews. If the color changes between
+ * scenes, the color change is animated.
+ */
+public class Recolor extends Transition {
+
+ private static final String PROPNAME_BACKGROUND = "android:recolor:background";
+ private static final String PROPNAME_TEXT_COLOR = "android:recolor:textColor";
+
+ @Override
+ protected void captureValues(TransitionValues values, boolean start) {
+ values.values.put(PROPNAME_BACKGROUND, values.view.getBackground());
+ if (values.view instanceof TextView) {
+ values.values.put(PROPNAME_TEXT_COLOR, ((TextView)values.view).getCurrentTextColor());
+ }
+ }
+
+ @Override
+ protected boolean prePlay(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null) {
+ return false;
+ }
+ final View view = endValues.view;
+ Drawable startBackground = (Drawable) startValues.values.get(PROPNAME_BACKGROUND);
+ Drawable endBackground = (Drawable) endValues.values.get(PROPNAME_BACKGROUND);
+ boolean changed = false;
+ if (startBackground instanceof ColorDrawable && endBackground instanceof ColorDrawable) {
+ ColorDrawable startColor = (ColorDrawable) startBackground;
+ ColorDrawable endColor = (ColorDrawable) endBackground;
+ if (startColor.getColor() != endColor.getColor()) {
+ endColor.setColor(startColor.getColor());
+ changed = true;
+ }
+ }
+ if (view instanceof TextView) {
+ TextView textView = (TextView) view;
+ int start = (Integer) startValues.values.get(PROPNAME_TEXT_COLOR);
+ int end = (Integer) endValues.values.get(PROPNAME_TEXT_COLOR);
+ if (start != end) {
+ textView.setTextColor(end);
+ changed = true;
+ }
+ }
+ return changed;
+ }
+
+ @Override
+ protected Animator play(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null) {
+ return null;
+ }
+ ObjectAnimator anim = null;
+ final View view = endValues.view;
+ HashMap<String, Object> startVals = startValues.values;
+ HashMap<String, Object> endVals = endValues.values;
+ Drawable startBackground = (Drawable) startVals.get(PROPNAME_BACKGROUND);
+ Drawable endBackground = (Drawable) endVals.get(PROPNAME_BACKGROUND);
+ if (startBackground instanceof ColorDrawable && endBackground instanceof ColorDrawable) {
+ ColorDrawable startColor = (ColorDrawable) startBackground;
+ ColorDrawable endColor = (ColorDrawable) endBackground;
+ if (startColor.getColor() != endColor.getColor()) {
+ anim = ObjectAnimator.ofObject(endBackground, "color",
+ new ArgbEvaluator(), startColor.getColor(), endColor.getColor());
+ if (getStartDelay() > 0) {
+ endColor.setColor(startColor.getColor());
+ }
+ }
+ }
+ if (view instanceof TextView) {
+ TextView textView = (TextView) view;
+ int start = (Integer) startValues.values.get(PROPNAME_TEXT_COLOR);
+ int end = (Integer) endValues.values.get(PROPNAME_TEXT_COLOR);
+ if (start != end) {
+ anim = ObjectAnimator.ofObject(textView, "textColor",
+ new ArgbEvaluator(), start, end);
+ if (getStartDelay() > 0) {
+ textView.setTextColor(end);
+ }
+ }
+ }
+ return anim;
+ }
+}
diff --git a/core/java/android/view/transition/Rotate.java b/core/java/android/view/transition/Rotate.java
new file mode 100644
index 0000000..b42fbe5
--- /dev/null
+++ b/core/java/android/view/transition/Rotate.java
@@ -0,0 +1,67 @@
+/*
+ * 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 android.view.transition;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * This transition captures the rotation property of targets before and after
+ * the scene change and animates any changes.
+ */
+public class Rotate extends Transition {
+
+ private static final String PROPNAME_ROTATION = "android:rotate:rotation";
+
+ @Override
+ protected void captureValues(TransitionValues values, boolean start) {
+ values.values.put(PROPNAME_ROTATION, values.view.getRotation());
+ }
+
+ @Override
+ protected boolean prePlay(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null) {
+ return false;
+ }
+ final View view = endValues.view;
+ float startRotation = (Float) startValues.values.get(PROPNAME_ROTATION);
+ float endRotation = (Float) endValues.values.get(PROPNAME_ROTATION);
+ if (startRotation != endRotation) {
+ view.setRotation(startRotation);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected Animator play(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null) {
+ return null;
+ }
+ final View view = endValues.view;
+ float startRotation = (Float) startValues.values.get(PROPNAME_ROTATION);
+ float endRotation = (Float) endValues.values.get(PROPNAME_ROTATION);
+ if (startRotation != endRotation) {
+ return ObjectAnimator.ofFloat(view, View.ROTATION,
+ startRotation, endRotation);
+ }
+ return null;
+ }
+}
diff --git a/core/java/android/view/transition/Scene.java b/core/java/android/view/transition/Scene.java
new file mode 100644
index 0000000..62cb9d3
--- /dev/null
+++ b/core/java/android/view/transition/Scene.java
@@ -0,0 +1,191 @@
+/*
+ * 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 android.view.transition;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+/**
+ * A scene represents the collection of values that various properties in the
+ * View hierarchy will have when the scene is applied. A Scene can be
+ * configured to automatically run a Transition when it is applied, which will
+ * animate the various property changes that take place during the
+ * scene change.
+ */
+public final class Scene {
+
+ private Context mContext;
+ private int mLayoutId = -1;
+ private ViewGroup mSceneRoot;
+ private ViewGroup mLayout; // alternative to layoutId
+ Runnable mEnterAction, mExitAction;
+
+ /**
+ * Constructs a Scene with no information about how values will change
+ * when this scene is applied. This constructor might be used when
+ * a Scene is created with the intention of being dynamically configured,
+ * through setting {@link #setEnterAction(Runnable)} and possibly
+ * {@link #setExitAction(Runnable)}.
+ *
+ * @param sceneRoot The root of the hierarchy in which scene changes
+ * and transitions will take place.
+ */
+ public Scene(ViewGroup sceneRoot) {
+ mSceneRoot = sceneRoot;
+ }
+
+ /**
+ * Constructs a Scene which, when entered, will remove any
+ * children from the sceneRoot container and will inflate and add
+ * the hierarchy specified by the layoutId resource file.
+ *
+ * @param sceneRoot The root of the hierarchy in which scene changes
+ * and transitions will take place.
+ * @param layoutId The id of a resource file that defines the view
+ * hierarchy of this scene.
+ * @param context The context used in the process of inflating
+ * the layout resource.
+ */
+ public Scene(ViewGroup sceneRoot, int layoutId, Context context) {
+ mContext = context;
+ mSceneRoot = sceneRoot;
+ mLayoutId = layoutId;
+ }
+
+ /**
+ * Constructs a Scene which, when entered, will remove any
+ * children from the sceneRoot container and add the layout
+ * object as a new child of that container.
+ *
+ * @param sceneRoot The root of the hierarchy in which scene changes
+ * and transitions will take place.
+ * @param layout The view hiearrchy of this scene, added as a child
+ * of sceneRoot when this scene is entered.
+ */
+ public Scene(ViewGroup sceneRoot, ViewGroup layout) {
+ mSceneRoot = sceneRoot;
+ mLayout = layout;
+ }
+
+ /**
+ * Gets the root of the scene, which is the root of the view hierarchy
+ * affected by changes due to this scene, and which will be animated
+ * when this scene is entered.
+ *
+ * @return The root of the view hierarchy affected by this scene.
+ */
+ public ViewGroup getSceneRoot() {
+ return mSceneRoot;
+ }
+
+ /**
+ * Exits this scene, if it is the {@link ViewGroup#getCurrentScene()
+ * currentScene} on the scene's {@link #getSceneRoot() scene root}.
+ * Exiting a scene involves removing the layout added if the scene
+ * has either a layoutId or layout view group (set at construction
+ * time) and running the {@link #setExitAction(Runnable) exit action}
+ * if there is one.
+ */
+ public void exit() {
+ if (mSceneRoot.getCurrentScene() == this) {
+ if (mLayoutId >= 0 || mLayout != null) {
+ // Undo layout change caused by entering this scene
+ getSceneRoot().removeAllViews();
+ }
+ if (mExitAction != null) {
+ mExitAction.run();
+ }
+ }
+ }
+
+ /**
+ * Enters this scene, which entails changing all values that
+ * are specified by this scene. These may be values associated
+ * with a layout view group or layout resource file which will
+ * now be added to the scene root, or it may be values changed by
+ * an {@link #setEnterAction(Runnable)} enter action}, or a
+ * combination of the these. No transition will be run when the
+ * scene is entered. To get transition behavior in scene changes,
+ * use one of the methods in {@link TransitionManager} instead.
+ */
+ public void enter() {
+
+ // Apply layout change, if any
+ if (mLayoutId >= 0 || mLayout != null) {
+ // redundant with exit() action of previous scene, but must
+ // empty out that parent container before adding to it
+ getSceneRoot().removeAllViews();
+
+ if (mLayoutId >= 0) {
+ LayoutInflater.from(mContext).inflate(mLayoutId, mSceneRoot);
+ } else {
+ mSceneRoot.addView(mLayout);
+ }
+ }
+
+ // Notify next scene that it is entering. Subclasses may override to configure scene.
+ if (mEnterAction != null) {
+ mEnterAction.run();
+ }
+
+ mSceneRoot.setCurrentScene(this );
+ }
+
+ /**
+ * Scenes that are not defined with layout resources or
+ * hierarchies, or which need to perform additional steps
+ * after those hierarchies are changed to, should set an enter
+ * action, and possibly an exit action as well. An enter action
+ * will cause Scene to call back into application code to do
+ * anything else the application needs after transitions have
+ * captured pre-change values and after any other scene changes
+ * have been applied, such as the layout (if any) being added to
+ * the view hierarchy. After this method is called, Transitions will
+ * be played.
+ *
+ * @param action The runnable whose {@link Runnable#run() run()} method will
+ * be called when this scene is entered
+ * @see #setExitAction(Runnable)
+ * @see Scene#Scene(ViewGroup, int, Context)
+ * @see Scene#Scene(ViewGroup, ViewGroup)
+ */
+ public void setEnterAction(Runnable action) {
+ mEnterAction = action;
+ }
+
+ /**
+ * Scenes that are not defined with layout resources or
+ * hierarchies, or which need to perform additional steps
+ * after those hierarchies are changed to, should set an enter
+ * action, and possibly an exit action as well. An exit action
+ * will cause Scene to call back into application code to do
+ * anything the application needs to do after applicable transitions have
+ * captured pre-change values, but before any other scene changes
+ * have been applied, such as the new layout (if any) being added to
+ * the view hierarchy. After this method is called, the next scene
+ * will be entered, including a call to {@link #setEnterAction(Runnable)}
+ * if an enter action is set.
+ *
+ * @see #setEnterAction(Runnable)
+ * @see Scene#Scene(ViewGroup, int, Context)
+ * @see Scene#Scene(ViewGroup, ViewGroup)
+ */
+ public void setExitAction(Runnable action) {
+ mExitAction = action;
+ }
+
+} \ No newline at end of file
diff --git a/core/java/android/view/transition/Slide.java b/core/java/android/view/transition/Slide.java
new file mode 100644
index 0000000..8630ee2
--- /dev/null
+++ b/core/java/android/view/transition/Slide.java
@@ -0,0 +1,70 @@
+/*
+ * 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 android.view.transition;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+
+/**
+ * This transition captures the visibility of target objects before and
+ * after a scene change and animates any changes by sliding the target
+ * objects into or out of place.
+ */
+public class Slide extends Visibility {
+
+ // TODO: Add parameter for sliding factor - it's hard-coded below
+
+ private static final TimeInterpolator sAccelerator = new AccelerateInterpolator();
+ private static final TimeInterpolator sDecelerator = new DecelerateInterpolator();
+
+ @Override
+ protected Animator appear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) {
+ ObjectAnimator anim = ObjectAnimator.ofFloat(endView, View.TRANSLATION_Y,
+ -2 * endView.getHeight(), 0);
+ anim.setInterpolator(sDecelerator);
+ return anim;
+ }
+
+ @Override
+ protected boolean preAppear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) {
+ endView.setTranslationY(-2 * endView.getHeight());
+ return true;
+ }
+
+ @Override
+ protected boolean preDisappear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) {
+ startView.setTranslationY(0);
+ return true;
+ }
+
+ @Override
+ protected Animator disappear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) {
+ ObjectAnimator anim = ObjectAnimator.ofFloat(startView, View.TRANSLATION_Y, 0,
+ -2 * startView.getHeight());
+ anim.setInterpolator(sAccelerator);
+ return anim;
+ }
+
+}
diff --git a/core/java/android/view/transition/TextChange.java b/core/java/android/view/transition/TextChange.java
new file mode 100644
index 0000000..0ba2412
--- /dev/null
+++ b/core/java/android/view/transition/TextChange.java
@@ -0,0 +1,90 @@
+/*
+ * 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 android.view.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import java.util.HashMap;
+
+/**
+ * This transition tracks changes to the text in TextView targets. If the text
+ * changes between the start and end scenes, the transition ensures that the
+ * starting text stays until the transition ends, at which point it changes
+ * to the end text. This is useful in situations where you want to resize a
+ * text view to its new size before displaying the text that goes there.
+ */
+public class TextChange extends Transition {
+ private static final String PROPNAME_TEXT = "android:textchange:text";
+
+ // 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).
+
+ @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());
+ }
+ }
+
+ @Override
+ protected boolean prePlay(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null || !(endValues.view instanceof TextView)) {
+ return false;
+ }
+ final TextView view = (TextView) endValues.view;
+ HashMap<String, Object> startVals = startValues.values;
+ HashMap<String, Object> endVals = endValues.values;
+ String startText = (String) startVals.get(PROPNAME_TEXT);
+ String endText = (String) endVals.get(PROPNAME_TEXT);
+ if (!startText.equals(endText)) {
+ view.setText(startText);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected Animator play(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ if (startValues == null || endValues == null || !(endValues.view instanceof TextView)) {
+ return null;
+ }
+ final TextView view = (TextView) endValues.view;
+ HashMap<String, Object> startVals = startValues.values;
+ HashMap<String, Object> endVals = endValues.values;
+ final String startText = (String) startVals.get(PROPNAME_TEXT);
+ final String endText = (String) endVals.get(PROPNAME_TEXT);
+ if (!startText.equals(endText)) {
+ // This noop animation is just used to keep the text in its start state
+ // until the transition ends
+ ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ view.setText(endText);
+ }
+ });
+ return anim;
+ }
+ return null;
+ }
+}
diff --git a/core/java/android/view/transition/Transition.java b/core/java/android/view/transition/Transition.java
new file mode 100644
index 0000000..150c218
--- /dev/null
+++ b/core/java/android/view/transition/Transition.java
@@ -0,0 +1,911 @@
+/*
+ * 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 android.view.transition;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.TimeInterpolator;
+import android.util.LongSparseArray;
+import android.util.SparseArray;
+import android.view.SurfaceView;
+import android.view.TextureView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOverlay;
+import android.widget.ListView;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * A Transition holds information about animations that will be run on its
+ * targets during a scene change. Subclasses of this abstract class may
+ * choreograph several child transitions ({@link TransitionGroup} or they may
+ * perform custom animations themselves. Any Transition has two main jobs:
+ * (1) capture property values, and (2) play animations based on changes to
+ * captured property values. A custom transition knows what property values
+ * on View objects are of interest to it, and also knows how to animate
+ * changes to those values. For example, the {@link Fade} transition tracks
+ * changes to visibility-related properties and is able to construct and run
+ * animations that fade items in or out based on changes to those properties.
+ *
+ * <p>Note: Transitions may not work correctly with either {@link SurfaceView}
+ * or {@link TextureView}, due to the way that these views are displayed
+ * on the screen. For SurfaceView, the problem is that the view is updated from
+ * a non-UI thread, so changes to the view due to transitions (such as moving
+ * and resizing the view) may be out of sync with the display inside those bounds.
+ * TextureView is more compatible with transitions in general, but some
+ * specific transitions (such as {@link Crossfade}) may not be compatible
+ * with TextureView because they rely on {@link ViewOverlay} functionality,
+ * which does not currently work with TextureView.</p>
+ */
+public abstract class Transition {
+
+ private static final String LOG_TAG = "Transition";
+ static final boolean DBG = false;
+
+ long mStartDelay = -1;
+ long mDuration = -1;
+ TimeInterpolator mInterpolator = null;
+ int[] mTargetIds;
+ View[] mTargets;
+ // TODO: sparse arrays instead of hashmaps?
+ private HashMap<View, TransitionValues> mStartValues =
+ new HashMap<View, TransitionValues>();
+ private SparseArray<TransitionValues> mStartIdValues = new SparseArray<TransitionValues>();
+ private LongSparseArray<TransitionValues> mStartItemIdValues =
+ new LongSparseArray<TransitionValues>();
+ private HashMap<View, TransitionValues> mEndValues =
+ new HashMap<View, TransitionValues>();
+ private SparseArray<TransitionValues> mEndIdValues = new SparseArray<TransitionValues>();
+ private LongSparseArray<TransitionValues> mEndItemIdValues =
+ new LongSparseArray<TransitionValues>();
+
+ // Used to carry data between preplay() and play(), cleared before every scene transition
+ private ArrayList<TransitionValues> mPlayStartValuesList = new ArrayList<TransitionValues>();
+ private ArrayList<TransitionValues> mPlayEndValuesList = new ArrayList<TransitionValues>();
+
+ // Number of per-target instances of this Transition currently running. This count is
+ // determined by calls to startTransition() and endTransition()
+ int mNumInstances = 0;
+
+
+ /**
+ * The set of listeners to be sent transition lifecycle events.
+ */
+ ArrayList<TransitionListener> mListeners = null;
+
+ /**
+ * Constructs a Transition object with no target objects. A transition with
+ * no targets defaults to running on all target objects in the scene hierarchy
+ * (if the transition is not contained in a TransitionGroup), or all target
+ * objects passed down from its parent (if it is in a TransitionGroup).
+ */
+ public Transition() {}
+
+ /**
+ * Sets the duration of this transition. By default, there is no duration
+ * (indicated by a negative number), which means that the Animator created by
+ * the transition will have its own specified duration. If the duration of a
+ * Transition is set, that duration will override the Animator duration.
+ *
+ * @param duration The length of the animation, in milliseconds.
+ * @return This transition object.
+ */
+ public Transition setDuration(long duration) {
+ mDuration = duration;
+ return this;
+ }
+
+ public long getDuration() {
+ return mDuration;
+ }
+
+ /**
+ * Sets the startDelay of this transition. By default, there is no delay
+ * (indicated by a negative number), which means that the Animator created by
+ * the transition will have its own specified startDelay. If the delay of a
+ * Transition is set, that delay will override the Animator delay.
+ *
+ * @param startDelay The length of the delay, in milliseconds.
+ */
+ public void setStartDelay(long startDelay) {
+ mStartDelay = startDelay;
+ }
+
+ public long getStartDelay() {
+ return mStartDelay;
+ }
+
+ /**
+ * Sets the interpolator of this transition. By default, the interpolator
+ * is null, which means that the Animator created by the transition
+ * will have its own specified interpolator. If the interpolator of a
+ * Transition is set, that interpolator will override the Animator interpolator.
+ *
+ * @param interpolator The time interpolator used by the transition
+ */
+ public void setInterpolator(TimeInterpolator interpolator) {
+ mInterpolator = interpolator;
+ }
+
+ public TimeInterpolator getInterpolator() {
+ return mInterpolator;
+ }
+
+ /**
+ * This method is called by the transition's parent (all the way up to the
+ * topmost Transition in the hierarchy) with the sceneRoot and start/end
+ * values that the transition may need to run animations on its target
+ * views. The method is called for every applicable target object, which
+ * is stored in the {@link TransitionValues#view} field. When the method
+ * results in an animation needing to be run, the transition will construct
+ * the appropriate {@link Animator} object and return it. The transition
+ * mechanism will apply any applicable duration, startDelay, and interpolator
+ * to that animation and start it. Returning null from the method tells the
+ * transition engine that there is no animation to be played (TransitionGroup
+ * will return null because any applicable animations were started on its child
+ * transitions already and there is no animation to be run on the group itself).
+ *
+ * @param sceneRoot
+ * @param startValues
+ * @param endValues
+ * @return Animator The animation to run.
+ */
+ protected abstract Animator play(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues);
+
+ /**
+ * This method is called by the transition's parent (all the way up to the
+ * topmost Transition in the hierarchy) with the sceneRoot and start/end
+ * values that the transition may need to set things up at the start of a
+ * Transition. For example, if an overall Transition consists of several
+ * child transitions in sequence, then some of the child transitions may
+ * want to set initial values on target views prior to the overall
+ * Transition commencing, to put them in an appropriate scene for the
+ * delay between that start and the child Transition start time. For
+ * example, a transition that fades an item in may wish to set the starting
+ * alpha value to 0, to avoid it blinking in prior to the transition
+ * actually starting the animation. This is necessary because the scene
+ * change that triggers the Transition will automatically set the end-scene
+ * on all target views, so a Transition that wants to animate from a
+ * different value should set that value in the preplay() method.
+ *
+ * <p>Additionally, a Transition can perform logic to determine whether
+ * the transition needs to run on the given target and start/end values.
+ * For example, a transition that resizes objects on the screen may wish
+ * to avoid running for views which are not present in either the start
+ * or end scenes. A return value of <code>false</code> indicates that
+ * the transition should not run, and there will be no ensuing call to the
+ * {@link #play(ViewGroup, TransitionValues, TransitionValues)} method during
+ * this scene change. The default implementation returns true.</p>
+ *
+ * <p>The method is called for every applicable target object, which is
+ * stored in the {@link TransitionValues#view} field.</p>
+ *
+ * @param sceneRoot
+ * @param startValues
+ * @param endValues
+ * @return True if the Transition's {@link #play(ViewGroup,
+ * TransitionValues, TransitionValues) play()} method should be called
+ * during this scene change, false otherwise.
+ */
+ protected boolean prePlay(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ return true;
+ }
+
+ /**
+ * This version of prePlay() is called with the entire set of start/end
+ * values. The implementation in Transition iterates through these lists
+ * and calls {@link #prePlay(ViewGroup, TransitionValues, TransitionValues)}
+ * with each set of start/end values on this transition. The
+ * TransitionGroup subclass overrides this method and delegates it to
+ * each of its children in succession. The intention in splitting
+ * preplay() out from play() is to allow all Transitions in the tree to
+ * set up the appropriate start scene for their target objects prior to
+ * any calls to play(), which is necessary when there is a sequential
+ * Transition, where a child transition which is not the first may want to
+ * set up a target's scene prior to the overall Transition start.
+ *
+ * @hide
+ */
+ protected void prePlay(ViewGroup sceneRoot, HashMap<View, TransitionValues> startValues,
+ SparseArray<TransitionValues> startIdValues,
+ LongSparseArray<TransitionValues> startItemIdValues,
+ HashMap<View, TransitionValues> endValues,
+ SparseArray<TransitionValues> endIdValues,
+ LongSparseArray<TransitionValues> endItemIdValues) {
+ mPlayStartValuesList.clear();
+ mPlayEndValuesList.clear();
+ HashMap<View, TransitionValues> endCopy = new HashMap<View, TransitionValues>(endValues);
+ SparseArray<TransitionValues> endIdCopy =
+ new SparseArray<TransitionValues>(endIdValues.size());
+ for (int i = 0; i < endIdValues.size(); ++i) {
+ int id = endIdValues.keyAt(i);
+ endIdCopy.put(id, endIdValues.valueAt(i));
+ }
+ LongSparseArray<TransitionValues> endItemIdCopy =
+ new LongSparseArray<TransitionValues>(endItemIdValues.size());
+ for (int i = 0; i < endItemIdValues.size(); ++i) {
+ long id = endItemIdValues.keyAt(i);
+ endItemIdCopy.put(id, endItemIdValues.valueAt(i));
+ }
+ // Walk through the start values, playing everything we find
+ // Remove from the end set as we go
+ ArrayList<TransitionValues> startValuesList = new ArrayList<TransitionValues>();
+ ArrayList<TransitionValues> endValuesList = new ArrayList<TransitionValues>();
+ for (View view : startValues.keySet()) {
+ TransitionValues start = null;
+ TransitionValues end = null;
+ boolean isInListView = false;
+ if (view.getParent() instanceof ListView) {
+ isInListView = true;
+ }
+ if (!isInListView) {
+ int id = view.getId();
+ start = startValues.get(view) != null ?
+ startValues.get(view) : startIdValues.get(id);
+ if (endValues.get(view) != null) {
+ end = endValues.get(view);
+ endCopy.remove(view);
+ } else {
+ end = endIdValues.get(id);
+ View removeView = null;
+ for (View viewToRemove : endCopy.keySet()) {
+ if (viewToRemove.getId() == id) {
+ removeView = viewToRemove;
+ }
+ }
+ if (removeView != null) {
+ endCopy.remove(removeView);
+ }
+ }
+ endIdCopy.remove(id);
+ if (isValidTarget(view, id)) {
+ startValuesList.add(start);
+ endValuesList.add(end);
+ }
+ } else {
+ ListView parent = (ListView) view.getParent();
+ if (parent.getAdapter().hasStableIds()) {
+ int position = parent.getPositionForView(view);
+ long itemId = parent.getItemIdAtPosition(position);
+ start = startItemIdValues.get(itemId);
+ endItemIdCopy.remove(itemId);
+ // TODO: deal with targetIDs for itemIDs for ListView items
+ startValuesList.add(start);
+ endValuesList.add(end);
+ }
+ }
+ }
+ int startItemIdCopySize = startItemIdValues.size();
+ for (int i = 0; i < startItemIdCopySize; ++i) {
+ long id = startItemIdValues.keyAt(i);
+ if (isValidTarget(null, id)) {
+ TransitionValues start = startItemIdValues.get(id);
+ TransitionValues end = endItemIdValues.get(id);
+ endItemIdCopy.remove(id);
+ startValuesList.add(start);
+ endValuesList.add(end);
+ }
+ }
+ // Now walk through the remains of the end set
+ for (View view : endCopy.keySet()) {
+ int id = view.getId();
+ if (isValidTarget(view, id)) {
+ TransitionValues start = startValues.get(view) != null ?
+ startValues.get(view) : startIdValues.get(id);
+ TransitionValues end = endCopy.get(view);
+ endIdCopy.remove(id);
+ startValuesList.add(start);
+ endValuesList.add(end);
+ }
+ }
+ int endIdCopySize = endIdCopy.size();
+ for (int i = 0; i < endIdCopySize; ++i) {
+ int id = endIdCopy.keyAt(i);
+ if (isValidTarget(null, id)) {
+ TransitionValues start = startIdValues.get(id);
+ TransitionValues end = endIdCopy.get(id);
+ startValuesList.add(start);
+ endValuesList.add(end);
+ }
+ }
+ int endItemIdCopySize = endItemIdCopy.size();
+ for (int i = 0; i < endItemIdCopySize; ++i) {
+ long id = endItemIdCopy.keyAt(i);
+ // TODO: Deal with targetIDs and itemIDs
+ TransitionValues start = startItemIdValues.get(id);
+ TransitionValues end = endItemIdCopy.get(id);
+ startValuesList.add(start);
+ endValuesList.add(end);
+ }
+ for (int i = 0; i < startValuesList.size(); ++i) {
+ TransitionValues start = startValuesList.get(i);
+ TransitionValues end = endValuesList.get(i);
+ // TODO: what to do about targetIds and itemIds?
+ if (prePlay(sceneRoot, start, end)) {
+ // Note: we've already done the check against targetIDs in these lists
+ mPlayStartValuesList.add(start);
+ mPlayEndValuesList.add(end);
+ }
+ }
+ }
+
+ /**
+ * Internal utility method for checking whether a given view/id
+ * is valid for this transition, where "valid" means that either
+ * the Transition has no target/targetId list (the default, in which
+ * cause the transition should act on all views in the hiearchy), or
+ * the given view is in the target list or the view id is in the
+ * targetId list. If the target parameter is null, then the target list
+ * is not checked (this is in the case of ListView items, where the
+ * views are ignored and only the ids are used).
+ */
+ boolean isValidTarget(View target, long targetId) {
+ if (mTargetIds == null && mTargets == null) {
+ return true;
+ }
+ if (mTargetIds != null) {
+ for (int i = 0; i < mTargetIds.length; ++i) {
+ if (mTargetIds[i] == targetId) {
+ return true;
+ }
+ }
+ }
+ if (target != null && mTargets != null) {
+ for (int i = 0; i < mTargets.length; ++i) {
+ if (mTargets[i] == target) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * This version of play() is called with the entire set of start/end
+ * values. The implementation in Transition iterates through these lists
+ * and calls {@link #play(ViewGroup, TransitionValues, TransitionValues)}
+ * with each set of start/end values on this transition. The
+ * TransitionGroup subclass overrides this method and delegates it to
+ * each of its children in succession.
+ *
+ * @hide
+ */
+ protected void play(ViewGroup sceneRoot,
+ final HashMap<View, TransitionValues> startValues,
+ final SparseArray<TransitionValues> startIdValues,
+ final LongSparseArray<TransitionValues> startItemIdValues,
+ final HashMap<View, TransitionValues> endValues,
+ final SparseArray<TransitionValues> endIdValues,
+ final LongSparseArray<TransitionValues> endItemIdValues) {
+
+ startTransition();
+ // Now walk the list of TransitionValues, calling play for each pair
+ for (int i = 0; i < mPlayStartValuesList.size(); ++i) {
+ TransitionValues start = mPlayStartValuesList.get(i);
+ TransitionValues end = mPlayEndValuesList.get(i);
+ startTransition();
+ animate(play(sceneRoot, start, end));
+ }
+ mPlayStartValuesList.clear();
+ mPlayEndValuesList.clear();
+ endTransition();
+ }
+
+ /**
+ * Captures the current scene of values for the properties that this
+ * transition monitors. These values can be either the start or end
+ * values used in a subsequent call to
+ * {@link #play(ViewGroup, TransitionValues, TransitionValues)}, as indicated by
+ * <code>start</code>. The main concern for an implementation is what the
+ * properties are that the transition cares about and what the values are
+ * for all of those properties. The start and end values will be compared
+ * later during the preplay and play() methods to determine what, if any,
+ * animations, should be run.
+ *
+ * @param transitionValues The holder any values that the Transition
+ * wishes to store. Values are stored in the fields of this
+ * TransitionValues object, according to their type, and are keyed from
+ * a String value. For example, to start a view's rotation value,
+ * a Transition might call
+ * <code>transitionValues.floatValues.put("rotation", view.getRotation())
+ * </code>. The target <code>View</code> will already be stored in
+ * the transitionValues structure when this method is called. The other
+ * fields in TransitionValues, e.g. <code>floatValues</code>,
+ * may need to be instantiated if they have not yet been created.
+ */
+ protected abstract void captureValues(TransitionValues transitionValues, boolean start);
+
+ /**
+ * Sets the ids of target views that this Transition is interested in
+ * animating. By default, there are no targetIds, and a Transition will
+ * listen for changes on every view in the hierarchy below the sceneRoot
+ * of the Scene being transitioned into. Setting targetIDs constrains
+ * the Transition to only listen for, and act on, views with these IDs.
+ * Views with different IDs, or no IDs whatsoever, will be ignored.
+ *
+ * @see View#getId()
+ * @param targetIds A list of IDs which specify the set of Views on which
+ * the Transition will act.
+ * @return Transition The Transition on which the targetIds have been set.
+ * Returning the same object makes it easier to chain calls during
+ * construction, such as
+ * <code>transitionGroup.addTransitions(new Fade()).setTargetIds(someId);</code>
+ */
+ public Transition setTargetIds(int... targetIds) {
+ int numTargets = targetIds.length;
+ mTargetIds = new int[numTargets];
+ System.arraycopy(targetIds, 0, mTargetIds, 0, numTargets);
+ return this;
+ }
+
+ /**
+ * Sets the target view instances that this Transition is interested in
+ * animating. By default, there are no targets, and a Transition will
+ * listen for changes on every view in the hierarchy below the sceneRoot
+ * of the Scene being transitioned into. Setting targets constrains
+ * the Transition to only listen for, and act on, these views.
+ * All other views will be ignored.
+ *
+ * <p>The target list is like the {@link #setTargetIds(int...) targetId}
+ * list except this list specifies the actual View instances, not the ids
+ * of the views. This is an important distinction when scene changes involve
+ * view hierarchies which have been inflated separately; different views may
+ * share the same id but not actually be the same instance. If the transition
+ * should treat those views as the same, then seTargetIds() should be used
+ * instead of setTargets(). If, on the other hand, scene changes involve
+ * changes all within the same view hierarchy, among views which do not
+ * necessary have ids set on them, then the target list may be more
+ * convenient.</p>
+ *
+ * @see #setTargetIds(int...)
+ * @param targets A list of Views on which the Transition will act.
+ * @return Transition The Transition on which the targets have been set.
+ * Returning the same object makes it easier to chain calls during
+ * construction, such as
+ * <code>transitionGroup.addTransitions(new Fade()).setTargets(someView);</code>
+ */
+ public Transition setTargets(View... targets) {
+ int numTargets = targets.length;
+ mTargets = new View[numTargets];
+ System.arraycopy(targets, 0, mTargets, 0, numTargets);
+ return this;
+ }
+
+ /**
+ * Returns the array of target IDs that this transition limits itself to
+ * tracking and animating. If the array is null for both this method and
+ * {@link #getTargets()}, then this transition is
+ * not limited to specific views, and will handle changes to any views
+ * in the hierarchy of a scene change.
+ *
+ * @return the list of target IDs
+ */
+ public int[] getTargetIds() {
+ return mTargetIds;
+ }
+
+ /**
+ * Returns the array of target views that this transition limits itself to
+ * tracking and animating. If the array is null for both this method and
+ * {@link #getTargetIds()}, then this transition is
+ * not limited to specific views, and will handle changes to any views
+ * in the hierarchy of a scene change.
+ *
+ * @return the list of target views
+ */
+ public View[] getTargets() {
+ return mTargets;
+ }
+
+ /**
+ * Recursive method that captures values for the given view and the
+ * hierarchy underneath it.
+ * @param sceneRoot The root of the view hierarchy being captured
+ * @param start true if this capture is happening before the scene change,
+ * false otherwise
+ */
+ void captureValues(ViewGroup sceneRoot, boolean start) {
+ if (mTargetIds != null && mTargetIds.length > 0 ||
+ mTargets != null && mTargets.length > 0) {
+ if (mTargetIds != null) {
+ for (int i = 0; i < mTargetIds.length; ++i) {
+ int id = mTargetIds[i];
+ View view = sceneRoot.findViewById(id);
+ if (view != null) {
+ TransitionValues values = new TransitionValues();
+ values.view = view;
+ captureValues(values, start);
+ if (start) {
+ mStartValues.put(view, values);
+ mStartIdValues.put(id, values);
+ } else {
+ mEndValues.put(view, values);
+ mEndIdValues.put(id, values);
+ }
+ }
+ }
+ }
+ if (mTargets != null) {
+ for (int i = 0; i < mTargets.length; ++i) {
+ View view = mTargets[i];
+ if (view != null) {
+ TransitionValues values = new TransitionValues();
+ values.view = view;
+ captureValues(values, start);
+ if (start) {
+ mStartValues.put(view, values);
+ } else {
+ mEndValues.put(view, values);
+ }
+ }
+ }
+ }
+ } else {
+ captureHierarchy(sceneRoot, start);
+ }
+ }
+
+ /**
+ * Recursive method which captures values for an entire view hierarchy,
+ * starting at some root view. Transitions without targetIDs will use this
+ * method to capture values for all possible views.
+ *
+ * @param view The view for which to capture values. Children of this View
+ * will also be captured, recursively down to the leaf nodes.
+ * @param start true if values are being captured in the start scene, false
+ * otherwise.
+ */
+ private void captureHierarchy(View view, boolean start) {
+ if (view == null) {
+ return;
+ }
+ boolean isListViewItem = false;
+ if (view.getParent() instanceof ListView) {
+ isListViewItem = true;
+ }
+ if (isListViewItem && !((ListView) view.getParent()).getAdapter().hasStableIds()) {
+ // ignore listview children unless we can track them with stable IDs
+ return;
+ }
+ long id;
+ if (!isListViewItem) {
+ id = view.getId();
+ } else {
+ ListView listview = (ListView) view.getParent();
+ int position = listview.getPositionForView(view);
+ id = listview.getItemIdAtPosition(position);
+ view.setHasTransientState(true);
+ }
+ TransitionValues values = new TransitionValues();
+ values.view = view;
+ captureValues(values, start);
+ if (start) {
+ if (!isListViewItem) {
+ mStartValues.put(view, values);
+ mStartIdValues.put((int) id, values);
+ } else {
+ mStartItemIdValues.put(id, values);
+ }
+ } else {
+ if (!isListViewItem) {
+ mEndValues.put(view, values);
+ mEndIdValues.put((int) id, values);
+ } else {
+ mEndItemIdValues.put(id, values);
+ }
+ }
+ if (view instanceof ViewGroup) {
+ ViewGroup parent = (ViewGroup) view;
+ for (int i = 0; i < parent.getChildCount(); ++i) {
+ captureHierarchy(parent.getChildAt(i), start);
+ }
+ }
+ }
+
+ /**
+ * Called by TransitionManager to play the transition. This calls
+ * prePlay() and then play() with the full set of per-view
+ * transitionValues objects
+ */
+ void play(ViewGroup sceneRoot) {
+ // prePlay() must be called on entire transition hierarchy and set of views
+ // before calling play() on anything; every transition needs a chance to set up
+ // target views appropriately before transitions begin running
+ prePlay(sceneRoot, mStartValues, mStartIdValues, mStartItemIdValues,
+ mEndValues, mEndIdValues, mEndItemIdValues);
+ play(sceneRoot, mStartValues, mStartIdValues, mStartItemIdValues,
+ mEndValues, mEndIdValues, mEndItemIdValues);
+ }
+
+ /**
+ * This is a utility method used by subclasses to handle standard parts of
+ * setting up and running an Animator: it sets the {@link #getDuration()
+ * duration} and the {@link #getStartDelay() startDelay}, starts the
+ * animation, and, when the animator ends, calls {@link #endTransition()}.
+ *
+ * @param animator The Animator to be run during this transition.
+ *
+ * @hide
+ */
+ protected void animate(Animator animator) {
+ // TODO: maybe pass auto-end as a boolean parameter?
+ if (animator == null) {
+ endTransition();
+ } else {
+ if (getDuration() >= 0) {
+ animator.setDuration(getDuration());
+ }
+ if (getStartDelay() >= 0) {
+ animator.setStartDelay(getStartDelay());
+ }
+ if (getInterpolator() != null) {
+ animator.setInterpolator(getInterpolator());
+ }
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ cancelTransition();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endTransition();
+ animation.removeListener(this);
+ }
+ });
+ animator.start();
+ }
+ }
+
+ /**
+ * Subclasses may override to receive notice of when the transition starts.
+ * This is equivalent to listening for the
+ * {@link TransitionListener#onTransitionStart(Transition)} callback.
+ */
+ protected void onTransitionStart() {
+ }
+
+ /**
+ * Subclasses may override to receive notice of when the transition is
+ * canceled. This is equivalent to listening for the
+ * {@link TransitionListener#onTransitionCancel(Transition)} callback.
+ */
+ protected void onTransitionCancel() {
+ }
+
+ /**
+ * Subclasses may override to receive notice of when the transition ends.
+ * This is equivalent to listening for the
+ * {@link TransitionListener#onTransitionEnd(Transition)} callback.
+ */
+ protected void onTransitionEnd() {
+ }
+
+ /**
+ * This method is called automatically by the transition and
+ * TransitionGroup classes prior to a Transition subclass starting;
+ * subclasses should not need to call it directly.
+ *
+ * @hide
+ */
+ protected void startTransition() {
+ if (mNumInstances == 0) {
+ onTransitionStart();
+ if (mListeners != null && mListeners.size() > 0) {
+ ArrayList<TransitionListener> tmpListeners =
+ (ArrayList<TransitionListener>) mListeners.clone();
+ int numListeners = tmpListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ tmpListeners.get(i).onTransitionStart(this);
+ }
+ }
+ }
+ mNumInstances++;
+ }
+
+ /**
+ * This method is called automatically by the Transition and
+ * TransitionGroup classes when a transition finishes, either because
+ * a transition did nothing (returned a null Animator from
+ * {@link Transition#play(ViewGroup, TransitionValues,
+ * TransitionValues)}) or because the transition returned a valid
+ * Animator and endTransition() was called in the onAnimationEnd()
+ * callback of the AnimatorListener.
+ *
+ * @hide
+ */
+ protected void endTransition() {
+ --mNumInstances;
+ if (mNumInstances == 0) {
+ onTransitionEnd();
+ if (mListeners != null && mListeners.size() > 0) {
+ ArrayList<TransitionListener> tmpListeners =
+ (ArrayList<TransitionListener>) mListeners.clone();
+ int numListeners = tmpListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ tmpListeners.get(i).onTransitionEnd(this);
+ }
+ }
+ for (int i = 0; i < mStartItemIdValues.size(); ++i) {
+ TransitionValues tv = mStartItemIdValues.valueAt(i);
+ View v = tv.view;
+ if (v.hasTransientState()) {
+ v.setHasTransientState(false);
+ }
+ }
+ for (int i = 0; i < mEndItemIdValues.size(); ++i) {
+ TransitionValues tv = mEndItemIdValues.valueAt(i);
+ View v = tv.view;
+ if (v.hasTransientState()) {
+ v.setHasTransientState(false);
+ }
+ }
+ mStartValues.clear();
+ mStartIdValues.clear();
+ mStartItemIdValues.clear();
+ mEndValues.clear();
+ mEndIdValues.clear();
+ mEndItemIdValues.clear();
+ }
+ }
+
+ /**
+ * This method cancels a transition that is currently running.
+ * Implementation TBD.
+ */
+ protected void cancelTransition() {
+ // TODO: how does this work with instances?
+ // TODO: this doesn't actually do *anything* yet
+ onTransitionCancel();
+ if (mListeners != null && mListeners.size() > 0) {
+ ArrayList<TransitionListener> tmpListeners =
+ (ArrayList<TransitionListener>) mListeners.clone();
+ int numListeners = tmpListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ tmpListeners.get(i).onTransitionCancel(this);
+ }
+ }
+ }
+
+ /**
+ * Adds a listener to the set of listeners that are sent events through the
+ * life of an animation, such as start, repeat, and end.
+ *
+ * @param listener the listener to be added to the current set of listeners
+ * for this animation.
+ */
+ public void addListener(TransitionListener listener) {
+ if (mListeners == null) {
+ mListeners = new ArrayList<TransitionListener>();
+ }
+ mListeners.add(listener);
+ }
+
+ /**
+ * Removes a listener from the set listening to this animation.
+ *
+ * @param listener the listener to be removed from the current set of
+ * listeners for this transition.
+ */
+ public void removeListener(TransitionListener listener) {
+ if (mListeners == null) {
+ return;
+ }
+ mListeners.remove(listener);
+ if (mListeners.size() == 0) {
+ mListeners = null;
+ }
+ }
+
+ /**
+ * Gets the set of {@link TransitionListener} objects that are currently
+ * listening for events on this <code>Transition</code> object.
+ *
+ * @return ArrayList<TransitionListener> The set of listeners.
+ */
+ public ArrayList<TransitionListener> getListeners() {
+ return mListeners;
+ }
+
+ @Override
+ public String toString() {
+ return toString("");
+ }
+
+ String toString(String indent) {
+ String result = indent + getClass().getSimpleName() + "@" +
+ Integer.toHexString(hashCode()) + ": ";
+ result += "dur(" + mDuration + ") ";
+ result += "dly(" + mStartDelay + ") ";
+ result += "interp(" + mInterpolator + ") ";
+ result += "tgts(";
+ if (mTargetIds != null) {
+ for (int i = 0; i < mTargetIds.length; ++i) {
+ if (i > 0) {
+ result += ", ";
+ }
+ result += mTargetIds[i];
+ }
+ }
+ if (mTargets != null) {
+ for (int i = 0; i < mTargets.length; ++i) {
+ if (i > 0) {
+ result += ", ";
+ }
+ result += mTargets[i];
+ }
+ }
+ result += ")";
+ return result;
+ }
+
+ /**
+ * A transition listener receives notifications from a transition.
+ * Notifications indicate transition lifecycle events: when the transition
+ * begins, ends, or is canceled.
+ */
+ public static interface TransitionListener {
+ /**
+ * Notification about the start of the transition.
+ *
+ * @param transition The started transition.
+ */
+ void onTransitionStart(Transition transition);
+
+ /**
+ * Notification about the end of the transition. Canceled transitions
+ * will always notify listeners of both the cancellation and end
+ * events. That is, {@link #onTransitionEnd()} is always called,
+ * regardless of whether the transition was canceled or played
+ * through to completion.
+ *
+ * @param transition The transition which reached its end.
+ */
+ void onTransitionEnd(Transition transition);
+
+ /**
+ * Notification about the cancellation of the transition.
+ *
+ * @param transition The transition which was canceled.
+ */
+ void onTransitionCancel(Transition transition);
+ }
+
+ /**
+ * Utility adapter class to avoid having to override all three methods
+ * whenever someone just wants to listen for a single event.
+ *
+ * @hide
+ * */
+ public static class TransitionListenerAdapter implements TransitionListener {
+ @Override
+ public void onTransitionStart(Transition transition) {
+ }
+
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ }
+
+ @Override
+ public void onTransitionCancel(Transition transition) {
+ }
+ }
+
+}
diff --git a/core/java/android/view/transition/TransitionGroup.java b/core/java/android/view/transition/TransitionGroup.java
new file mode 100644
index 0000000..363872a
--- /dev/null
+++ b/core/java/android/view/transition/TransitionGroup.java
@@ -0,0 +1,292 @@
+/*
+ * 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 android.view.transition;
+
+import android.animation.Animator;
+import android.util.AndroidRuntimeException;
+import android.util.LongSparseArray;
+import android.util.SparseArray;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * A TransitionGroup is a parent of child transitions (including other
+ * TransitionGroups). Using TransitionGroups enables more complex
+ * choreography of transitions, where some groups play {@link #TOGETHER} and
+ * others play {@link #SEQUENTIALLY}. For example, {@link AutoTransition}
+ * uses a TransitionGroup to sequentially play a Fade(Fade.OUT), followed by
+ * a {@link Move}, followed by a Fade(Fade.OUT) transition.
+ */
+public class TransitionGroup extends Transition {
+
+ ArrayList<Transition> mTransitions = new ArrayList<Transition>();
+ private boolean mPlayTogether = true;
+ int mCurrentListeners;
+ boolean mStarted = false;
+
+ /**
+ * A flag used to indicate that the child transitions of this group
+ * should all start at the same time.
+ */
+ public static final int TOGETHER = 0;
+ /**
+ * A flag used to indicate that the child transitions of this group should
+ * play in sequence; when one child transition ends, the next child
+ * transition begins. Note that a transition does not end until all
+ * instances of it (which are playing on all applicable targets of the
+ * transition) end.
+ */
+ public static final int SEQUENTIALLY = 1;
+
+ /**
+ * Constructs an empty transition group. Add child transitions to the
+ * group by calling to {@link #addTransitions(Transition...)} )}. By default,
+ * child transitions will play {@link #TOGETHER}.
+ */
+ public TransitionGroup() {
+ }
+
+ /**
+ * Constructs an empty transition group with the specified ordering.
+ *
+ * @param ordering {@link #TOGETHER} to start this group's child
+ * transitions together, {@link #SEQUENTIALLY} to play the child
+ * transitions in sequence.
+ * @see #setOrdering(int)
+ */
+ public TransitionGroup(int ordering) {
+ setOrdering(ordering);
+ }
+
+ /**
+ * Sets the play order of this group's child transitions.
+ *
+ * @param ordering {@link #TOGETHER} to start this group's child
+ * transitions together, {@link #SEQUENTIALLY} to play the child
+ * transitions in sequence.
+ */
+ public void setOrdering(int ordering) {
+ switch (ordering) {
+ case SEQUENTIALLY:
+ mPlayTogether = false;
+ break;
+ case TOGETHER:
+ mPlayTogether = true;
+ break;
+ default:
+ throw new AndroidRuntimeException("Invalid parameter for TransitionGroup " +
+ "ordering: " + ordering);
+ }
+ }
+
+ /**
+ * Adds child transitions to this group. The order of the child transitions
+ * passed in determines the order in which they are started.
+ *
+ * @param transitions A list of child transition to be added to this group.
+ */
+ public void addTransitions(Transition... transitions) {
+ if (transitions != null) {
+ int numTransitions = transitions.length;
+ for (int i = 0; i < numTransitions; ++i) {
+ mTransitions.add(transitions[i]);
+ }
+ }
+ }
+
+ /**
+ * Removes the specified child transition from this group.
+ *
+ * @param transition The transition to be removed.
+ */
+ public void removeTransition(Transition transition) {
+ mTransitions.remove(transition);
+ }
+
+ /**
+ * Sets up listeners for each of the child transitions. This is used to
+ * determine when this transition group is finished (all child transitions
+ * must finish first).
+ */
+ private void setupStartEndListeners() {
+ for (Transition childTransition : mTransitions) {
+ childTransition.addListener(mListener);
+ }
+ mCurrentListeners = mTransitions.size();
+ }
+
+ /**
+ * This listener is used to detect when all child transitions are done, at
+ * which point this transition group is also done.
+ */
+ private TransitionListener mListener = new TransitionListenerAdapter() {
+ @Override
+ public void onTransitionStart(Transition transition) {
+ if (!mStarted) {
+ startTransition();
+ mStarted = true;
+ }
+ }
+
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ --mCurrentListeners;
+ if (mCurrentListeners == 0) {
+ // All child trans
+ mStarted = false;
+ endTransition();
+ }
+ transition.removeListener(this);
+ }
+ };
+
+ /**
+ * @hide
+ */
+ @Override
+ protected void prePlay(ViewGroup sceneRoot,
+ HashMap<View, TransitionValues> startValues,
+ SparseArray<TransitionValues> startIdValues,
+ LongSparseArray<TransitionValues> startItemIdValues,
+ HashMap<View, TransitionValues> endValues,
+ SparseArray<TransitionValues> endIdValues,
+ LongSparseArray<TransitionValues> endItemIdValues) {
+ for (Transition childTransition : mTransitions) {
+ childTransition.prePlay(sceneRoot, startValues, startIdValues, startItemIdValues,
+ endValues, endIdValues, endItemIdValues);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ protected void play(ViewGroup sceneRoot,
+ final HashMap<View, TransitionValues> startValues,
+ final SparseArray<TransitionValues> startIdValues,
+ final LongSparseArray<TransitionValues> startItemIdValues,
+ final HashMap<View, TransitionValues> endValues,
+ final SparseArray<TransitionValues> endIdValues,
+ final LongSparseArray<TransitionValues> endItemIdValues) {
+ setupStartEndListeners();
+ final ViewGroup finalSceneRoot = sceneRoot;
+ if (!mPlayTogether) {
+ // Setup sequence with listeners
+ // TODO: Need to add listeners in such a way that we can remove them later if canceled
+ for (int i = 1; i < mTransitions.size(); ++i) {
+ Transition previousTransition = mTransitions.get(i - 1);
+ final Transition nextTransition = mTransitions.get(i);
+ previousTransition.addListener(new TransitionListenerAdapter() {
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ nextTransition.play(finalSceneRoot,
+ startValues, startIdValues, startItemIdValues,
+ endValues, endIdValues, endItemIdValues);
+ transition.removeListener(this);
+ }
+ });
+ }
+ Transition firstTransition = mTransitions.get(0);
+ if (firstTransition != null) {
+ firstTransition.play(finalSceneRoot, startValues, startIdValues, startItemIdValues,
+ endValues, endIdValues, endItemIdValues);
+ }
+ } else {
+ for (Transition childTransition : mTransitions) {
+ childTransition.play(finalSceneRoot, startValues, startIdValues, startItemIdValues,
+ endValues, endIdValues, endItemIdValues);
+ }
+ }
+ }
+
+ @Override
+ protected Animator play(ViewGroup sceneRoot,
+ TransitionValues startValues, TransitionValues endValues) {
+ final View view = (endValues != null) ? endValues.view :
+ (startValues != null) ? startValues.view : null;
+ final int targetId = (view != null) ? view.getId() : -1;
+ // TODO: not sure this is a valid check - what about auto-targets? No need for ids.
+ if (targetId < 0) {
+ return null;
+ }
+ setupStartEndListeners();
+ if (!mPlayTogether) {
+ final ViewGroup finalSceneRoot = sceneRoot;
+ final TransitionValues finalStartValues = startValues;
+ final TransitionValues finalEndValues = endValues;
+ // Setup sequence with listeners
+ // TODO: Need to add listeners in such a way that we can remove them later if canceled
+ for (int i = 1; i < mTransitions.size(); ++i) {
+ Transition previousTransition = mTransitions.get(i - 1);
+ final Transition nextTransition = mTransitions.get(i);
+ previousTransition.addListener(new TransitionListenerAdapter() {
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ nextTransition.startTransition();
+ if (nextTransition.isValidTarget(view, targetId)) {
+ animate(nextTransition.play(finalSceneRoot, finalStartValues,
+ finalEndValues));
+ } else {
+ nextTransition.endTransition();
+ }
+ }
+ });
+ }
+ Transition firstTransition = mTransitions.get(0);
+ if (firstTransition != null) {
+ firstTransition.startTransition();
+ if (firstTransition.isValidTarget(view, targetId)) {
+ animate(firstTransition.play(finalSceneRoot, finalStartValues, finalEndValues));
+ } else {
+ firstTransition.endTransition();
+ }
+ }
+ } else {
+ for (Transition childTransition : mTransitions) {
+ childTransition.startTransition();
+ if (childTransition.isValidTarget(view, targetId)) {
+ animate(childTransition.play(sceneRoot, startValues, endValues));
+ } else {
+ childTransition.endTransition();
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected void captureValues(TransitionValues transitionValues, boolean start) {
+ int targetId = transitionValues.view.getId();
+ for (Transition childTransition : mTransitions) {
+ if (childTransition.isValidTarget(transitionValues.view, targetId)) {
+ childTransition.captureValues(transitionValues, start);
+ }
+ }
+ }
+
+ @Override
+ String toString(String indent) {
+ String result = super.toString(indent);
+ for (int i = 0; i < mTransitions.size(); ++i) {
+ result += "\n" + mTransitions.get(i).toString(indent + " ");
+ }
+ return result;
+ }
+
+}
diff --git a/core/java/android/view/transition/TransitionInflater.java b/core/java/android/view/transition/TransitionInflater.java
new file mode 100644
index 0000000..a5f5836
--- /dev/null
+++ b/core/java/android/view/transition/TransitionInflater.java
@@ -0,0 +1,392 @@
+/*
+ * 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 android.view.transition;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.util.AttributeSet;
+import android.util.SparseArray;
+import android.util.Xml;
+import android.view.InflateException;
+import android.view.ViewGroup;
+import android.view.animation.AnimationUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * This class inflates scenes and transitions from resource files.
+ */
+public class TransitionInflater {
+
+ // We only need one inflater for any given context. Also, this allows us to associate
+ // ids with unique instances per-Context, used to avoid re-inflating
+ // already-inflated resources into new/different instances
+ private static final HashMap<Context, TransitionInflater> sInflaterMap =
+ new HashMap<Context, TransitionInflater>();
+
+ private Context mContext;
+ // TODO: do we need id maps for transitions and transitionMgrs as well?
+ SparseArray<Scene> mScenes = new SparseArray<Scene>();
+
+ private TransitionInflater(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Obtains the TransitionInflater from the given context.
+ */
+ public static TransitionInflater from(Context context) {
+ TransitionInflater inflater = sInflaterMap.get(context);
+ if (inflater != null) {
+ return inflater;
+ }
+ inflater = new TransitionInflater(context);
+ sInflaterMap.put(context, inflater);
+ return inflater;
+ }
+
+ /**
+ * Loads a {@link Transition} object from a resource
+ *
+ * @param resource The resource id of the transition to load
+ * @return The loaded Transition object
+ * @throws android.content.res.Resources.NotFoundException when the
+ * transition cannot be loaded
+ */
+ public Transition inflateTransition(int resource) {
+ XmlResourceParser parser = mContext.getResources().getXml(resource);
+ try {
+ return createTransitionFromXml(parser, Xml.asAttributeSet(parser), null);
+ } catch (XmlPullParserException e) {
+ InflateException ex = new InflateException(e.getMessage());
+ ex.initCause(e);
+ throw ex;
+ } catch (IOException e) {
+ InflateException ex = new InflateException(
+ parser.getPositionDescription()
+ + ": " + e.getMessage());
+ ex.initCause(e);
+ throw ex;
+ } finally {
+ parser.close();
+ }
+ }
+
+ /**
+ * Loads a {@link TransitionManager} object from a resource
+ *
+ *
+ *
+ * @param resource The resource id of the transition manager to load
+ * @return The loaded TransitionManager object
+ * @throws android.content.res.Resources.NotFoundException when the
+ * transition manager cannot be loaded
+ */
+ public TransitionManager inflateTransitionManager(int resource, ViewGroup sceneRoot) {
+ XmlResourceParser parser = mContext.getResources().getXml(resource);
+ try {
+ return createTransitionManagerFromXml(parser, Xml.asAttributeSet(parser), sceneRoot);
+ } catch (XmlPullParserException e) {
+ InflateException ex = new InflateException(e.getMessage());
+ ex.initCause(e);
+ throw ex;
+ } catch (IOException e) {
+ InflateException ex = new InflateException(
+ parser.getPositionDescription()
+ + ": " + e.getMessage());
+ ex.initCause(e);
+ throw ex;
+ } finally {
+ parser.close();
+ }
+ }
+
+ /**
+ * Loads a {@link Scene} object from a resource
+ *
+ * @param resource The resource id of the scene to load
+ * @return The loaded Scene object
+ * @throws android.content.res.Resources.NotFoundException when the scene
+ * cannot be loaded
+ */
+ public Scene inflateScene(int resource, ViewGroup parent) {
+ Scene scene = mScenes.get(resource);
+ if (scene != null) {
+ return scene;
+ }
+ XmlResourceParser parser = mContext.getResources().getXml(resource);
+ try {
+ scene = createSceneFromXml(parser, Xml.asAttributeSet(parser), parent);
+ mScenes.put(resource, scene);
+ return scene;
+ } catch (XmlPullParserException e) {
+ InflateException ex = new InflateException(e.getMessage());
+ ex.initCause(e);
+ throw ex;
+ } catch (IOException e) {
+ InflateException ex = new InflateException(
+ parser.getPositionDescription()
+ + ": " + e.getMessage());
+ ex.initCause(e);
+ throw ex;
+ } finally {
+ parser.close();
+ }
+ }
+
+
+ //
+ // Transition loading
+ //
+
+ private Transition createTransitionFromXml(XmlPullParser parser,
+ AttributeSet attrs, TransitionGroup transitionGroup)
+ throws XmlPullParserException, IOException {
+
+ Transition transition = null;
+
+ // Make sure we are on a start tag.
+ int type;
+ int depth = parser.getDepth();
+
+ while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
+ && type != XmlPullParser.END_DOCUMENT) {
+
+ boolean newTransition = false;
+
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ String name = parser.getName();
+ if ("fade".equals(name)) {
+ transition = new Fade();
+ newTransition = true;
+ } else if ("move".equals(name)) {
+ transition = new Move();
+ newTransition = true;
+ } else if ("slide".equals(name)) {
+ transition = new Slide();
+ newTransition = true;
+ } else if ("autoTransition".equals(name)) {
+ transition = new AutoTransition();
+ newTransition = true;
+ } else if ("recolor".equals(name)) {
+ transition = new Recolor();
+ newTransition = true;
+ } else if ("transitionGroup".equals(name)) {
+ transition = new TransitionGroup();
+ createTransitionFromXml(parser, attrs, ((TransitionGroup) transition));
+ newTransition = true;
+ } else if ("targets".equals(name)) {
+ if (parser.getDepth() - 1 > depth && transition != null) {
+ // We're inside the child tag - add targets to the child
+ getTargetIDs(parser, attrs, transition);
+ } else if (parser.getDepth() - 1 == depth && transitionGroup != null) {
+ // add targets to the group
+ getTargetIDs(parser, attrs, transitionGroup);
+ }
+ }
+ if (transition != null || "targets".equals(name)) {
+ if (newTransition) {
+ loadTransition(transition, attrs);
+ if (transitionGroup != null) {
+ transitionGroup.addTransitions(transition);
+ }
+ }
+ } else {
+ throw new RuntimeException("Unknown scene name: " + parser.getName());
+ }
+ }
+
+ return transition;
+ }
+
+ private void getTargetIDs(XmlPullParser parser,
+ AttributeSet attrs, Transition transition) throws XmlPullParserException, IOException {
+
+ // Make sure we are on a start tag.
+ int type;
+ int depth = parser.getDepth();
+
+ ArrayList<Integer> targetIds = new ArrayList<Integer>();
+ while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
+ && type != XmlPullParser.END_DOCUMENT) {
+
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ String name = parser.getName();
+ if (name.equals("target")) {
+ TypedArray a = mContext.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.Transition);
+ int id = a.getResourceId(com.android.internal.R.styleable.Transition_targetID, -1);
+ if (id >= 0) {
+ targetIds.add(id);
+ }
+ } else {
+ throw new RuntimeException("Unknown scene name: " + parser.getName());
+ }
+ }
+ int numTargets = targetIds.size();
+ if (numTargets > 0) {
+ int[] targetsArray = new int[numTargets];
+ for (int i = 0; i < targetIds.size(); ++i) {
+ targetsArray[i] = targetIds.get(i);
+ }
+ transition.setTargetIds(targetsArray);
+ }
+ }
+
+ private Transition loadTransition(Transition transition, AttributeSet attrs)
+ throws Resources.NotFoundException {
+
+ TypedArray a =
+ mContext.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Transition);
+ long duration = a.getInt(com.android.internal.R.styleable.Transition_duration, -1);
+ if (duration >= 0) {
+ transition.setDuration(duration);
+ }
+ long startOffset = a.getInt(com.android.internal.R.styleable.Transition_startOffset, -1);
+ if (startOffset > 0) {
+ transition.setStartDelay(startOffset);
+ }
+ final int resID =
+ a.getResourceId(com.android.internal.R.styleable.Animator_interpolator, 0);
+ if (resID > 0) {
+ transition.setInterpolator(AnimationUtils.loadInterpolator(mContext, resID));
+ }
+ a.recycle();
+ return transition;
+ }
+
+ //
+ // TransitionManager loading
+ //
+
+ private TransitionManager createTransitionManagerFromXml(XmlPullParser parser,
+ AttributeSet attrs, ViewGroup sceneRoot) throws XmlPullParserException, IOException {
+
+ // Make sure we are on a start tag.
+ int type;
+ int depth = parser.getDepth();
+ TransitionManager transitionManager = null;
+
+ while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
+ && type != XmlPullParser.END_DOCUMENT) {
+
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ String name = parser.getName();
+ if (name.equals("transitionManager")) {
+ transitionManager = new TransitionManager();
+ } else if (name.equals("transition") && (transitionManager != null)) {
+ loadTransition(attrs, sceneRoot, transitionManager);
+ } else {
+ throw new RuntimeException("Unknown scene name: " + parser.getName());
+ }
+ }
+ return transitionManager;
+ }
+
+ private void loadTransition(AttributeSet attrs, ViewGroup sceneRoot,
+ TransitionManager transitionManager)
+ throws Resources.NotFoundException {
+
+ TypedArray a = mContext.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.TransitionManager);
+ int transitionId = attrs.getAttributeResourceValue(
+ com.android.internal.R.styleable.TransitionManager_transition, -1);
+ Scene fromScene = null, toScene = null;
+ int fromId = attrs.getAttributeResourceValue(
+ com.android.internal.R.styleable.TransitionManager_fromScene, -1);
+ if (fromId >= 0) fromScene = inflateScene(fromId, sceneRoot);
+ int toId = attrs.getAttributeResourceValue(
+ com.android.internal.R.styleable.TransitionManager_toScene, -1);
+ if (toId >= 0) toScene = inflateScene(toId, sceneRoot);
+ if (transitionId >= 0) {
+ Transition transition = inflateTransition(transitionId);
+ if (transition != null) {
+ if (fromScene != null) {
+ if (toScene == null){
+ throw new RuntimeException("No matching toScene for given fromScene " +
+ "for transition ID " + transitionId);
+ } else {
+ transitionManager.setTransition(fromScene, toScene, transition);
+ }
+ } else if (toId >= 0) {
+ transitionManager.setTransition(toScene, transition);
+ }
+ }
+ }
+ a.recycle();
+ }
+
+ //
+ // Scene loading
+ //
+
+ private Scene createSceneFromXml(XmlPullParser parser, AttributeSet attrs, ViewGroup parent)
+ throws XmlPullParserException, IOException {
+ Scene scene = null;
+
+ // Make sure we are on a start tag.
+ int type;
+ int depth = parser.getDepth();
+
+ while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
+ && type != XmlPullParser.END_DOCUMENT) {
+
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ String name = parser.getName();
+ if (name.equals("scene")) {
+ scene = loadScene(attrs, parent);
+ } else {
+ throw new RuntimeException("Unknown scene name: " + parser.getName());
+ }
+ }
+
+ return scene;
+ }
+
+ private Scene loadScene(AttributeSet attrs, ViewGroup parent)
+ throws Resources.NotFoundException {
+
+ Scene scene;
+ TypedArray a = mContext.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.Scene);
+ int layoutId = a.getResourceId(com.android.internal.R.styleable.Scene_layout, -1);
+ if (layoutId >= 0) {
+ scene = new Scene(parent, layoutId, mContext);
+ } else {
+ scene = new Scene(parent);
+ }
+ a.recycle();
+ return scene;
+ }
+}
diff --git a/core/java/android/view/transition/TransitionManager.java b/core/java/android/view/transition/TransitionManager.java
new file mode 100644
index 0000000..5a1991c
--- /dev/null
+++ b/core/java/android/view/transition/TransitionManager.java
@@ -0,0 +1,261 @@
+/*
+ * 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 android.view.transition;
+
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+
+import java.util.HashMap;
+
+/**
+ * This class manages the set of transitions that fire when there is a
+ * change of {@link Scene}. To use the manager, add scenes along with
+ * transition objects with calls to {@link #setTransition(Scene, Transition)}
+ * or {@link #setTransition(Scene, Scene, Transition)}. Setting specific
+ * transitions for scene changes is not required; by default, a Scene change
+ * will use {@link AutoTransition} to do something reasonable for most
+ * situations. Specifying other transitions for particular scene changes is
+ * only necessary if the application wants different transition behavior
+ * in these situations.
+ */
+public class TransitionManager {
+ // TODO: how to handle enter/exit?
+
+ private static final Transition sDefaultTransition = new AutoTransition();
+ private Transition mDefaultTransition = new AutoTransition();
+
+ HashMap<Scene, Transition> mSceneTransitions = new HashMap<Scene, Transition>();
+ HashMap<Scene, HashMap<Scene, Transition>> mScenePairTransitions =
+ new HashMap<Scene, HashMap<Scene, Transition>>();
+
+ /**
+ * Sets the transition to be used for any scene change for which no
+ * other transition is explicitly set. The initial value is
+ * an {@link AutoTransition} instance.
+ *
+ * @param transition The default transition to be used for scene changes.
+ */
+ public void setDefaultTransition(Transition transition) {
+ mDefaultTransition = transition;
+ }
+
+ /**
+ * Gets the current default transition. The initial value is an {@link
+ * AutoTransition} instance.
+ *
+ * @return The current default transition.
+ * @see #setDefaultTransition(Transition)
+ */
+ public Transition getDefaultTransition() {
+ return mDefaultTransition;
+ }
+
+ /**
+ * Sets a specific transition to occur when the given scene is entered.
+ *
+ * @param scene The scene which, when applied, will cause the given
+ * transition to run.
+ * @param transition The transition that will play when the given scene is
+ * entered. A value of null will result in the default behavior of
+ * using {@link AutoTransition}.
+ */
+ public void setTransition(Scene scene, Transition transition) {
+ mSceneTransitions.put(scene, transition);
+ }
+
+ /**
+ * Sets a specific transition to occur when the given pair of scenes is
+ * exited/entered.
+ *
+ * @param fromScene The scene being exited when the given transition will
+ * be run
+ * @param toScene The scene being entered when the given transition will
+ * be run
+ * @param transition The transition that will play when the given scene is
+ * entered. A value of null will result in the default behavior of
+ * using {@link AutoTransition}.
+ */
+ public void setTransition(Scene fromScene, Scene toScene, Transition transition) {
+ HashMap<Scene, Transition> sceneTransitionMap = mScenePairTransitions.get(toScene);
+ if (sceneTransitionMap == null) {
+ sceneTransitionMap = new HashMap<Scene, Transition>();
+ mScenePairTransitions.put(toScene, sceneTransitionMap);
+ }
+ sceneTransitionMap.put(fromScene, transition);
+ }
+
+ /**
+ * Returns the Transition for the given scene being entered. The result
+ * depends not only on the given scene, but also the scene which the
+ * {@link Scene#getSceneRoot() sceneRoot} of the Scene is currently in.
+ *
+ * @param scene The scene being entered
+ * @return The Transition to be used for the given scene change. If no
+ * Transition was specified for this scene change, {@link AutoTransition}
+ * will be used instead.
+ */
+ private Transition getTransition(Scene scene) {
+ Transition transition = null;
+ ViewGroup sceneRoot = scene.getSceneRoot();
+ if (sceneRoot != null) {
+ // TODO: cached in Scene instead? long-term, cache in View itself
+ Scene currScene = sceneRoot.getCurrentScene();
+ if (currScene != null) {
+ HashMap<Scene, Transition> sceneTransitionMap = mScenePairTransitions.get(scene);
+ if (sceneTransitionMap != null) {
+ transition = sceneTransitionMap.get(currScene);
+ if (transition != null) {
+ return transition;
+ }
+ }
+ }
+ }
+ transition = mSceneTransitions.get(scene);
+ return (transition != null) ? transition : new AutoTransition();
+ }
+
+ /**
+ * This is where all of the work of a transition/scene-change is
+ * orchestrated. This method captures the start values for the given
+ * transition, exits the current Scene, enters the new scene, captures
+ * the end values for the transition, and finally plays the
+ * resulting values-populated transition.
+ *
+ * @param scene The scene being entered
+ * @param transition The transition to play for this scene change
+ */
+ private static void changeScene(Scene scene, final Transition transition) {
+
+ final ViewGroup sceneRoot = scene.getSceneRoot();
+
+ // Capture current values
+ if (transition != null) {
+ transition.captureValues(sceneRoot, true);
+ }
+
+ // Notify previous scene that it is being exited
+ Scene previousScene = sceneRoot.getCurrentScene();
+ if (previousScene != null) {
+ previousScene.exit();
+ }
+
+ scene.enter();
+
+ if (transition != null) {
+ final ViewTreeObserver observer = sceneRoot.getViewTreeObserver();
+ observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+ public boolean onPreDraw() {
+ sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
+ transition.captureValues(sceneRoot, false);
+ transition.play(sceneRoot);
+ return true;
+ }
+ });
+ }
+ }
+
+ /**
+ * Change to the given scene, using the
+ * appropriate transition for this particular scene change
+ * (as specified to the TransitionManager, or the default
+ * if no such transition exists).
+ *
+ * @param scene The Scene to change to
+ */
+ public void transitionTo(Scene scene) {
+ // Auto transition if there is no transition declared for the Scene, but there is
+ // a root or parent view
+ changeScene(scene, getTransition(scene));
+
+ }
+
+ /**
+ * Static utility method to simply change to the given scene using
+ * the default transition for TransitionManager.
+ *
+ * @param scene The Scene to change to
+ */
+ public static void go(Scene scene) {
+ changeScene(scene, sDefaultTransition);
+ }
+
+ /**
+ * Static utility method to simply change to the given scene using
+ * the given transition.
+ *
+ * <p>Passing in <code>null</code> for the transition parameter will
+ * result in the scene changing without any transition running, and is
+ * equivalent to calling {@link Scene#exit()} on the scene root's
+ * {@link ViewGroup#getCurrentScene() current scene}, followed by
+ * {@link Scene#enter()} on the scene specified by the <code>scene</code>
+ * parameter.</p>
+ *
+ * @param scene The Scene to change to
+ * @param transition The transition to use for this scene change. A
+ * value of null causes the scene change to happen with no transition.
+ */
+ public static void go(Scene scene, Transition transition) {
+ changeScene(scene, transition);
+ }
+
+ /**
+ * Static utility method to simply change to a scene defined by the
+ * code in the given runnable, which will be executed after
+ * the current values have been captured for the transition.
+ * This is equivalent to creating a Scene and calling {@link
+ * Scene#setEnterAction(Runnable)} with the runnable, then calling
+ * {@link #go(Scene, Transition)}. The transition used will be the
+ * default provided by TransitionManager.
+ *
+ * @param sceneRoot The root of the View hierarchy used when this scene
+ * runs a transition automatically.
+ * @param action The runnable whose {@link Runnable#run() run()} method will
+ * be called.
+ */
+ public static void go(ViewGroup sceneRoot, Runnable action) {
+ Scene scene = new Scene(sceneRoot);
+ scene.setEnterAction(action);
+ changeScene(scene, sDefaultTransition);
+ }
+
+ /**
+ * Static utility method to simply change to a scene defined by the
+ * code in the given runnable, which will be executed after
+ * the current values have been captured for the transition.
+ * This is equivalent to creating a Scene and calling {@link
+ * Scene#setEnterAction(Runnable)} with the runnable, then calling
+ * {@link #go(Scene, Transition)}. The given transition will be
+ * used to animate the changes.
+ *
+ * <p>Passing in <code>null</code> for the transition parameter will
+ * result in the scene changing without any transition running, and is
+ * equivalent to calling {@link Scene#exit()} on the scene root's
+ * {@link ViewGroup#getCurrentScene() current scene}, followed by
+ * {@link Scene#enter()} on a new scene specified by the
+ * <code>action</code> parameter.</p>
+ *
+ * @param sceneRoot The root of the View hierarchy to run the transition on.
+ * @param action The runnable whose {@link Runnable#run() run()} method will
+ * be called.
+ * @param transition The transition to use for this change. A
+ * value of null causes the change to happen with no transition.
+ */
+ public static void go(ViewGroup sceneRoot, Runnable action, Transition transition) {
+ Scene scene = new Scene(sceneRoot);
+ scene.setEnterAction(action);
+ changeScene(scene, transition);
+ }
+}
diff --git a/core/java/android/view/transition/TransitionValues.java b/core/java/android/view/transition/TransitionValues.java
new file mode 100644
index 0000000..120ace8
--- /dev/null
+++ b/core/java/android/view/transition/TransitionValues.java
@@ -0,0 +1,60 @@
+/*
+ * 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 android.view.transition;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.HashMap;
+
+/**
+ * Data structure which holds cached values for the transition.
+ * The view field is the target which all of the values pertain to.
+ * The values field is a hashmap which holds information for fields
+ * according to names selected by the transitions. These names should
+ * be unique to avoid clobbering values stored by other transitions,
+ * such as the convention project:transition_name:property_name. For
+ * example, the platform might store a property "alpha" in a transition
+ * "Fader" as "android:fader:alpha".
+ *
+ * <p>These values are cached during the
+ * {@link Transition#captureValues(TransitionValues, boolean)}
+ * capture} phases of a scene change, once when the start values are captured
+ * and again when the end values are captured. These start/end values are then
+ * passed into the transitions during the play phase of the scene change,
+ * for {@link Transition#prePlay(ViewGroup, TransitionValues, TransitionValues)} and
+ * for {@link Transition#play(ViewGroup, TransitionValues, TransitionValues)}.</p>
+ */
+public class TransitionValues {
+
+ /**
+ * The View with these values
+ */
+ public View view;
+
+ /**
+ * The set of values tracked by transitions for this scene
+ */
+ public final HashMap<String, Object> values = new HashMap<String, Object>();
+
+ @Override
+ public String toString() {
+ String returnValue = "TransitionValues@" + Integer.toHexString(hashCode()) + ":\n";
+ returnValue += " view = " + view + "\n";
+ returnValue += " values = " + values + "\n";
+ return returnValue;
+ }
+} \ No newline at end of file
diff --git a/core/java/android/view/transition/Visibility.java b/core/java/android/view/transition/Visibility.java
new file mode 100644
index 0000000..a3e6e77
--- /dev/null
+++ b/core/java/android/view/transition/Visibility.java
@@ -0,0 +1,243 @@
+/*
+ * 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 android.view.transition;
+
+import android.animation.Animator;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * This transition tracks changes to the visibility of target views in the
+ * start and end scenes. Visibility is determined not just by the
+ * {@link View#setVisibility(int)} state of views, but also whether
+ * views exist in the current view hierarchy. The class is intended to be a
+ * utility for subclasses such as {@link Fade}, which use this visibility
+ * information to determine the specific animations to run when visibility
+ * changes occur. Subclasses should implement one or more of the methods
+ * {@link #preAppear(ViewGroup, View, int, View, int)},
+ * {@link #preDisappear(ViewGroup, View, int, View, int)},
+ * {@link #appear(ViewGroup, View, int, View, int)}, and
+ * {@link #disappear(ViewGroup, View, int, View, int)}.
+ */
+public abstract class Visibility extends Transition {
+
+ private static final String PROPNAME_VISIBILITY = "android:visibility:visibility";
+ private static final String PROPNAME_PARENT = "android:visibility:parent";
+
+ @Override
+ protected void captureValues(TransitionValues values, boolean start) {
+ int visibility = values.view.getVisibility();
+ values.values.put(PROPNAME_VISIBILITY, visibility);
+ values.values.put(PROPNAME_PARENT, values.view.getParent());
+ }
+
+ @Override
+ protected boolean prePlay(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ boolean visibilityChange = false;
+ boolean fadeIn = false;
+ int startVisibility, endVisibility;
+ View startParent, endParent;
+ if (startValues != null) {
+ startVisibility = (Integer) startValues.values.get(PROPNAME_VISIBILITY);
+ startParent = (View) startValues.values.get(PROPNAME_PARENT);
+ } else {
+ startVisibility = -1;
+ startParent = null;
+ }
+ if (endValues != null) {
+ endVisibility = (Integer) endValues.values.get(PROPNAME_VISIBILITY);
+ endParent = (View) endValues.values.get(PROPNAME_PARENT);
+ } else {
+ endVisibility = -1;
+ endParent = null;
+ }
+ boolean existenceChange = false;
+ if (startValues != null && endValues != null) {
+ if (startVisibility == endVisibility && startParent == endParent) {
+ return false;
+ } else {
+ if (startVisibility != endVisibility) {
+ if (startVisibility == View.VISIBLE) {
+ fadeIn = false;
+ visibilityChange = true;
+ } else if (endVisibility == View.VISIBLE) {
+ fadeIn = true;
+ visibilityChange = true;
+ }
+ // no visibilityChange if going between INVISIBLE and GONE
+ } else if (startParent != endParent) {
+ existenceChange = true;
+ if (endParent == null) {
+ fadeIn = false;
+ visibilityChange = true;
+ } else if (startParent == null) {
+ fadeIn = true;
+ visibilityChange = true;
+ }
+ }
+ }
+ }
+ if (startValues == null) {
+ existenceChange = true;
+ fadeIn = true;
+ visibilityChange = true;
+ } else if (endValues == null) {
+ existenceChange = true;
+ fadeIn = false;
+ visibilityChange = true;
+ }
+ if (visibilityChange) {
+ if (fadeIn) {
+ return preAppear(sceneRoot, existenceChange ? null : startValues.view,
+ startVisibility, endValues.view, endVisibility);
+ } else {
+ return preDisappear(sceneRoot, startValues.view, startVisibility,
+ existenceChange ? null : endValues.view, endVisibility);
+ }
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ protected Animator play(ViewGroup sceneRoot, TransitionValues startValues,
+ TransitionValues endValues) {
+ boolean visibilityChange = false;
+ boolean fadeIn = false;
+ int startVisibility, endVisibility;
+ View startParent, endParent;
+ if (startValues != null) {
+ startVisibility = (Integer) startValues.values.get(PROPNAME_VISIBILITY);
+ startParent = (View) startValues.values.get(PROPNAME_PARENT);
+ } else {
+ startVisibility = -1;
+ startParent = null;
+ }
+ if (endValues != null) {
+ endVisibility = (Integer) endValues.values.get(PROPNAME_VISIBILITY);
+ endParent = (View) endValues.values.get(PROPNAME_PARENT);
+ } else {
+ endVisibility = -1;
+ endParent = null;
+ }
+ boolean existenceChange = false;
+ if (startValues != null && endValues != null) {
+ if (startVisibility == endVisibility && startParent == endParent) {
+ return null;
+ } else {
+ if (startVisibility != endVisibility) {
+ if (startVisibility == View.VISIBLE) {
+ fadeIn = false;
+ visibilityChange = true;
+ } else if (endVisibility == View.VISIBLE) {
+ fadeIn = true;
+ visibilityChange = true;
+ }
+ // no visibilityChange if going between INVISIBLE and GONE
+ } else if (startParent != endParent) {
+ existenceChange = true;
+ if (endParent == null) {
+ fadeIn = false;
+ visibilityChange = true;
+ } else if (startParent == null) {
+ fadeIn = true;
+ visibilityChange = true;
+ }
+ }
+ }
+ }
+ if (startValues == null) {
+ existenceChange = true;
+ fadeIn = true;
+ visibilityChange = true;
+ } else if (endValues == null) {
+ existenceChange = true;
+ fadeIn = false;
+ visibilityChange = true;
+ }
+ if (visibilityChange) {
+ if (fadeIn) {
+ return appear(sceneRoot, existenceChange ? null : startValues.view, startVisibility,
+ endValues.view, endVisibility);
+ } else {
+ return disappear(sceneRoot, startValues.view, startVisibility,
+ existenceChange ? null : endValues.view, endVisibility);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * The default implementation of this method does nothing. Subclasses
+ * should override if they need to set up anything prior to the
+ * transition starting.
+ *
+ * @param sceneRoot
+ * @param startView
+ * @param startVisibility
+ * @param endView
+ * @param endVisibility
+ * @return
+ */
+ protected boolean preAppear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) {
+ return true;
+ }
+
+ /**
+ * The default implementation of this method does nothing. Subclasses
+ * should override if they need to set up anything prior to the
+ * transition starting.
+ * @param sceneRoot
+ * @param startView
+ * @param startVisibility
+ * @param endView
+ * @param endVisibility
+ * @return
+ */
+ protected boolean preDisappear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) {
+ return true;
+ }
+
+ /**
+ * The default implementation of this method does nothing. Subclasses
+ * should override if they need to do anything when target objects
+ * appear during the scene change.
+ * @param sceneRoot
+ * @param startView
+ * @param startVisibility
+ * @param endView
+ * @param endVisibility
+ */
+ protected Animator appear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) { return null; }
+
+ /**
+ * The default implementation of this method does nothing. Subclasses
+ * should override if they need to do anything when target objects
+ * disappear during the scene change.
+ * @param sceneRoot
+ * @param startView
+ * @param startVisibility
+ * @param endView
+ * @param endVisibility
+ */
+ protected Animator disappear(ViewGroup sceneRoot, View startView, int startVisibility,
+ View endView, int endVisibility) { return null; }
+
+}
diff --git a/core/java/android/view/transition/package.html b/core/java/android/view/transition/package.html
new file mode 100644
index 0000000..37dc0ec
--- /dev/null
+++ b/core/java/android/view/transition/package.html
@@ -0,0 +1,25 @@
+<html>
+<body>
+<p>The classes in this package enable "scenes & transitions" functionality for
+view hiearchies.</p>
+
+<p>A <b>Scene</b> is an encapsulation of the state of a view hiearchy,
+including the views in that hierarchy and the various values (layout-related
+and otherwise) that those views have. A scene be defined by a layout hierarchy
+directly or some code which sets up the scene dynamically as it is entered.</p>
+
+<p>A <b>Transition</b> is a mechanism to automatically animate changes that occur
+when a new scene is entered. Some transition capabilities are automatic. That
+is, entering a scene may cause animations to run which fade out views that
+go away, move and resize existing views that change, and fade in views that
+become visible. There are additional transitions that can animate other
+attributes, such as color changes, and which can optionally be specified
+to take place during particular scene changes. Finally, developers can
+define their own Transition subclasses which monitor particular property
+changes and which run custom animations when those properties change values.</p>
+
+<p><b>TransitionManager</b> is used to specify custom transitions for particular
+scene changes, and to cause scene changes with transitions to take place.</p>
+
+</body>
+</html>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 8c7a374..939d435 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4378,6 +4378,28 @@
</declare-styleable>
<!-- ========================== -->
+ <!-- State class attributes -->
+ <!-- ========================== -->
+ <eat-comment />
+
+ <declare-styleable name="Scene">
+ <attr name="layout" />
+ </declare-styleable>
+
+ <declare-styleable name="Transition">
+ <attr name="duration" />
+ <attr name="startOffset" />
+ <attr name="interpolator" />
+ <attr name="targetID" format="reference" />
+ </declare-styleable>
+
+ <declare-styleable name="TransitionManager">
+ <attr name="transition" format="reference" />
+ <attr name="fromScene" format="reference" />
+ <attr name="toScene" format="reference" />
+ </declare-styleable>
+
+ <!-- ========================== -->
<!-- ValueAnimator class attributes -->
<!-- ========================== -->
<eat-comment />
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 03d7207..1f088a5 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2058,5 +2058,9 @@
<eat-comment />
<public type="attr" name="keyset" />
+ <public type="attr" name="targetID" />
+ <public type="attr" name="fromScene" />
+ <public type="attr" name="toScene" />
+ <public type="attr" name="transition" />
</resources>
diff --git a/tests/TransitionTests/Android.mk b/tests/TransitionTests/Android.mk
new file mode 100644
index 0000000..22fa638
--- /dev/null
+++ b/tests/TransitionTests/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := TransitionTests
+
+LOCAL_STATIC_JAVA_LIBRARIES += android-common
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_PACKAGE)
+
+# Use the following include to make our test apk.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/TransitionTests/AndroidManifest.xml b/tests/TransitionTests/AndroidManifest.xml
new file mode 100644
index 0000000..8cd36bf
--- /dev/null
+++ b/tests/TransitionTests/AndroidManifest.xml
@@ -0,0 +1,211 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.transitiontests"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <application
+ android:icon="@drawable/icon"
+ android:label="@string/app_name"
+ android:hardwareAccelerated="true"
+ android:theme="@style/AppTheme">
+ <activity android:label="@string/states_test1"
+ android:name="ScenesTestv21">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="@string/states_test_auto_targets"
+ android:name="ScenesTestAutoTargets">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="@string/states_test_auto_transition"
+ android:name="ScenesTestAutoTransition">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="@string/states_test_auto_transition2"
+ android:name="ScenesTestAutoTransition2">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="@string/contacts_expansion"
+ android:name="ContactsExpansion">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="Demo0"
+ android:name="Demo0">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="Demo1"
+ android:name="Demo1">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="Demo2"
+ android:name="Demo2">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="Demo3"
+ android:name="Demo3">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="Demo4"
+ android:name="Demo4">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="Demo5"
+ android:name="Demo5">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="LoginActivity"
+ android:name="LoginActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="LoginActivityFromResources"
+ android:name="LoginActivityFromResources">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="OverlayTest"
+ android:name="OverlayTest">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="ResourceLoadingTest"
+ android:name="ResourceLoadingTest">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="FadingTest"
+ android:name="FadingTest">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="UniqueIds"
+ android:name="UniqueIds">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="HitRectBug"
+ android:name="HitRectBug">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="SequenceTest"
+ android:name="SequenceTest">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="SequenceTestSimple"
+ android:name="SequenceTestSimple">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="ChangingText"
+ android:name="ChangingText">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="ClippingText"
+ android:name="ClippingText">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="ListViewAddRemove"
+ android:name="ListViewAddRemove">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="ListViewAddRemoveNoTransition"
+ android:name="ListViewAddRemoveNoTransition">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="CrossFadeDemo"
+ android:name="CrossFadeDemo">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="Reparenting"
+ android:name="Reparenting">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="SurfaceAndTextureViews"
+ android:name="SurfaceAndTextureViews">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:label="InstanceTargets"
+ android:name="InstanceTargets">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ </application>
+
+</manifest>
diff --git a/tests/TransitionTests/res/drawable-hdpi/icon.png b/tests/TransitionTests/res/drawable-hdpi/icon.png
new file mode 100644
index 0000000..8074c4c
--- /dev/null
+++ b/tests/TransitionTests/res/drawable-hdpi/icon.png
Binary files differ
diff --git a/tests/TransitionTests/res/drawable-ldpi/icon.png b/tests/TransitionTests/res/drawable-ldpi/icon.png
new file mode 100644
index 0000000..1095584
--- /dev/null
+++ b/tests/TransitionTests/res/drawable-ldpi/icon.png
Binary files differ
diff --git a/tests/TransitionTests/res/drawable-mdpi/icon.png b/tests/TransitionTests/res/drawable-mdpi/icon.png
new file mode 100644
index 0000000..a07c69f
--- /dev/null
+++ b/tests/TransitionTests/res/drawable-mdpi/icon.png
Binary files differ
diff --git a/tests/TransitionTests/res/drawable-nodpi/arrow_thumbnail.png b/tests/TransitionTests/res/drawable-nodpi/arrow_thumbnail.png
new file mode 100644
index 0000000..5cae8f2
--- /dev/null
+++ b/tests/TransitionTests/res/drawable-nodpi/arrow_thumbnail.png
Binary files differ
diff --git a/tests/TransitionTests/res/drawable-nodpi/self_portrait_square.jpg b/tests/TransitionTests/res/drawable-nodpi/self_portrait_square.jpg
new file mode 100644
index 0000000..8cce1c1
--- /dev/null
+++ b/tests/TransitionTests/res/drawable-nodpi/self_portrait_square.jpg
Binary files differ
diff --git a/tests/TransitionTests/res/drawable-nodpi/self_portrait_square_100.jpg b/tests/TransitionTests/res/drawable-nodpi/self_portrait_square_100.jpg
new file mode 100644
index 0000000..26c0a85
--- /dev/null
+++ b/tests/TransitionTests/res/drawable-nodpi/self_portrait_square_100.jpg
Binary files differ
diff --git a/tests/TransitionTests/res/drawable-nodpi/self_portrait_square_200.jpg b/tests/TransitionTests/res/drawable-nodpi/self_portrait_square_200.jpg
new file mode 100644
index 0000000..f18ae5b
--- /dev/null
+++ b/tests/TransitionTests/res/drawable-nodpi/self_portrait_square_200.jpg
Binary files differ
diff --git a/tests/TransitionTests/res/layout/activity_login.xml b/tests/TransitionTests/res/layout/activity_login.xml
new file mode 100644
index 0000000..2aaafc0
--- /dev/null
+++ b/tests/TransitionTests/res/layout/activity_login.xml
@@ -0,0 +1,86 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:id="@+id/password"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignRight="@+id/username"
+ android:layout_below="@+id/username"
+ android:layout_marginTop="30dp"
+ android:textColor="#7f7f7f"
+ android:text="@string/password"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <EditText
+ android:id="@+id/usernameEdit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@+id/username"
+ android:layout_marginLeft="14dp"
+ android:layout_toRightOf="@+id/username"
+ android:ems="10" />
+
+ <TextView
+ android:id="@+id/username"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginLeft="20dp"
+ android:layout_marginTop="51dp"
+ android:text="@string/username"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <EditText
+ android:id="@+id/passwordEdit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBaseline="@+id/password"
+ android:layout_alignBottom="@+id/password"
+ android:layout_alignLeft="@+id/usernameEdit"
+ android:layout_alignParentRight="true"
+ android:editable="false"
+ android:ems="10" >
+
+ <requestFocus />
+ </EditText>
+
+ <Button
+ android:id="@+id/cancel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_marginBottom="14dp"
+ android:layout_toLeftOf="@+id/passwordEdit"
+ android:onClick="sendMessage"
+ android:text="@string/cancel" />
+
+ <Button
+ android:id="@+id/submit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBaseline="@+id/cancel"
+ android:layout_alignBottom="@+id/cancel"
+ android:layout_alignLeft="@+id/passwordEdit"
+ android:layout_marginLeft="41dp"
+ android:onClick="sendMessage"
+ android:text="@string/submit" />
+
+ <TextView
+ android:id="@+id/newuser"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/passwordEdit"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="16dp"
+ android:textColor="#0000ff"
+ android:text="@string/new_user"
+ android:clickable="true"
+ android:onClick="sendMessage"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textStyle="italic" />
+
+</RelativeLayout> \ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/button_template.xml b/tests/TransitionTests/res/layout/button_template.xml
new file mode 100644
index 0000000..9b867f2
--- /dev/null
+++ b/tests/TransitionTests/res/layout/button_template.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+ android:onClick="sendMessage"
+ android:id="@+id/button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+</Button> \ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/changing_text_1.xml b/tests/TransitionTests/res/layout/changing_text_1.xml
new file mode 100644
index 0000000..88086a3
--- /dev/null
+++ b/tests/TransitionTests/res/layout/changing_text_1.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/submit"
+ android:onClick="sendMessage"
+ android:id="@+id/sceneSwitchButton"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/shortText1"
+ android:background="#8f8f8f"
+ android:id="@+id/textview1"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/buttona"
+ android:text="@string/button"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/longText1"
+ android:background="#8f8f8f"
+ android:id="@+id/textview2"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/buttonb"
+ android:text="@string/button"/>
+
+</LinearLayout> \ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/changing_text_2.xml b/tests/TransitionTests/res/layout/changing_text_2.xml
new file mode 100644
index 0000000..91fdfef
--- /dev/null
+++ b/tests/TransitionTests/res/layout/changing_text_2.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/submit"
+ android:onClick="sendMessage"
+ android:id="@+id/sceneSwitchButton"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/longText2"
+ android:background="#8f8f8f"
+ android:id="@+id/textview1"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/buttona"
+ android:text="@string/button"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/shortText2"
+ android:background="#8f8f8f"
+ android:id="@+id/textview2"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/buttonb"
+ android:text="@string/button"/>
+
+</LinearLayout> \ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/clipping_text_1.xml b/tests/TransitionTests/res/layout/clipping_text_1.xml
new file mode 100644
index 0000000..5fe45ab
--- /dev/null
+++ b/tests/TransitionTests/res/layout/clipping_text_1.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/submit"
+ android:onClick="sendMessage"
+ android:id="@+id/sceneSwitchButton"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/shortText1"
+ android:background="#8f8f8f"
+ android:id="@+id/textview1"/>
+
+</LinearLayout> \ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/clipping_text_2.xml b/tests/TransitionTests/res/layout/clipping_text_2.xml
new file mode 100644
index 0000000..3a0e3f4
--- /dev/null
+++ b/tests/TransitionTests/res/layout/clipping_text_2.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:layout_width="500dip"
+ android:layout_height="wrap_content"
+ android:text="@string/submit"
+ android:onClick="sendMessage"
+ android:id="@+id/sceneSwitchButton"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/longText2"
+ android:background="#8f8f8f"
+ android:id="@+id/textview1"/>
+</LinearLayout> \ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/contact_collapsed.xml b/tests/TransitionTests/res/layout/contact_collapsed.xml
new file mode 100644
index 0000000..bfa97df
--- /dev/null
+++ b/tests/TransitionTests/res/layout/contact_collapsed.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:background="#fff"
+ android:layout_width="fill_parent"
+ android:id="@+id/topContainer"
+ android:layout_height="wrap_content">
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_arrow"
+ android:src="@drawable/arrow_thumbnail"/>
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_picture"/>
+ <LinearLayout android:orientation="vertical"
+ android:layout_width="0dip"
+ android:id="@+id/itemContainer"
+ android:background="#ccc"
+ android:layout_weight="1"
+ android:layout_height="wrap_content">
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_name"/>
+ <LinearLayout android:orientation="vertical"
+ android:visibility="gone"
+ android:id="@+id/expanded_info"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_street"/>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_city"/>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_phone"/>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/contact_email"/>
+ </LinearLayout>
+ </LinearLayout>
+</LinearLayout> \ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/contact_expanded.xml b/tests/TransitionTests/res/layout/contact_expanded.xml
new file mode 100644
index 0000000..4007d4a
--- /dev/null
+++ b/tests/TransitionTests/res/layout/contact_expanded.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:background="#fff"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_arrow"
+ android:src="@drawable/arrow_thumbnail"/>
+ <LinearLayout android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_name"/>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_street"/>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_city"/>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_phone"/>
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/contact_email"/>
+ </LinearLayout>
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/contact_picture"/>
+</LinearLayout> \ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/contacts_list.xml b/tests/TransitionTests/res/layout/contacts_list.xml
new file mode 100644
index 0000000..d38704c
--- /dev/null
+++ b/tests/TransitionTests/res/layout/contacts_list.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/contactsContainer"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+
+
+</LinearLayout> \ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/crossfade.xml b/tests/TransitionTests/res/layout/crossfade.xml
new file mode 100644
index 0000000..d766ce1
--- /dev/null
+++ b/tests/TransitionTests/res/layout/crossfade.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:id="@+id/container">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:onClick="sendMessage"
+ android:id="@+id/button"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/shortText1"
+ android:id="@+id/textview"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/longText1"
+ android:id="@+id/textview1"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/someText"
+ android:id="@+id/textview2"/>
+
+</LinearLayout> \ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/crossfade_1.xml b/tests/TransitionTests/res/layout/crossfade_1.xml
new file mode 100644
index 0000000..4cc5bc9
--- /dev/null
+++ b/tests/TransitionTests/res/layout/crossfade_1.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:id="@+id/container">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:onClick="sendMessage"
+ android:id="@+id/button"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/shortText2"
+ android:id="@+id/textview"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/longText2"
+ android:id="@+id/textview1"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/someText"
+ android:id="@+id/textview2"/>
+
+</LinearLayout> \ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/fading_test.xml b/tests/TransitionTests/res/layout/fading_test.xml
new file mode 100644
index 0000000..3728b5e
--- /dev/null
+++ b/tests/TransitionTests/res/layout/fading_test.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/submit"
+ android:onClick="sendMessage"
+ android:id="@+id/sceneSwitchButton"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/removingButton"
+ android:id="@+id/removingButton"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/invisibleButton"
+ android:id="@+id/invisibleButton"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/goneButton"
+ android:id="@+id/goneButton"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/button"
+ android:text="@string/button"/>
+
+</LinearLayout> \ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/fading_test_scene_2.xml b/tests/TransitionTests/res/layout/fading_test_scene_2.xml
new file mode 100644
index 0000000..baf5b4d
--- /dev/null
+++ b/tests/TransitionTests/res/layout/fading_test_scene_2.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/submit"
+ android:onClick="sendMessage"
+ android:id="@+id/sceneSwitchButton"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/invisibleButton"
+ android:visibility="invisible"
+ android:id="@+id/invisibleButton"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/goneButton"
+ android:visibility="gone"
+ android:id="@+id/goneButton"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/button"
+ android:text="@string/button"/>
+
+</LinearLayout> \ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/fading_test_simple.xml b/tests/TransitionTests/res/layout/fading_test_simple.xml
new file mode 100644
index 0000000..d201eca
--- /dev/null
+++ b/tests/TransitionTests/res/layout/fading_test_simple.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/submit"
+ android:onClick="sendMessage"
+ android:id="@+id/sceneSwitchButton"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/removingButton"
+ android:id="@+id/removingButton"/>
+
+</LinearLayout> \ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/fading_test_simple2.xml b/tests/TransitionTests/res/layout/fading_test_simple2.xml
new file mode 100644
index 0000000..80f3f94
--- /dev/null
+++ b/tests/TransitionTests/res/layout/fading_test_simple2.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="50dip"
+ android:text="@string/submit"
+ android:onClick="sendMessage"
+ android:id="@+id/sceneSwitchButton"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/removingButton"
+ android:visibility="invisible"
+ android:id="@+id/removingButton"/>
+
+</LinearLayout> \ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/incorrect_password.xml b/tests/TransitionTests/res/layout/incorrect_password.xml
new file mode 100644
index 0000000..af59618
--- /dev/null
+++ b/tests/TransitionTests/res/layout/incorrect_password.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <Button
+ android:id="@+id/okay"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="164dp"
+ android:onClick="sendMessage"
+ android:text="@string/okay" />
+
+ <TextView
+ android:id="@+id/incorrectpassword"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_above="@+id/okay"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="93dp"
+ android:text="@string/incorrect_password"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <TextView
+ android:id="@+id/tryagain"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@+id/incorrectpassword"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="35dp"
+ android:text="@string/try_again"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+</RelativeLayout> \ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/instance_targets.xml b/tests/TransitionTests/res/layout/instance_targets.xml
new file mode 100644
index 0000000..5677d52
--- /dev/null
+++ b/tests/TransitionTests/res/layout/instance_targets.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/container">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:onClick="sendMessage"
+ android:id="@+id/button0"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/button0"
+ android:onClick="sendMessage"
+ android:id="@+id/button1"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/button1"
+ android:onClick="sendMessage"
+ android:id="@+id/button2"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/button2"
+ android:onClick="sendMessage"
+ android:id="@+id/button3"/>
+
+
+</RelativeLayout> \ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/list_view_add_remove.xml b/tests/TransitionTests/res/layout/list_view_add_remove.xml
new file mode 100644
index 0000000..230511f
--- /dev/null
+++ b/tests/TransitionTests/res/layout/list_view_add_remove.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ListView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/listview"/>
+
+</LinearLayout> \ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/login_password.xml b/tests/TransitionTests/res/layout/login_password.xml
new file mode 100644
index 0000000..1e75694
--- /dev/null
+++ b/tests/TransitionTests/res/layout/login_password.xml
@@ -0,0 +1,86 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".LoginActivity" >
+
+ <TextView
+ android:id="@+id/password"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignRight="@+id/username"
+ android:layout_below="@+id/username"
+ android:layout_marginTop="30dp"
+ android:text="@string/password"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <EditText
+ android:id="@+id/usernameEdit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@+id/username"
+ android:layout_marginLeft="14dp"
+ android:layout_toRightOf="@+id/username"
+ android:ems="10" />
+
+ <TextView
+ android:id="@+id/username"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginLeft="20dp"
+ android:layout_marginTop="51dp"
+ android:text="@string/username"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <EditText
+ android:id="@+id/passwordEdit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBaseline="@+id/password"
+ android:layout_alignBottom="@+id/password"
+ android:layout_alignLeft="@+id/usernameEdit"
+ android:layout_alignParentRight="true"
+ android:ems="10" >
+
+ <requestFocus />
+ </EditText>
+
+ <Button
+ android:id="@+id/cancel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_marginBottom="14dp"
+ android:layout_toLeftOf="@+id/passwordEdit"
+ android:onClick="sendMessage"
+ android:text="@string/cancel" />
+
+ <Button
+ android:id="@+id/submit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBaseline="@+id/cancel"
+ android:layout_alignBottom="@+id/cancel"
+ android:layout_alignLeft="@+id/passwordEdit"
+ android:layout_marginLeft="41dp"
+ android:onClick="sendMessage"
+ android:text="@string/submit" />
+
+ <TextView
+ android:id="@+id/newuser"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/passwordEdit"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="16dp"
+ android:textColor="#0000ff"
+ android:text="@string/new_user"
+ android:clickable="true"
+ android:onClick="sendMessage"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textStyle="italic" />
+
+</RelativeLayout> \ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/main.xml b/tests/TransitionTests/res/layout/main.xml
new file mode 100644
index 0000000..b42318e
--- /dev/null
+++ b/tests/TransitionTests/res/layout/main.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:id="@+id/container">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/button"/>
+</LinearLayout> \ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/new_user.xml b/tests/TransitionTests/res/layout/new_user.xml
new file mode 100644
index 0000000..f8dfab0
--- /dev/null
+++ b/tests/TransitionTests/res/layout/new_user.xml
@@ -0,0 +1,92 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/container"
+ tools:context=".LoginActivity" >
+
+ <TextView
+ android:id="@+id/password"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignRight="@+id/username"
+ android:layout_below="@+id/username"
+ android:layout_marginTop="30dp"
+ android:text="@string/password"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <EditText
+ android:id="@+id/usernameEdit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@+id/username"
+ android:layout_marginLeft="14dp"
+ android:layout_toRightOf="@+id/username"
+ android:ems="10" />
+
+ <TextView
+ android:id="@+id/username"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginLeft="20dp"
+ android:layout_marginTop="51dp"
+ android:text="@string/username"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <EditText
+ android:id="@+id/passwordEdit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBaseline="@+id/password"
+ android:layout_alignBottom="@+id/password"
+ android:layout_alignLeft="@+id/usernameEdit"
+ android:layout_alignParentRight="true"
+ android:ems="10" >
+
+ <requestFocus />
+ </EditText>
+
+ <Button
+ android:id="@+id/cancel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_marginBottom="14dp"
+ android:layout_toLeftOf="@+id/passwordEdit"
+ android:onClick="sendMessage"
+ android:text="@string/cancel" />
+
+ <Button
+ android:id="@+id/submit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBaseline="@+id/cancel"
+ android:layout_alignBottom="@+id/cancel"
+ android:layout_alignLeft="@+id/passwordEdit"
+ android:layout_marginLeft="41dp"
+ android:onClick="sendMessage"
+ android:text="@string/submit" />
+
+ <EditText
+ android:id="@+id/retypeEdit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBaseline="@+id/retype"
+ android:layout_alignBottom="@+id/retype"
+ android:layout_alignLeft="@+id/passwordEdit"
+ android:layout_alignParentRight="true"
+ android:ems="10" />
+
+ <TextView
+ android:id="@+id/retype"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@+id/passwordEdit"
+ android:layout_marginTop="26dp"
+ android:layout_toLeftOf="@+id/usernameEdit"
+ android:text="@string/retype"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+</RelativeLayout> \ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/overlay_test.xml b/tests/TransitionTests/res/layout/overlay_test.xml
new file mode 100644
index 0000000..edd0393
--- /dev/null
+++ b/tests/TransitionTests/res/layout/overlay_test.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/button"
+ android:text="@string/start"
+ android:onClick="onClick"/>
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/fadingButton"
+ android:id="@+id/fadingButton"/>
+
+</LinearLayout> \ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/reparenting.xml b/tests/TransitionTests/res/layout/reparenting.xml
new file mode 100644
index 0000000..b3bfbb9
--- /dev/null
+++ b/tests/TransitionTests/res/layout/reparenting.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:id="@+id/container">
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/container1"/>
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/container2"/>
+</LinearLayout> \ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/resources_test_layout.xml b/tests/TransitionTests/res/layout/resources_test_layout.xml
new file mode 100644
index 0000000..48affa0
--- /dev/null
+++ b/tests/TransitionTests/res/layout/resources_test_layout.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ImageView
+ src="@drawable/icon"/> \ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/results_screen.xml b/tests/TransitionTests/res/layout/results_screen.xml
new file mode 100644
index 0000000..8550a5e
--- /dev/null
+++ b/tests/TransitionTests/res/layout/results_screen.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:background="#7f7f7f"
+ android:id="@+id/container">
+ <LinearLayout android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top|right"
+ android:id="@+id/searchContainer">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/searchText"
+ android:id="@+id/searchText"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/searchButton"
+ android:onClick="sendMessage"
+ android:id="@+id/searchButton"/>
+ </LinearLayout>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:text="@string/resultsTitle"
+ android:id="@+id/resultsText"/>
+ <LinearLayout android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:id="@+id/resultsList">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/placeholder"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/placeholder"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/placeholder"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/placeholder"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/placeholder"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/placeholder"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/placeholder"/>
+
+ </LinearLayout>
+</LinearLayout> \ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/search_screen.xml b/tests/TransitionTests/res/layout/search_screen.xml
new file mode 100644
index 0000000..947702b
--- /dev/null
+++ b/tests/TransitionTests/res/layout/search_screen.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:background="#000000"
+ android:id="@+id/container">
+ <LinearLayout android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:id="@+id/searchContainer">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/searchText"
+ android:id="@+id/searchText"/>
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/searchButton"
+ android:onClick="sendMessage"
+ android:id="@+id/searchButton"/>
+ </LinearLayout>
+</LinearLayout> \ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/success.xml b/tests/TransitionTests/res/layout/success.xml
new file mode 100644
index 0000000..9c265ef
--- /dev/null
+++ b/tests/TransitionTests/res/layout/success.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <TextView
+ android:id="@+id/loginsuccessful"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="93dp"
+ android:text="@string/login_successful"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <TextView
+ android:id="@+id/firstactivityscreen"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@+id/loginsuccessful"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="35dp"
+ android:text="@string/first_activity_screen"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <Button
+ android:id="@+id/reset"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="164dp"
+ android:onClick="sendMessage"
+ android:text="@string/reset" />
+
+</RelativeLayout> \ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/surface_texture_views.xml b/tests/TransitionTests/res/layout/surface_texture_views.xml
new file mode 100644
index 0000000..9260bc0
--- /dev/null
+++ b/tests/TransitionTests/res/layout/surface_texture_views.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <Button
+ android:text="@string/toggle"
+ android:id="@+id/toggleButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+</LinearLayout> \ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/unique_id_test.xml b/tests/TransitionTests/res/layout/unique_id_test.xml
new file mode 100644
index 0000000..9b7eb10
--- /dev/null
+++ b/tests/TransitionTests/res/layout/unique_id_test.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+</LinearLayout> \ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/username_taken.xml b/tests/TransitionTests/res/layout/username_taken.xml
new file mode 100644
index 0000000..9484e69
--- /dev/null
+++ b/tests/TransitionTests/res/layout/username_taken.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <Button
+ android:id="@+id/okay"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="164dp"
+ android:onClick="sendMessage"
+ android:text="@string/okay" />
+
+ <TextView
+ android:id="@+id/usernametaken"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_above="@+id/okay"
+ android:layout_centerHorizontal="true"
+ android:layout_marginBottom="93dp"
+ android:text="@string/username_taken"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+ <TextView
+ android:id="@+id/textView2"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignTop="@+id/usernametaken"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="35dp"
+ android:text="@string/try_again"
+ android:textAppearance="?android:attr/textAppearanceLarge" />
+
+</RelativeLayout> \ No newline at end of file
diff --git a/tests/TransitionTests/res/scene/incorrect_password_scene.xml b/tests/TransitionTests/res/scene/incorrect_password_scene.xml
new file mode 100644
index 0000000..a31ad22
--- /dev/null
+++ b/tests/TransitionTests/res/scene/incorrect_password_scene.xml
@@ -0,0 +1,2 @@
+<scene xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout="@layout/incorrect_password"/> \ No newline at end of file
diff --git a/tests/TransitionTests/res/scene/login_scene.xml b/tests/TransitionTests/res/scene/login_scene.xml
new file mode 100644
index 0000000..b258303
--- /dev/null
+++ b/tests/TransitionTests/res/scene/login_scene.xml
@@ -0,0 +1,2 @@
+<scene xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout="@layout/activity_login"/> \ No newline at end of file
diff --git a/tests/TransitionTests/res/scene/my_scene.xml b/tests/TransitionTests/res/scene/my_scene.xml
new file mode 100644
index 0000000..b8278c9
--- /dev/null
+++ b/tests/TransitionTests/res/scene/my_scene.xml
@@ -0,0 +1,3 @@
+<scene >
+
+</scene> \ No newline at end of file
diff --git a/tests/TransitionTests/res/scene/new_user_scene.xml b/tests/TransitionTests/res/scene/new_user_scene.xml
new file mode 100644
index 0000000..d6e5f0f
--- /dev/null
+++ b/tests/TransitionTests/res/scene/new_user_scene.xml
@@ -0,0 +1,2 @@
+<scene xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout="@layout/new_user"/> \ No newline at end of file
diff --git a/tests/TransitionTests/res/scene/password_scene.xml b/tests/TransitionTests/res/scene/password_scene.xml
new file mode 100644
index 0000000..99a0038
--- /dev/null
+++ b/tests/TransitionTests/res/scene/password_scene.xml
@@ -0,0 +1,2 @@
+<scene xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout="@layout/login_password"/> \ No newline at end of file
diff --git a/tests/TransitionTests/res/scene/results_scene.xml b/tests/TransitionTests/res/scene/results_scene.xml
new file mode 100644
index 0000000..9c9fc69
--- /dev/null
+++ b/tests/TransitionTests/res/scene/results_scene.xml
@@ -0,0 +1,3 @@
+<scene xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout="@layout/results_screen">
+</scene> \ No newline at end of file
diff --git a/tests/TransitionTests/res/scene/search_scene.xml b/tests/TransitionTests/res/scene/search_scene.xml
new file mode 100644
index 0000000..742cd57
--- /dev/null
+++ b/tests/TransitionTests/res/scene/search_scene.xml
@@ -0,0 +1,3 @@
+<scene xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout="@layout/search_screen">
+</scene> \ No newline at end of file
diff --git a/tests/TransitionTests/res/scene/success_scene.xml b/tests/TransitionTests/res/scene/success_scene.xml
new file mode 100644
index 0000000..3d76d89
--- /dev/null
+++ b/tests/TransitionTests/res/scene/success_scene.xml
@@ -0,0 +1,2 @@
+<scene xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout="@layout/success"/> \ No newline at end of file
diff --git a/tests/TransitionTests/res/scene/username_taken_scene.xml b/tests/TransitionTests/res/scene/username_taken_scene.xml
new file mode 100644
index 0000000..b2f050e
--- /dev/null
+++ b/tests/TransitionTests/res/scene/username_taken_scene.xml
@@ -0,0 +1,2 @@
+<scene xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout="@layout/username_taken"/> \ No newline at end of file
diff --git a/tests/TransitionTests/res/transition/colorizer_transition.xml b/tests/TransitionTests/res/transition/colorizer_transition.xml
new file mode 100644
index 0000000..731d7ee
--- /dev/null
+++ b/tests/TransitionTests/res/transition/colorizer_transition.xml
@@ -0,0 +1,6 @@
+<recolor xmlns:android="http://schemas.android.com/apk/res/android">>
+ <targets>
+ <target android:targetID="@id/password"/>
+ <target android:targetID="@id/passwordEdit"/>
+ </targets>
+</recolor> \ No newline at end of file
diff --git a/tests/TransitionTests/res/transition/fader.xml b/tests/TransitionTests/res/transition/fader.xml
new file mode 100644
index 0000000..c71fd94
--- /dev/null
+++ b/tests/TransitionTests/res/transition/fader.xml
@@ -0,0 +1 @@
+<fade/> \ No newline at end of file
diff --git a/tests/TransitionTests/res/transition/login_slider_transition.xml b/tests/TransitionTests/res/transition/login_slider_transition.xml
new file mode 100644
index 0000000..dbdd6e9
--- /dev/null
+++ b/tests/TransitionTests/res/transition/login_slider_transition.xml
@@ -0,0 +1,22 @@
+<transitionGroup xmlns:android="http://schemas.android.com/apk/res/android">
+ <slide>
+ <targets>
+ <target android:targetID="@id/retype"/>
+ <target android:targetID="@id/retypeEdit"/>
+ </targets>
+ </slide>
+ <recolor>
+ <targets>
+ <target android:targetID="@id/password"/>
+ <target android:targetID="@id/passwordEdit"/>
+ </targets>
+ </recolor>
+ <fade/>
+</transitionGroup>
+
+<!--
+ TransitionGroup slider = new TransitionGroup();
+ slider.addTransition(new Slide(R.id.retype, R.id.retypeEdit));
+ slider.addTransition(new Recolor(R.id.password, R.id.passwordEdit));
+ slider.addTransition(new Fade());
+--> \ No newline at end of file
diff --git a/tests/TransitionTests/res/transition/login_transition_mgr.xml b/tests/TransitionTests/res/transition/login_transition_mgr.xml
new file mode 100644
index 0000000..8a8b9e9
--- /dev/null
+++ b/tests/TransitionTests/res/transition/login_transition_mgr.xml
@@ -0,0 +1,39 @@
+<transitionManager xmlns:android="http://schemas.android.com/apk/res/android">
+ <transition fromScene="@scene/login_scene" toScene="@scene/new_user_scene"
+ transition="@transition/login_slider_transition"/>
+ <transition fromScene="@scene/password_scene" toScene="@scene/new_user_scene"
+ transition="@transition/login_slider_transition"/>
+ <transition fromScene="@scene/new_user_scene" toScene="@scene/login_scene"
+ transition="@transition/login_slider_transition"/>
+ <transition fromScene="@scene/new_user_scene" toScene="@scene/password_scene"
+ transition="@transition/login_slider_transition"/>
+ <transition fromScene="@scene/login_scene" toScene="@scene/password_scene"
+ transition="@transition/colorizer_transition"/>
+ <transition fromScene="@scene/password_scene" toScene="@scene/login_scene"
+ transition="@transition/colorizer_transition"/>
+</transitionManager>
+
+ <!--
+ mLoginScene = new Scene(this, mSceneRoot, R.layout.activity_login);
+ mPasswordScene = new Scene(this, mSceneRoot, R.layout.login_password);
+ mIncorrectPasswordScene = new Scene(this, mSceneRoot, R.layout.incorrect_password);
+ mUsernameTakenScene = new Scene(this, mSceneRoot, R.layout.username_taken);
+ mSuccessScene = new Scene(this, mSceneRoot, R.layout.success);
+ mNewUserScene = new Scene(this, mSceneRoot, R.layout.new_user);
+
+ mTransitionManager = new TransitionManager();
+ // Custom transitions in/out of NewUser screen - slide in the 2nd password UI
+ TransitionGroup slider = new TransitionGroup();
+ slider.addTransition(new Slide(R.id.retype, R.id.retypeEdit));
+ slider.addTransition(new Recolor(R.id.password, R.id.passwordEdit));
+ slider.addTransition(new Fade());
+ mTransitionManager.setTransition(mLoginScene, mNewUserScene, slider);
+ mTransitionManager.setTransition(mPasswordScene, mNewUserScene, slider);
+ mTransitionManager.setTransition(mNewUserScene, mLoginScene, slider);
+ mTransitionManager.setTransition(mNewUserScene, mPasswordScene, slider);
+
+ // Custom transitions with recoloring password field
+ Transition colorizer = new Recolor(R.id.password, R.id.passwordEdit);
+ mTransitionManager.setTransition(mLoginScene, mPasswordScene, colorizer);
+ mTransitionManager.setTransition(mPasswordScene, mLoginScene, colorizer);
+--> \ No newline at end of file
diff --git a/tests/TransitionTests/res/transition/mover.xml b/tests/TransitionTests/res/transition/mover.xml
new file mode 100644
index 0000000..3c47606
--- /dev/null
+++ b/tests/TransitionTests/res/transition/mover.xml
@@ -0,0 +1 @@
+<move/> \ No newline at end of file
diff --git a/tests/TransitionTests/res/transition/mover_fader.xml b/tests/TransitionTests/res/transition/mover_fader.xml
new file mode 100644
index 0000000..8b805e9
--- /dev/null
+++ b/tests/TransitionTests/res/transition/mover_fader.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<transitionGroup xmlns:android="http://schemas.android.com/apk/res/android">
+ <fade/>
+ <move/>
+</transitionGroup> \ No newline at end of file
diff --git a/tests/TransitionTests/res/transition/my_transition.xml b/tests/TransitionTests/res/transition/my_transition.xml
new file mode 100644
index 0000000..14c91b9
--- /dev/null
+++ b/tests/TransitionTests/res/transition/my_transition.xml
@@ -0,0 +1,20 @@
+<transitionGroup xmlns:android="http://schemas.android.com/apk/res/android">
+ <fade></fade>
+ <transitionGroup>
+ <move android:duration="500">
+ <targets>
+ <target android:targetID="@id/container"/>
+ <target android:targetID="@id/resultsList"/>
+ </targets>
+ </move>
+ <transitionGroup>
+ <targets>
+ <target android:targetID="@id/container"/>
+ <target android:targetID="@id/resultsList"/>
+ </targets>
+ <fade android:startOffset="25"/>
+ </transitionGroup>
+ <recolor/>
+ </transitionGroup>
+ <move/>
+</transitionGroup> \ No newline at end of file
diff --git a/tests/TransitionTests/res/transition/my_transition_mgr.xml b/tests/TransitionTests/res/transition/my_transition_mgr.xml
new file mode 100644
index 0000000..ca9705a
--- /dev/null
+++ b/tests/TransitionTests/res/transition/my_transition_mgr.xml
@@ -0,0 +1,6 @@
+<transitionManager xmlns:android="http://schemas.android.com/apk/res/android">
+ <transition fromScene="@scene/search_scene" toScene="@scene/results_scene"
+ transition="@transition/mover_fader"/>
+ <transition fromScene="@scene/results_scene" toScene="@scene/search_scene"
+ transition="@transition/mover_fader"/>
+</transitionManager> \ No newline at end of file
diff --git a/tests/TransitionTests/res/values-v11/styles.xml b/tests/TransitionTests/res/values-v11/styles.xml
new file mode 100644
index 0000000..541752f
--- /dev/null
+++ b/tests/TransitionTests/res/values-v11/styles.xml
@@ -0,0 +1,11 @@
+<resources>
+
+ <!--
+ Base application theme for API 11+. This theme completely replaces
+ AppBaseTheme from res/values/styles.xml on API 11+ devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light">
+ <!-- API 11 theme customizations can go here. -->
+ </style>
+
+</resources> \ No newline at end of file
diff --git a/tests/TransitionTests/res/values-v14/styles.xml b/tests/TransitionTests/res/values-v14/styles.xml
new file mode 100644
index 0000000..f20e015
--- /dev/null
+++ b/tests/TransitionTests/res/values-v14/styles.xml
@@ -0,0 +1,12 @@
+<resources>
+
+ <!--
+ Base application theme for API 14+. This theme completely replaces
+ AppBaseTheme from BOTH res/values/styles.xml and
+ res/values-v11/styles.xml on API 14+ devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+ <!-- API 14 theme customizations can go here. -->
+ </style>
+
+</resources> \ No newline at end of file
diff --git a/tests/TransitionTests/res/values/strings.xml b/tests/TransitionTests/res/values/strings.xml
new file mode 100644
index 0000000..e251d5c
--- /dev/null
+++ b/tests/TransitionTests/res/values/strings.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">StatesTest</string>
+ <string name="states_test1">StatesTestv21</string>
+ <string name="states_test_auto_targets">StatesTestAutoTargets</string>
+ <string name="states_test_auto_transition">StatesTestAutoTransition</string>
+ <string name="states_test_auto_transition2">StatesTestAutoTransition2</string>
+ <string name="contacts_expansion">ContactsExpansion</string>
+ <string name="states_test3">StatesTest3</string>
+ <string name="states_test4">StatesTest4</string>
+ <string name="states_test5">StatesTest5</string>
+ <string name="states_test6">StatesTest6</string>
+ <string name="button">Button</string>
+ <string name="searchButton">Search</string>
+ <string name="searchText">This is some text</string>
+ <string name="resultsTitle">Search Results</string>
+ <string name="placeholder">Blah Blah Blah</string>
+ <string name="username">Username:</string>
+ <string name="password">Password:</string>
+ <string name="retype">Retype:</string>
+ <string name="new_user">New User?</string>
+ <string name="incorrect_password">Incorrect password:</string>
+ <string name="try_again">Please try again.</string>
+ <string name="login_successful">Success!</string>
+ <string name="first_activity_screen">First activity screen</string>
+ <string name="username_taken">Username taken:</string>
+ <string name="cancel">Cancel</string>
+ <string name="submit">Submit</string>
+ <string name="okay">Okay</string>
+ <string name="reset">Reset</string>
+ <string name="fadingButton">Fading Button</string>
+ <string name="removingButton">Removing Button</string>
+ <string name="invisibleButton">invisible Button</string>
+ <string name="goneButton">Gone Button</string>
+ <string name="start">Start</string>
+ <string name="toggle">Toggle State</string>
+ <string name="someText">This is some text</string>
+ <string name="shortText1">This is some short text</string>
+ <string name="shortText2">Not much text here</string>
+ <string name="longText1">This is the beginning of the Spring of my discontent. In the event of a real emergency, you would be notified by email. Fear not, for death comes swiftly.</string>
+ <string name="longText2">When do we get to eat? I like all things, especially following strong leaders, and mangy cats. Break glass in emergency. The purpose of a framework is to provide the facilities and functionality of a powerful toolkit with the simplicity of a refrigerator.</string>
+</resources>
diff --git a/tests/TransitionTests/res/values/styles.xml b/tests/TransitionTests/res/values/styles.xml
new file mode 100644
index 0000000..4a10ca4
--- /dev/null
+++ b/tests/TransitionTests/res/values/styles.xml
@@ -0,0 +1,20 @@
+<resources>
+
+ <!--
+ Base application theme, dependent on API level. This theme is replaced
+ by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+ -->
+ <style name="AppBaseTheme" parent="android:Theme.Light">
+ <!--
+ Theme customizations available in newer API levels can go in
+ res/values-vXX/styles.xml, while customizations related to
+ backward-compatibility can go here.
+ -->
+ </style>
+
+ <!-- Application theme. -->
+ <style name="AppTheme" parent="AppBaseTheme">
+ <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+ </style>
+
+</resources> \ No newline at end of file
diff --git a/tests/TransitionTests/src/com/android/transitiontests/ChangingText.java b/tests/TransitionTests/src/com/android/transitiontests/ChangingText.java
new file mode 100644
index 0000000..ed9b46a
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/ChangingText.java
@@ -0,0 +1,63 @@
+/*
+ * 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.Scene;
+import android.widget.Button;
+import android.view.transition.Fade;
+import android.view.transition.Move;
+import android.view.transition.TextChange;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+public class ChangingText extends Activity {
+
+ Button mRemovingButton, mInvisibleButton, mGoneButton;
+ Scene mScene1, mScene2;
+ ViewGroup mSceneRoot;
+ Fade fader;
+ TransitionGroup mChanger;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.changing_text_1);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mScene1 = new Scene(mSceneRoot, R.layout.changing_text_1, this);
+ mScene2 = new Scene(mSceneRoot, R.layout.changing_text_2, this);
+
+ mChanger = new TransitionGroup(TransitionGroup.TOGETHER);
+ mChanger.addTransitions(new Move(), new TextChange());
+
+ mSceneRoot.setCurrentScene(mScene1);
+ }
+
+ public void sendMessage(View view) {
+ if (mSceneRoot.getCurrentScene() == mScene1) {
+ TransitionManager.go(mScene2, mChanger);
+ } else {
+ TransitionManager.go(mScene1, mChanger);
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/ClippingText.java b/tests/TransitionTests/src/com/android/transitiontests/ClippingText.java
new file mode 100644
index 0000000..0ecf1f4
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/ClippingText.java
@@ -0,0 +1,66 @@
+/*
+ * 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.Scene;
+import android.widget.Button;
+import android.view.transition.Fade;
+import android.view.transition.Move;
+import android.view.transition.TextChange;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+public class ClippingText extends Activity {
+
+ Button mRemovingButton, mInvisibleButton, mGoneButton;
+ Scene mScene1, mScene2;
+ ViewGroup mSceneRoot;
+ // static Fade sFade = new Fade(R.id.removingButton, R.id.invisibleButton, R.id.goneButton);
+ Fade fader;
+ TransitionGroup mChanger;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.clipping_text_1);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mScene1 = new Scene(mSceneRoot, R.layout.clipping_text_1, this);
+ mScene2 = new Scene(mSceneRoot, R.layout.clipping_text_2, this);
+
+ mChanger = new TransitionGroup(TransitionGroup.TOGETHER);
+ Move move = new Move();
+ move.setResizeClip(true);
+ mChanger.addTransitions(move, new TextChange());
+
+ mSceneRoot.setCurrentScene(mScene1);
+ }
+
+ public void sendMessage(View view) {
+ if (mSceneRoot.getCurrentScene() == mScene1) {
+ TransitionManager.go(mScene2, mChanger);
+ } else {
+ TransitionManager.go(mScene1, mChanger);
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/ContactsExpansion.java b/tests/TransitionTests/src/com/android/transitiontests/ContactsExpansion.java
new file mode 100644
index 0000000..55a96a5
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/ContactsExpansion.java
@@ -0,0 +1,123 @@
+/*
+ * 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.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Fade;
+import android.view.transition.Scene;
+import android.view.transition.Transition;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.view.transition.Crossfade;
+import android.view.transition.Move;
+import android.view.transition.Rotate;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+public class ContactsExpansion extends Activity {
+
+ String contactsData[] = {
+ "Alan Green", "56 Bob Street", "Boston, MA 02134", "617-555-5555", "blatt@blatt.com",
+ "Bob Foonman", "92 The Avenue", "Chico, CA 78456", "510-555-5556", "bob@jerk.com",
+ "Tracey Sue", "95 Houses Street", "San Jose, CA 96504", "415-555-5557", "ts@thing.com",
+ };
+
+ View currentItem = null;
+
+ TransitionGroup mMyAutoTransition = new TransitionGroup(TransitionGroup.SEQUENTIALLY);
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.contacts_list);
+ ViewGroup contactsContainer = (ViewGroup) findViewById(R.id.contactsContainer);
+
+ int contactsIndex = 0;
+ addContact(contactsContainer, contactsIndex, R.drawable.self_portrait_square_100);
+ contactsIndex += 5;
+ addContact(contactsContainer, contactsIndex, R.drawable.self_portrait_square_100);
+ contactsIndex += 5;
+ addContact(contactsContainer, contactsIndex, R.drawable.self_portrait_square_100);
+
+ }
+
+ private void addContact(ViewGroup container, int dataIndex, int thumbnailID) {
+ LayoutInflater inflater = (LayoutInflater)
+ getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View contactItem = inflater.inflate(R.layout.contact_collapsed, container, false);
+ ImageView thumbnailView = (ImageView) contactItem.findViewById(R.id.contact_picture);
+ thumbnailView.setImageResource(thumbnailID);
+ ((TextView)contactItem.findViewById(R.id.contact_name)).setText(contactsData[dataIndex++]);
+ ((TextView)contactItem.findViewById(R.id.contact_street)).
+ setText(contactsData[dataIndex++]);
+ ((TextView)contactItem.findViewById(R.id.contact_city)).setText(contactsData[dataIndex++]);
+ ((TextView)contactItem.findViewById(R.id.contact_phone)).setText(contactsData[dataIndex++]);
+ ((TextView)contactItem.findViewById(R.id.contact_email)).setText(contactsData[dataIndex++]);
+ container.addView(contactItem);
+
+ final TransitionGroup myTransition = new TransitionGroup();
+ myTransition.addTransitions(new Fade(Fade.IN),
+ new Rotate().setTargetIds(R.id.contact_arrow),
+ new Move(), new Fade(Fade.OUT),
+ new Crossfade().setTargetIds(R.id.contact_picture));
+ final ToggleScene toggleScene = new ToggleScene(container, myTransition);
+ contactItem.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ currentItem = v;
+ toggleScene.changeToScene();
+ }
+ });
+ }
+
+ class ToggleScene {
+ boolean expanded = false;
+ Scene mScene;
+ Transition mTransition;
+
+ ToggleScene(ViewGroup rootView, Transition transition) {
+ mScene = new Scene(rootView);
+ mTransition = transition;
+ mScene.setEnterAction(new Runnable() {
+ @Override
+ public void run() {
+ if (currentItem != null) {
+ System.out.println("onsceneChanged: currentItem = " + currentItem);
+ View expandedContainer = currentItem.findViewById(R.id.expanded_info);
+ expandedContainer.setVisibility(expanded ? View.GONE : View.VISIBLE);
+ ImageView thumbnailView =
+ (ImageView) currentItem.findViewById(R.id.contact_picture);
+ thumbnailView.setImageResource(expanded ? R.drawable.self_portrait_square_100 :
+ R.drawable.self_portrait_square_200);
+ ImageView arrow = (ImageView) currentItem.findViewById(R.id.contact_arrow);
+ arrow.setRotation(expanded ? 0 : 90);
+ expanded = !expanded;
+ }
+ }
+ });
+ }
+
+ void changeToScene() {
+ TransitionManager.go(mScene, mTransition);
+ }
+ };
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/CrossFadeDemo.java b/tests/TransitionTests/src/com/android/transitiontests/CrossFadeDemo.java
new file mode 100644
index 0000000..c428a73
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/CrossFadeDemo.java
@@ -0,0 +1,67 @@
+/*
+ * 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.Scene;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+public class CrossFadeDemo extends Activity {
+
+ ViewGroup mSceneRoot;
+ static int mCurrentScene;
+ Scene mScene1, mScene2;
+ TransitionManager mTransitionManager;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.crossfade);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mScene1 = new Scene(mSceneRoot, R.layout.crossfade, this);
+ mScene2 = new Scene(mSceneRoot, R.layout.crossfade_1, this);
+
+ Crossfade crossfade = new Crossfade();
+ crossfade.setTargetIds(R.id.textview, R.id.textview1, R.id.textview2);
+ mTransitionManager = new TransitionManager();
+ TransitionGroup moveCrossFade = new TransitionGroup();
+ moveCrossFade.addTransitions(crossfade, new Move());
+ mTransitionManager.setTransition(mScene1, moveCrossFade);
+ mTransitionManager.setTransition(mScene2, moveCrossFade);
+ mCurrentScene = 1;
+ }
+
+ public void sendMessage(View view) {
+ if (mCurrentScene == 1) {
+ mTransitionManager.transitionTo(mScene2);
+ mCurrentScene = 2;
+ } else {
+ mTransitionManager.transitionTo(mScene1);
+ mCurrentScene = 1;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/Demo0.java b/tests/TransitionTests/src/com/android/transitiontests/Demo0.java
new file mode 100644
index 0000000..55bf956
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/Demo0.java
@@ -0,0 +1,60 @@
+/*
+ * 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.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import com.android.transitiontest.R;
+
+
+public class Demo0 extends Activity {
+
+ private static final int SEARCH_SCREEN = 0;
+ private static final int RESULTS_SCREEN = 1;
+ ViewGroup mSceneRoot;
+ static int mCurrentScene;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mCurrentScene = SEARCH_SCREEN;
+ }
+
+ public void sendMessage(View view) {
+ if (mCurrentScene == RESULTS_SCREEN) {
+ mSceneRoot.removeAllViews();
+ LayoutInflater inflater = (LayoutInflater)
+ getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(R.layout.search_screen, mSceneRoot);
+ mCurrentScene = SEARCH_SCREEN;
+ } else {
+ mSceneRoot.removeAllViews();
+ LayoutInflater inflater = (LayoutInflater)
+ getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(R.layout.results_screen, mSceneRoot);
+ mCurrentScene = RESULTS_SCREEN;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/Demo1.java b/tests/TransitionTests/src/com/android/transitiontests/Demo1.java
new file mode 100644
index 0000000..360a764
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/Demo1.java
@@ -0,0 +1,88 @@
+/*
+ * 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.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Fade;
+import android.view.transition.Move;
+import android.view.transition.Scene;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+public class Demo1 extends Activity {
+ ViewGroup mSceneRoot;
+ static Scene mCurrentScene;
+ boolean mFirstTime = true;
+ Scene mSearchScreen, mResultsScreen;
+ TransitionManager mTransitionManager = null;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+// mResultsScreen = new MyScene(mSceneRoot, R.layout.results_screen);
+// mSearchScreen = new MyScene(mSceneRoot, R.layout.search_screen);
+ mResultsScreen = new Scene(mSceneRoot);
+ mResultsScreen.setEnterAction(new Runnable() {
+ @Override
+ public void run() {
+ LayoutInflater inflater = (LayoutInflater)
+ getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(R.layout.results_screen, mSceneRoot);
+ }
+ });
+ mSearchScreen = new Scene(mSceneRoot);
+ mSearchScreen.setEnterAction(new Runnable() {
+ @Override
+ public void run() {
+ LayoutInflater inflater = (LayoutInflater)
+ getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ inflater.inflate(R.layout.search_screen, mSceneRoot);
+ }
+ });
+
+ }
+
+ public void sendMessage(View view) {
+ if (mFirstTime) {
+ mFirstTime = false;
+ TransitionGroup transition = new TransitionGroup();
+ transition.addTransitions(new Fade().setTargetIds(R.id.resultsText, R.id.resultsList),
+ new Move().setTargetIds(R.id.searchContainer));
+ mTransitionManager = new TransitionManager();
+ mTransitionManager.setTransition(mSearchScreen, transition);
+ mTransitionManager.setTransition(mResultsScreen, transition);
+ }
+ if (mCurrentScene == mResultsScreen) {
+ mTransitionManager.transitionTo(mSearchScreen);
+ mCurrentScene = mSearchScreen;
+ } else {
+ mTransitionManager.transitionTo(mResultsScreen);
+ mCurrentScene = mResultsScreen;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/Demo2.java b/tests/TransitionTests/src/com/android/transitiontests/Demo2.java
new file mode 100644
index 0000000..40f8a08
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/Demo2.java
@@ -0,0 +1,79 @@
+/*
+ * 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.Fade;
+import android.view.transition.Move;
+import android.view.transition.Recolor;
+import android.view.transition.Scene;
+import android.view.transition.TransitionInflater;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+public class Demo2 extends Activity {
+ ViewGroup mSceneRoot;
+ static Scene mCurrentScene;
+ boolean mFirstTime = true;
+ Scene mSearchScreen, mResultsScreen;
+ TransitionManager mTransitionManager = null;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ }
+
+ public void sendMessage(View view) {
+ if (mFirstTime) {
+ mFirstTime = false;
+ // Non-resource approach of creating scenes
+// mSearchScreen = new Scene(this, mSceneRoot, R.layout.search_screen);
+// mResultsScreen = new Scene(this, mSceneRoot, R.layout.results_screen);
+ try {
+ mSearchScreen = TransitionInflater.from(this).
+ inflateScene(R.scene.search_scene, mSceneRoot);
+ mResultsScreen = TransitionInflater.from(this).
+ inflateScene(R.scene.results_scene, mSceneRoot);
+ } catch (Exception e) {
+ System.out.println("Problem loading scene resource: " + e);
+ }
+
+ TransitionGroup transition = new TransitionGroup();
+ transition.addTransitions(new Fade().setTargetIds(R.id.resultsText, R.id.resultsList),
+ new Move().setTargetIds(R.id.searchContainer),
+ new Recolor().setTargetIds(R.id.container));
+ mTransitionManager = new TransitionManager();
+ mTransitionManager.setTransition(mSearchScreen, transition);
+ mTransitionManager.setTransition(mResultsScreen, transition);
+ }
+ if (mCurrentScene == mResultsScreen) {
+ mTransitionManager.transitionTo(mSearchScreen);
+ mCurrentScene = mSearchScreen;
+ } else {
+ mTransitionManager.transitionTo(mResultsScreen);
+ mCurrentScene = mResultsScreen;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/Demo3.java b/tests/TransitionTests/src/com/android/transitiontests/Demo3.java
new file mode 100644
index 0000000..f30f502
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/Demo3.java
@@ -0,0 +1,65 @@
+/*
+ * 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.Fade;
+import android.view.transition.Move;
+import android.view.transition.Recolor;
+import android.view.transition.Scene;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+public class Demo3 extends Activity {
+ ViewGroup mSceneRoot;
+ static Scene mCurrentScene;
+ Scene mSearchScreen, mResultsScreen;
+ TransitionManager mTransitionManager = null;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mSearchScreen = new Scene(mSceneRoot, R.layout.search_screen, this);
+ mResultsScreen = new Scene(mSceneRoot, R.layout.results_screen, this);
+
+ TransitionGroup transition = new TransitionGroup();
+ transition.addTransitions(new Fade(), new Move(), new Recolor());
+
+ mTransitionManager = new TransitionManager();
+ mTransitionManager.setTransition(mSearchScreen, transition);
+ mTransitionManager.setTransition(mResultsScreen, transition);
+ }
+
+ public void sendMessage(View view) {
+ if (mCurrentScene == mResultsScreen) {
+ mTransitionManager.transitionTo(mSearchScreen);
+ mCurrentScene = mSearchScreen;
+ } else {
+ mTransitionManager.transitionTo(mResultsScreen);
+ mCurrentScene = mResultsScreen;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/Demo4.java b/tests/TransitionTests/src/com/android/transitiontests/Demo4.java
new file mode 100644
index 0000000..41d6d08
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/Demo4.java
@@ -0,0 +1,74 @@
+/*
+ * 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.Fade;
+import android.view.transition.Move;
+import android.view.transition.Recolor;
+import android.view.transition.Scene;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+public class Demo4 extends Activity {
+ ViewGroup mSceneRoot;
+ static Scene mCurrentScene;
+ Scene mSearchScreen, mResultsScreen;
+ TransitionManager mTransitionManager = null;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mSearchScreen = new Scene(mSceneRoot, R.layout.search_screen, this);
+ mResultsScreen = new Scene(mSceneRoot, R.layout.results_screen, this);
+
+ TransitionGroup transitionToResults = new TransitionGroup();
+ Fade fade = new Fade();
+ fade.setTargetIds(R.id.resultsText, R.id.resultsList);
+ fade.setStartDelay(300);
+ fade.setDuration(1000);
+ transitionToResults.addTransitions(fade, new Move().setTargetIds(R.id.searchContainer),
+ new Recolor().setTargetIds(R.id.container));
+
+ TransitionGroup transitionToSearch = new TransitionGroup();
+ transitionToSearch.addTransitions(fade, new Move().setTargetIds(R.id.searchContainer),
+ new Recolor().setTargetIds(R.id.container));
+
+ mTransitionManager = new TransitionManager();
+ mTransitionManager.setTransition(mSearchScreen, transitionToSearch);
+ mTransitionManager.setTransition(mResultsScreen, transitionToResults);
+ }
+
+ public void sendMessage(View view) {
+ if (mCurrentScene == mResultsScreen) {
+ mTransitionManager.transitionTo(mSearchScreen);
+ mCurrentScene = mSearchScreen;
+ } else {
+ mTransitionManager.transitionTo(mResultsScreen);
+ mCurrentScene = mResultsScreen;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/Demo5.java b/tests/TransitionTests/src/com/android/transitiontests/Demo5.java
new file mode 100644
index 0000000..9d64f07
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/Demo5.java
@@ -0,0 +1,54 @@
+/*
+ * 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.Scene;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+public class Demo5 extends Activity {
+ ViewGroup mSceneRoot;
+ static Scene mCurrentScene;
+ Scene mSearchScreen, mResultsScreen;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mSearchScreen = new Scene(mSceneRoot, R.layout.search_screen, this);
+ mResultsScreen = new Scene(mSceneRoot, R.layout.results_screen, this);
+
+ }
+
+ public void sendMessage(View view) {
+ if (mCurrentScene == mResultsScreen) {
+ TransitionManager.go(mSearchScreen);
+ mCurrentScene = mSearchScreen;
+ } else {
+ TransitionManager.go(mResultsScreen);
+ mCurrentScene = mResultsScreen;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/FadingTest.java b/tests/TransitionTests/src/com/android/transitiontests/FadingTest.java
new file mode 100644
index 0000000..7d30dfd
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/FadingTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.Scene;
+import android.widget.Button;
+import android.view.transition.Fade;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+public class FadingTest extends Activity {
+
+ Button mRemovingButton, mInvisibleButton, mGoneButton;
+ Scene mScene1, mScene2;
+ ViewGroup mSceneRoot;
+ static Fade sFade = new Fade();
+
+ static {
+ sFade.setTargetIds(R.id.removingButton, R.id.invisibleButton, R.id.goneButton);
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.fading_test);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+
+ mRemovingButton = (Button) findViewById(R.id.removingButton);
+ mInvisibleButton = (Button) findViewById(R.id.invisibleButton);
+ mGoneButton = (Button) findViewById(R.id.goneButton);
+
+ mGoneButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mGoneButton.setAlpha(mGoneButton.getAlpha() < 1 ? 1 : .6f);
+ }
+ });
+
+ mScene1 = new Scene(mSceneRoot, R.layout.fading_test, this);
+ mScene2 = new Scene(mSceneRoot, R.layout.fading_test_scene_2, this);
+
+ mSceneRoot.setCurrentScene(mScene1);
+ }
+
+ public void sendMessage(View view) {
+ if (mSceneRoot.getCurrentScene() == mScene1) {
+ TransitionManager.go(mScene2);
+ } else {
+ TransitionManager.go(mScene1);
+ }
+ }
+
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/HitRectBug.java b/tests/TransitionTests/src/com/android/transitiontests/HitRectBug.java
new file mode 100644
index 0000000..33e00a8
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/HitRectBug.java
@@ -0,0 +1,92 @@
+/*
+ * 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.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import com.android.transitiontest.R;
+
+
+public class HitRectBug extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ setContentView(new TestDrawingView(this));
+ }
+
+ public static class TestDrawingView extends RelativeLayout
+ {
+ private Rect mRect = new Rect();
+ private Paint mPaint;
+ private ImageView mImageView;
+
+ public TestDrawingView(Context context)
+ {
+ super(context);
+ setWillNotDraw(false);
+
+ mPaint = new Paint();
+ mPaint.setColor(Color.RED);
+ mPaint.setStyle(Paint.Style.STROKE);
+
+ mImageView = new ImageView(context);
+ mImageView.setLeft(100);
+ mImageView.setRight(200);
+ mImageView.setImageResource(R.drawable.self_portrait_square);
+ mImageView.setScaleX(3);
+ mImageView.setScaleY(3);
+// mImageView.setRotation(145);
+
+ ObjectAnimator anim = ObjectAnimator.ofFloat(mImageView, View.ROTATION, 0, 360);
+ anim.setRepeatCount(ValueAnimator.INFINITE);
+ anim.setDuration(5000);
+ anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ invalidate();
+ }
+ });
+ anim.start();
+ RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(128, 128);
+ params.addRule(RelativeLayout.CENTER_IN_PARENT);
+ addView(mImageView, params);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas)
+ {
+ super.onDraw(canvas);
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
+ mImageView.getHitRect(mRect);
+ canvas.drawRect(mRect, mPaint);
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/InstanceTargets.java b/tests/TransitionTests/src/com/android/transitiontests/InstanceTargets.java
new file mode 100644
index 0000000..efccfa9
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/InstanceTargets.java
@@ -0,0 +1,69 @@
+/*
+ * 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.content.Context;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Move;
+import android.view.transition.Scene;
+import android.view.transition.TransitionManager;
+import android.widget.Button;
+import android.widget.RelativeLayout;
+import com.android.transitiontest.R;
+
+import static android.widget.RelativeLayout.ALIGN_PARENT_LEFT;
+import static android.widget.RelativeLayout.ALIGN_PARENT_RIGHT;
+import static android.widget.RelativeLayout.LayoutParams;
+
+public class InstanceTargets extends Activity {
+
+ ViewGroup mSceneRoot;
+ static int mCurrentScene;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.instance_targets);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container;
+ }
+
+ public void sendMessage(final View view) {
+ TransitionManager.go(mSceneRoot, new Runnable() {
+ @Override
+ public void run() {
+ for (int i = 0; i < mSceneRoot.getChildCount(); ++i) {
+ Button button = (Button) mSceneRoot.getChildAt(i);
+ LayoutParams params = (LayoutParams) button.getLayoutParams();
+ int rules[] = params.getRules();
+ if (rules[ALIGN_PARENT_RIGHT] != 0) {
+ params.removeRule(ALIGN_PARENT_RIGHT);
+ params.addRule(ALIGN_PARENT_LEFT);
+ } else {
+ params.removeRule(ALIGN_PARENT_LEFT);
+ params.addRule(ALIGN_PARENT_RIGHT);
+ }
+ button.setLayoutParams(params);
+ }
+ }
+ }, new Move().setTargets(view));
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/ListViewAddRemove.java b/tests/TransitionTests/src/com/android/transitiontests/ListViewAddRemove.java
new file mode 100644
index 0000000..f0864dc
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/ListViewAddRemove.java
@@ -0,0 +1,167 @@
+/*
+ * 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.content.Context;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.transition.Fade;
+import android.view.transition.Scene;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.view.transition.AutoTransition;
+import android.view.transition.Move;
+import android.view.transition.Transition;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+public class ListViewAddRemove extends Activity {
+
+ final ArrayList<String> numList = new ArrayList<String>();
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.list_view_add_remove);
+
+ final LinearLayout container = (LinearLayout) findViewById(R.id.container);
+
+ final ListView listview = (ListView) findViewById(R.id.listview);
+ for (int i = 0; i < 200; ++i) {
+ numList.add(Integer.toString(i));
+ }
+ final StableArrayAdapter adapter = new StableArrayAdapter(this,
+ android.R.layout.simple_list_item_1, numList);
+ listview.setAdapter(adapter);
+
+ final ViewTreeObserver observer = container.getViewTreeObserver();
+ observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
+ public void onGlobalLayout() {
+ System.out.println("-------------------------------------");
+ System.out.println("onLayoutListener: listview view tops: ");
+ for (int i = 0; i < listview.getChildCount(); ++i) {
+ TextView view = (TextView) listview.getChildAt(i);
+ System.out.println(" " + view.getText() + ": " + view.getTop());
+ }
+ }
+ });
+
+ final Scene mySceneChanger = new Scene(listview);
+
+ mySceneChanger.setEnterAction(new Runnable() {
+ @Override
+ public void run() {
+ numList.remove(mItemToDelete);
+ adapter.notifyDataSetChanged();
+ }
+ });
+ final Transition myTransition = new AutoTransition();
+ final TransitionGroup noFadeIn = new TransitionGroup(TransitionGroup.SEQUENTIALLY);
+ Fade fadeIn = new Fade(Fade.IN);
+ fadeIn.setDuration(50);
+ noFadeIn.addTransitions(new Fade(Fade.OUT), new Move(), fadeIn);
+
+ myTransition.addListener(new Transition.TransitionListenerAdapter() {
+ @Override
+ public void onTransitionStart(Transition transition) {
+ System.out.println("---------ListView Tops: Before--------");
+ for (int i = 0; i < listview.getChildCount(); ++i) {
+ TextView view = (TextView) listview.getChildAt(i);
+ int position = listview.getPositionForView(view);
+ }
+ }
+
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ System.out.println("---------ListView Tops: After--------");
+ for (int i = 0; i < listview.getChildCount(); ++i) {
+ TextView view = (TextView) listview.getChildAt(i);
+ int position = listview.getPositionForView(view);
+ if (view.hasTransientState()) {
+// view.setHasTransientState(false);
+ }
+ }
+ myTransition.removeListener(this);
+ }
+ });
+
+ listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, final View view, int position, long id) {
+ System.out.println("---------ListView Tops: OnClick--------");
+ String item = (String) parent.getItemAtPosition(position);
+ for (int i = 0; i < listview.getChildCount(); ++i) {
+ TextView v = (TextView) listview.getChildAt(i);
+ if (!item.equals(v.getText())) {
+// v.setHasTransientState(true);
+ }
+ }
+// listview.setHasTransientState(true);
+ mItemToDelete = item;
+// numList.remove(item);
+ TransitionManager.go(mySceneChanger, noFadeIn);
+// view.postDelayed(new Runnable() {
+// @Override
+// public void run() {
+// for (int i = 0; i < listview.getChildCount(); ++i) {
+// TextView v = (TextView) listview.getChildAt(i);
+// v.setHasTransientState(false);
+// }
+// }
+// }, 200);
+ }
+
+ });
+ }
+
+ String mItemToDelete = null;
+
+ private class StableArrayAdapter extends ArrayAdapter<String> {
+
+ HashMap<String, Integer> mIdMap = new HashMap<String, Integer>();
+
+ public StableArrayAdapter(Context context, int textViewResourceId,
+ List<String> objects) {
+ super(context, textViewResourceId, objects);
+ for (int i = 0; i < objects.size(); ++i) {
+ mIdMap.put(objects.get(i), i);
+ }
+ }
+
+ @Override
+ public long getItemId(int position) {
+ String item = getItem(position);
+ return mIdMap.get(item);
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/ListViewAddRemoveNoTransition.java b/tests/TransitionTests/src/com/android/transitiontests/ListViewAddRemoveNoTransition.java
new file mode 100644
index 0000000..93d3d97
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/ListViewAddRemoveNoTransition.java
@@ -0,0 +1,104 @@
+/*
+ * 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.content.Context;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+import com.android.transitiontest.R;
+
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+public class ListViewAddRemoveNoTransition extends Activity {
+
+ final ArrayList<String> numList = new ArrayList<String>();
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.list_view_add_remove);
+
+ final LinearLayout container = (LinearLayout) findViewById(R.id.container);
+
+ final ListView listview = (ListView) findViewById(R.id.listview);
+ for (int i = 0; i < 200; ++i) {
+ numList.add(Integer.toString(i));
+ }
+ final StableArrayAdapter adapter = new StableArrayAdapter(this,
+ android.R.layout.simple_list_item_1, numList);
+ listview.setAdapter(adapter);
+
+ listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, final View view, int position, long id) {
+ String item = (String) parent.getItemAtPosition(position);
+ for (int i = 0; i < listview.getChildCount(); ++i) {
+ TextView v = (TextView) listview.getChildAt(i);
+ if (!item.equals(v.getText())) {
+ v.setHasTransientState(true);
+ }
+ }
+ numList.remove(item);
+ adapter.notifyDataSetChanged();
+ view.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ for (int i = 0; i < listview.getChildCount(); ++i) {
+ TextView v = (TextView) listview.getChildAt(i);
+ v.setHasTransientState(false);
+ }
+ }
+ }, 200);
+ }
+
+ });
+ }
+
+ private class StableArrayAdapter extends ArrayAdapter<String> {
+
+ HashMap<String, Integer> mIdMap = new HashMap<String, Integer>();
+
+ public StableArrayAdapter(Context context, int textViewResourceId,
+ List<String> objects) {
+ super(context, textViewResourceId, objects);
+ for (int i = 0; i < objects.size(); ++i) {
+ mIdMap.put(objects.get(i), i);
+ }
+ }
+
+ @Override
+ public long getItemId(int position) {
+ String item = getItem(position);
+ return mIdMap.get(item);
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/LoginActivity.java b/tests/TransitionTests/src/com/android/transitiontests/LoginActivity.java
new file mode 100644
index 0000000..d3c5174
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/LoginActivity.java
@@ -0,0 +1,105 @@
+/*
+ * 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.Scene;
+import android.widget.TextView;
+import android.view.transition.Fade;
+import android.view.transition.Recolor;
+import android.view.transition.Slide;
+import android.view.transition.Transition;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+public class LoginActivity extends Activity {
+ ViewGroup mSceneRoot;
+ Scene mCurrentScene;
+ TransitionManager mTransitionManager;
+ Scene mLoginScene, mPasswordScene, mIncorrectPasswordScene, mSuccessScene, mUsernameTakenScene,
+ mNewUserScene;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_login);
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mLoginScene = new Scene(mSceneRoot, R.layout.activity_login, this);
+ mPasswordScene = new Scene(mSceneRoot, R.layout.login_password, this);
+ mIncorrectPasswordScene = new Scene(mSceneRoot, R.layout.incorrect_password, this);
+ mUsernameTakenScene = new Scene(mSceneRoot, R.layout.username_taken, this);
+ mSuccessScene = new Scene(mSceneRoot, R.layout.success, this);
+ mNewUserScene = new Scene(mSceneRoot, R.layout.new_user, this);
+
+ mTransitionManager = new TransitionManager();
+
+ // Custom transitions in/out of NewUser screen - slide in the 2nd password UI
+ TransitionGroup slider = new TransitionGroup();
+ slider.addTransitions(new Slide().setTargetIds(R.id.retype, R.id.retypeEdit));
+ slider.addTransitions(new Recolor().setTargetIds(R.id.password, R.id.passwordEdit));
+ slider.addTransitions(new Fade());
+ mTransitionManager.setTransition(mLoginScene, mNewUserScene, slider);
+ mTransitionManager.setTransition(mPasswordScene, mNewUserScene, slider);
+ mTransitionManager.setTransition(mNewUserScene, mLoginScene, slider);
+ mTransitionManager.setTransition(mNewUserScene, mPasswordScene, slider);
+
+ // Custom transitions with recoloring password field
+ Transition colorizer = new Recolor().setTargetIds(R.id.password, R.id.passwordEdit);
+ mTransitionManager.setTransition(mLoginScene, mPasswordScene, colorizer);
+ mTransitionManager.setTransition(mPasswordScene, mLoginScene, colorizer);
+
+ mCurrentScene = mLoginScene;
+ mSceneRoot.setCurrentScene(mLoginScene);
+ }
+
+ public void applyScene(Scene scene) {
+ mTransitionManager.transitionTo(scene);
+ mCurrentScene = scene;
+ }
+
+ public void sendMessage(View view) {
+ TextView textView = (TextView) view;
+ CharSequence text = textView.getText();
+ if (text.equals("Cancel")) {
+ applyScene(mLoginScene);
+ } else if (text.equals("Submit")) {
+ if (mCurrentScene == mLoginScene) {
+ applyScene(mPasswordScene);
+ } else if (mCurrentScene == mPasswordScene) {
+ applyScene(Math.random() < .5 ? mSuccessScene : mIncorrectPasswordScene);
+ } else if (mCurrentScene == mNewUserScene) {
+ applyScene(Math.random() < .5 ? mSuccessScene : mUsernameTakenScene);
+ }
+ } else if (text.equals("New User?")) {
+ applyScene(mNewUserScene);
+ } else if (text.equals("Okay")) {
+ if (mCurrentScene == mIncorrectPasswordScene) {
+ applyScene(mPasswordScene);
+ } else { // username taken scene
+ applyScene(mNewUserScene);
+ }
+ } else if (text.equals("Reset")) {
+ applyScene(mLoginScene);
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/LoginActivityFromResources.java b/tests/TransitionTests/src/com/android/transitiontests/LoginActivityFromResources.java
new file mode 100644
index 0000000..fc8aa9b
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/LoginActivityFromResources.java
@@ -0,0 +1,94 @@
+/*
+ * 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.Scene;
+import android.view.transition.TransitionInflater;
+import android.widget.TextView;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+public class LoginActivityFromResources extends Activity {
+ ViewGroup mSceneRoot;
+ Scene mCurrentScene;
+ TransitionManager mTransitionManager = null;
+ Scene mLoginScene, mPasswordScene, mIncorrectPasswordScene, mSuccessScene, mUsernameTakenScene,
+ mNewUserScene;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_login);
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ }
+
+ public void applyScene(Scene scene) {
+ mTransitionManager.transitionTo(scene);
+ mCurrentScene = scene;
+ }
+
+ public void sendMessage(View view) {
+ if (mTransitionManager == null) {
+ TransitionInflater inflater = TransitionInflater.from(this);
+
+ mLoginScene = inflater.inflateScene(R.scene.login_scene, mSceneRoot);
+ mPasswordScene = inflater.inflateScene(R.scene.password_scene, mSceneRoot);
+ mIncorrectPasswordScene =
+ inflater.inflateScene(R.scene.incorrect_password_scene,mSceneRoot);
+ mUsernameTakenScene =
+ inflater.inflateScene(R.scene.username_taken_scene, mSceneRoot);
+ mSuccessScene = inflater.inflateScene(R.scene.success_scene, mSceneRoot);
+ mNewUserScene = inflater.inflateScene(R.scene.new_user_scene, mSceneRoot);
+
+ mTransitionManager =
+ inflater.inflateTransitionManager(R.transition.login_transition_mgr,
+ mSceneRoot);
+
+ mCurrentScene = mLoginScene;
+ mSceneRoot.setCurrentScene(mLoginScene);
+ }
+ TextView textView = (TextView) view;
+ CharSequence text = textView.getText();
+ if (text.equals("Cancel")) {
+ applyScene(mLoginScene);
+ } else if (text.equals("Submit")) {
+ if (mCurrentScene == mLoginScene) {
+ applyScene(mPasswordScene);
+ } else if (mCurrentScene == mPasswordScene) {
+ applyScene(Math.random() < .5 ? mSuccessScene : mIncorrectPasswordScene);
+ } else if (mCurrentScene == mNewUserScene) {
+ applyScene(Math.random() < .5 ? mSuccessScene : mUsernameTakenScene);
+ }
+ } else if (text.equals("New User?")) {
+ applyScene(mNewUserScene);
+ } else if (text.equals("Okay")) {
+ if (mCurrentScene == mIncorrectPasswordScene) {
+ applyScene(mPasswordScene);
+ } else { // username taken scene
+ applyScene(mNewUserScene);
+ }
+ } else if (text.equals("Reset")) {
+ applyScene(mLoginScene);
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/OverlayTest.java b/tests/TransitionTests/src/com/android/transitiontests/OverlayTest.java
new file mode 100644
index 0000000..2f8224a
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/OverlayTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.widget.Button;
+import com.android.transitiontest.R;
+
+
+public class OverlayTest extends Activity {
+
+ ViewGroup mContainer;
+ ViewGroup mRoot;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.overlay_test);
+
+ mContainer = (ViewGroup) findViewById(R.id.container);
+ mRoot = (ViewGroup) mContainer.getParent();
+ }
+
+ public void onClick(View view) {
+ final Button fadingButton = (Button) findViewById(R.id.fadingButton);
+ if (fadingButton != null) {
+ mContainer.removeView(fadingButton);
+ mRoot.getOverlay().add(fadingButton);
+ fadingButton.animate().alpha(0).setDuration(1000).withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ fadingButton.setAlpha(1);
+ mRoot.getOverlay().remove(fadingButton);
+ mContainer.addView(fadingButton);
+ }
+ });
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/Reparenting.java b/tests/TransitionTests/src/com/android/transitiontests/Reparenting.java
new file mode 100644
index 0000000..8ee9d3f
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/Reparenting.java
@@ -0,0 +1,76 @@
+/*
+ * 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.Move;
+import android.view.transition.Scene;
+import android.view.transition.TransitionManager;
+import android.widget.Button;
+import com.android.transitiontest.R;
+
+public class Reparenting extends Activity {
+
+ ViewGroup mSceneRoot;
+ ViewGroup mContainer1, mContainer2;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.reparenting);
+
+ ViewGroup container = (ViewGroup) findViewById(R.id.container);
+ mContainer1 = (ViewGroup) findViewById(R.id.container1);
+ mContainer2 = (ViewGroup) findViewById(R.id.container2);
+ System.out.println("container 1 and 2 " + mContainer1 + ", " + mContainer2);
+
+ setupButtons(0, mContainer1);
+ setupButtons(3, mContainer2);
+
+ mSceneRoot = container;
+ }
+
+ private void setupButtons(int startIndex, ViewGroup parent) {
+ for (int i = startIndex; i < (startIndex + 3); ++i) {
+ Button button = new Button(this);
+ button.setText(Integer.toString(i));
+ button.setOnClickListener(mButtonListener);
+ parent.addView(button);
+ }
+ }
+
+ private View.OnClickListener mButtonListener = new View.OnClickListener() {
+ @Override
+ public void onClick(final View v) {
+ Scene newScene = new Scene(mSceneRoot);
+ newScene.setEnterAction(new Runnable() {
+ @Override
+ public void run() {
+ ViewGroup oldParent = (ViewGroup) v.getParent();
+ ViewGroup newParent = oldParent == mContainer1 ? mContainer2 : mContainer1;
+ oldParent.removeView(v);
+ newParent.addView(v);
+ }
+ });
+ Move reparent = new Move();
+ reparent.setReparent(true);
+ TransitionManager.go(newScene, reparent);
+ }
+ };
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/ResourceLoadingTest.java b/tests/TransitionTests/src/com/android/transitiontests/ResourceLoadingTest.java
new file mode 100644
index 0000000..c05f898
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/ResourceLoadingTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.Scene;
+import android.view.transition.TransitionInflater;
+import android.view.transition.Transition;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+public class ResourceLoadingTest extends Activity {
+
+ private static final int SEARCH_SCREEN = 0;
+ private static final int RESULTS_SCREEN = 1;
+ ViewGroup mSceneRoot;
+ static int mCurrentScene;
+ TransitionManager mTransitionManager = null;
+ TransitionInflater mInflater;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mCurrentScene = SEARCH_SCREEN;
+
+ mInflater = TransitionInflater.from(this);
+ }
+
+ public void sendMessage(View view) {
+ if (mTransitionManager == null) {
+ try {
+ TransitionInflater inflater = TransitionInflater.from(this);
+ mTransitionManager =
+ inflater.inflateTransitionManager(R.transition.my_transition_mgr,
+ mSceneRoot);
+ Scene loadedScene = inflater.inflateScene(R.scene.my_scene, mSceneRoot);
+ System.out.println("loadedScene = " + loadedScene);
+ Transition loadedTransition = inflater.inflateTransition(R.transition.my_transition);
+ System.out.println("loadedTransition = " + loadedTransition);
+ } catch (Exception e) {
+ System.out.println("Problem loading scene resource: " + e);
+ }
+ }
+ if (mCurrentScene == RESULTS_SCREEN) {
+ Scene scene = mInflater.inflateScene(R.scene.search_scene, mSceneRoot);
+ mTransitionManager.transitionTo(scene);
+ mCurrentScene = SEARCH_SCREEN;
+ } else {
+ Scene scene = mInflater.inflateScene(R.scene.results_scene, mSceneRoot);
+ mTransitionManager.transitionTo(scene);
+ mCurrentScene = RESULTS_SCREEN;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/ScenesTestAutoTargets.java b/tests/TransitionTests/src/com/android/transitiontests/ScenesTestAutoTargets.java
new file mode 100644
index 0000000..ae8ee5b
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/ScenesTestAutoTargets.java
@@ -0,0 +1,66 @@
+/*
+ * 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.Fade;
+import android.view.transition.Move;
+import android.view.transition.Recolor;
+import android.view.transition.Scene;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+public class ScenesTestAutoTargets extends Activity {
+ ViewGroup mSceneRoot;
+ static Scene mCurrentScene;
+ TransitionManager mTransitionManager = null;
+ Scene mResultsScreen, mSearchScreen;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mSearchScreen = new Scene(mSceneRoot, R.layout.search_screen, this);
+ mResultsScreen = new Scene(mSceneRoot, R.layout.results_screen, this);
+
+ TransitionGroup transition = new TransitionGroup();
+ transition.addTransitions(new Fade(), new Move(), new Recolor());
+
+ mTransitionManager = new TransitionManager();
+ mTransitionManager.setTransition(mSearchScreen, transition);
+ mTransitionManager.setTransition(mResultsScreen, transition);
+ }
+
+ public void sendMessage(View view) {
+ if (mCurrentScene == mResultsScreen) {
+ mTransitionManager.transitionTo(mSearchScreen);
+ mCurrentScene = mSearchScreen;
+ } else {
+ mTransitionManager.transitionTo(mResultsScreen);
+ mCurrentScene = mResultsScreen;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/ScenesTestAutoTransition.java b/tests/TransitionTests/src/com/android/transitiontests/ScenesTestAutoTransition.java
new file mode 100644
index 0000000..9184e87
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/ScenesTestAutoTransition.java
@@ -0,0 +1,57 @@
+/*
+ * 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.AutoTransition;
+import android.view.transition.Scene;
+import android.view.transition.Transition;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+public class ScenesTestAutoTransition extends Activity {
+ ViewGroup mSceneRoot;
+ static Scene mCurrentScene;
+ Transition mTransition = new AutoTransition();
+ Scene mResultsScreen, mSearchScreen;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mSearchScreen = new Scene(mSceneRoot, R.layout.search_screen, this);
+ mResultsScreen = new Scene(mSceneRoot, R.layout.results_screen, this);
+ }
+
+ public void sendMessage(View view) {
+ if (mCurrentScene == mResultsScreen) {
+ TransitionManager.go(mSearchScreen, mTransition);
+ mCurrentScene = mSearchScreen;
+ } else {
+ TransitionManager.go(mResultsScreen, mTransition);
+ mCurrentScene = mResultsScreen;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/ScenesTestAutoTransition2.java b/tests/TransitionTests/src/com/android/transitiontests/ScenesTestAutoTransition2.java
new file mode 100644
index 0000000..fc8acc1
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/ScenesTestAutoTransition2.java
@@ -0,0 +1,55 @@
+/*
+ * 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.Scene;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+public class ScenesTestAutoTransition2 extends Activity {
+ ViewGroup mSceneRoot;
+ static Scene mCurrentScene;
+
+ Scene mResultsScreen, mSearchScreen;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mSearchScreen = new Scene(mSceneRoot, R.layout.search_screen, this);
+ mResultsScreen = new Scene(mSceneRoot, R.layout.results_screen, this);
+ }
+
+ public void sendMessage(View view) {
+ if (mCurrentScene == mResultsScreen) {
+ TransitionManager.go(mSearchScreen);
+ mCurrentScene = mSearchScreen;
+ } else {
+ TransitionManager.go(mResultsScreen);
+ mCurrentScene = mResultsScreen;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/ScenesTestv21.java b/tests/TransitionTests/src/com/android/transitiontests/ScenesTestv21.java
new file mode 100644
index 0000000..2dae463
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/ScenesTestv21.java
@@ -0,0 +1,75 @@
+/*
+ * 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.Fade;
+import android.view.transition.Move;
+import android.view.transition.Recolor;
+import android.view.transition.Scene;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+public class ScenesTestv21 extends Activity {
+ ViewGroup mSceneRoot;
+ static Scene mCurrentScene;
+ TransitionManager mTransitionManager = null;
+ Scene mResultsScreen, mSearchScreen;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.search_screen);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mSearchScreen = new Scene(mSceneRoot, R.layout.search_screen, this);
+ mResultsScreen = new Scene(mSceneRoot, R.layout.results_screen, this);
+
+ TransitionGroup transitionToResults = new TransitionGroup();
+ Fade fade = new Fade();
+ fade.setTargetIds(R.id.resultsText, R.id.resultsList);
+ fade.setStartDelay(300);
+ transitionToResults.addTransitions(fade);
+ transitionToResults.addTransitions(new Move().setTargetIds(R.id.searchContainer));
+ transitionToResults.addTransitions(new Recolor().setTargetIds(R.id.container));
+
+ TransitionGroup transitionToSearch = new TransitionGroup();
+ transitionToSearch.addTransitions(new Fade().setTargetIds(R.id.resultsText, R.id.resultsList));
+ transitionToSearch.addTransitions(new Move().setTargetIds(R.id.searchContainer));
+ transitionToSearch.addTransitions(new Recolor().setTargetIds(R.id.container));
+ mTransitionManager = new TransitionManager();
+ mTransitionManager.setTransition(mSearchScreen, transitionToSearch);
+ mTransitionManager.setTransition(mResultsScreen, transitionToResults);
+ }
+
+ public void sendMessage(View view) {
+ if (mCurrentScene == mResultsScreen) {
+ mTransitionManager.transitionTo(mSearchScreen);
+ mCurrentScene = mSearchScreen;
+ } else {
+ mTransitionManager.transitionTo(mResultsScreen);
+ mCurrentScene = mResultsScreen;
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/SequenceTest.java b/tests/TransitionTests/src/com/android/transitiontests/SequenceTest.java
new file mode 100644
index 0000000..8cb6a1a
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/SequenceTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.Scene;
+import android.view.transition.Transition;
+import android.widget.Button;
+import android.view.transition.Fade;
+import android.view.transition.Move;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+public class SequenceTest extends Activity {
+
+ Button mRemovingButton, mInvisibleButton, mGoneButton;
+ Scene mScene1, mScene2;
+ ViewGroup mSceneRoot;
+ TransitionGroup sequencedFade, reverseSequencedFade;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.fading_test);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mRemovingButton = (Button) findViewById(R.id.removingButton);
+ mInvisibleButton = (Button) findViewById(R.id.invisibleButton);
+ mGoneButton = (Button) findViewById(R.id.goneButton);
+
+ mScene1 = new Scene(mSceneRoot, R.layout.fading_test, this);
+ mScene2 = new Scene(mSceneRoot, R.layout.fading_test_scene_2, this);
+
+ Transition fade1 = new Fade().setTargetIds(R.id.removingButton);
+ Transition fade2 = new Fade().setTargetIds(R.id.invisibleButton);
+ Transition fade3 = new Fade().setTargetIds(R.id.goneButton);
+ TransitionGroup fader = new TransitionGroup(TransitionGroup.SEQUENTIALLY);
+ fader.addTransitions(fade1, fade2, fade3, new Move());
+ sequencedFade = fader;
+
+ reverseSequencedFade = new TransitionGroup(TransitionGroup.SEQUENTIALLY);
+ reverseSequencedFade.addTransitions(new Move(), fade3, fade2, fade1);
+
+ mSceneRoot.setCurrentScene(mScene1);
+ }
+
+ public void sendMessage(View view) {
+ if (mSceneRoot.getCurrentScene() == mScene1) {
+ TransitionManager.go(mScene2, sequencedFade);
+ } else {
+ TransitionManager.go(mScene1, reverseSequencedFade);
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/SequenceTestSimple.java b/tests/TransitionTests/src/com/android/transitiontests/SequenceTestSimple.java
new file mode 100644
index 0000000..93bfdb4
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/SequenceTestSimple.java
@@ -0,0 +1,71 @@
+/*
+ * 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.Scene;
+import android.widget.Button;
+import android.view.transition.Fade;
+import android.view.transition.Move;
+import android.view.transition.Transition;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+public class SequenceTestSimple extends Activity {
+
+ Button mRemovingButton, mInvisibleButton, mGoneButton;
+ Scene mScene1, mScene2;
+ ViewGroup mSceneRoot;
+ Transition sequencedFade;
+ TransitionGroup sequencedFadeReverse;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.fading_test_simple);
+
+ View container = (View) findViewById(R.id.container);
+ mSceneRoot = (ViewGroup) container.getParent();
+
+ mRemovingButton = (Button) findViewById(R.id.removingButton);
+
+ mScene1 = new Scene(mSceneRoot, R.layout.fading_test_simple, this);
+ mScene2 = new Scene(mSceneRoot, R.layout.fading_test_simple2, this);
+
+ TransitionGroup fader = new TransitionGroup(TransitionGroup.SEQUENTIALLY);
+ fader.addTransitions(new Fade().setTargetIds(R.id.removingButton));
+ fader.addTransitions(new Move().setTargetIds(R.id.sceneSwitchButton));
+ sequencedFade = fader;
+
+ sequencedFadeReverse = new TransitionGroup(TransitionGroup.SEQUENTIALLY);
+ sequencedFadeReverse.addTransitions(new Move().setTargetIds(R.id.sceneSwitchButton));
+ sequencedFadeReverse.addTransitions(new Fade().setTargetIds(R.id.removingButton));
+
+ mSceneRoot.setCurrentScene(mScene1);
+ }
+
+ public void sendMessage(View view) {
+ if (mSceneRoot.getCurrentScene() == mScene1) {
+ TransitionManager.go(mScene2, sequencedFade);
+ } else {
+ TransitionManager.go(mScene1, sequencedFadeReverse);
+ }
+ }}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/SurfaceAndTextureViews.java b/tests/TransitionTests/src/com/android/transitiontests/SurfaceAndTextureViews.java
new file mode 100644
index 0000000..f8829c9
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/SurfaceAndTextureViews.java
@@ -0,0 +1,195 @@
+/*
+ * 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.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.SurfaceTexture;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.TextureView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Crossfade;
+import android.view.transition.Move;
+import android.view.transition.Scene;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import android.widget.Button;
+import com.android.transitiontest.R;
+
+import static android.widget.LinearLayout.LayoutParams;
+
+public class SurfaceAndTextureViews extends Activity {
+
+ SimpleView mView;
+ SimpleSurfaceView mSurfaceView;
+ SimpleTextureView mTextureView;
+ private static final int SMALL_SIZE = 200;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.surface_texture_views);
+
+ final ViewGroup container = (ViewGroup) findViewById(R.id.container);
+ Button toggleButton = (Button) findViewById(R.id.toggleButton);
+
+ mView = new SimpleView(this);
+ mView.setId(0);
+ mView.setLayoutParams(new LayoutParams(SMALL_SIZE, SMALL_SIZE));
+ container.addView(mView);
+
+ mSurfaceView = new SimpleSurfaceView(this);
+ mSurfaceView.setId(1);
+ mSurfaceView.setLayoutParams(new LayoutParams(SMALL_SIZE, SMALL_SIZE));
+ container.addView(mSurfaceView);
+
+ mTextureView = new SimpleTextureView(this);
+ mTextureView.setId(2);
+ mTextureView.setLayoutParams(new LayoutParams(SMALL_SIZE, SMALL_SIZE));
+ container.addView(mTextureView);
+
+ final TransitionGroup transition = new TransitionGroup();
+ transition.addTransitions(new Move(), new Crossfade().setTargetIds(0, 1, 2));
+
+ toggleButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Scene newScene = new Scene(container);
+ newScene.setEnterAction(new Runnable() {
+ @Override
+ public void run() {
+ if (mView.getWidth() <= SMALL_SIZE) {
+ mView.setLayoutParams(new LayoutParams(SMALL_SIZE * 2, SMALL_SIZE));
+ mSurfaceView.setLayoutParams(new LayoutParams(SMALL_SIZE * 2, SMALL_SIZE));
+ mTextureView.setLayoutParams(new LayoutParams(SMALL_SIZE * 2, SMALL_SIZE));
+ mView.mColor = SimpleView.LARGE_COLOR;
+ mSurfaceView.mColor = SimpleSurfaceView.LARGE_COLOR;
+ mTextureView.mColor = SimpleTextureView.LARGE_COLOR;
+ } else {
+ mView.setLayoutParams(new LayoutParams(SMALL_SIZE, SMALL_SIZE));
+ mSurfaceView.setLayoutParams(new LayoutParams(SMALL_SIZE, SMALL_SIZE));
+ mTextureView.setLayoutParams(new LayoutParams(SMALL_SIZE, SMALL_SIZE));
+ mView.mColor = SimpleView.SMALL_COLOR;
+ mSurfaceView.mColor = SimpleSurfaceView.SMALL_COLOR;
+ mTextureView.mColor = SimpleTextureView.SMALL_COLOR;
+ }
+ }
+ });
+ TransitionManager.go(newScene, transition);
+ }
+ });
+
+ }
+
+ static private class SimpleView extends View {
+ static final int SMALL_COLOR = Color.BLUE;
+ static final int LARGE_COLOR = Color.YELLOW;
+ int mColor = SMALL_COLOR;
+
+ private SimpleView(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ canvas.drawColor(mColor);
+ }
+ }
+
+ static private class SimpleSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
+
+ static final int SMALL_COLOR = Color.GREEN;
+ static final int LARGE_COLOR = Color.GRAY;
+ int mColor = SMALL_COLOR;
+ SurfaceHolder mHolder = null;
+
+ private SimpleSurfaceView(Context context) {
+ super(context);
+ SurfaceHolder holder = getHolder();
+ holder.addCallback(this);
+ }
+
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ System.out.println("surfaceCreated");
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ System.out.println("surfaceChanged: w h = " + width + ", " + height);
+ Canvas canvas = holder.lockCanvas();
+ canvas.drawColor(mColor);
+ holder.unlockCanvasAndPost(canvas);
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ System.out.println("surfaceDestroyed");
+ }
+ }
+
+ static private class SimpleTextureView extends TextureView implements TextureView.SurfaceTextureListener {
+
+ static final int SMALL_COLOR = Color.RED;
+ static final int LARGE_COLOR = Color.CYAN;
+ int mColor = SMALL_COLOR;
+
+ private SimpleTextureView(Context context) {
+ super(context);
+ setSurfaceTextureListener(this);
+ }
+
+ private SimpleTextureView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setSurfaceTextureListener(this);
+ }
+
+ private SimpleTextureView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ setSurfaceTextureListener(this);
+ }
+
+ @Override
+ public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
+ System.out.println("SurfaceTexture available");
+ }
+
+ @Override
+ public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+ System.out.println("SurfaceTexture size changed to " + width + ", " + height);
+ Canvas canvas = lockCanvas();
+ canvas.drawColor(mColor);
+ unlockCanvasAndPost(canvas);
+ }
+
+ @Override
+ public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
+ return false;
+ }
+
+ @Override
+ public void onSurfaceTextureUpdated(SurfaceTexture surface) {
+ System.out.println("SurfaceTexture updated");
+ }
+ }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/UniqueIds.java b/tests/TransitionTests/src/com/android/transitiontests/UniqueIds.java
new file mode 100644
index 0000000..18537c7
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/UniqueIds.java
@@ -0,0 +1,86 @@
+/*
+ * 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.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Scene;
+import android.view.transition.Transition;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.view.transition.TransitionManager;
+import com.android.transitiontest.R;
+
+
+import java.util.HashMap;
+
+public class UniqueIds extends Activity {
+ ViewGroup mSceneRoot;
+ static Scene mCurrentScene;
+ TransitionManager mTransitionManager = null;
+ HashMap<Button, ToggleScene> mSceneMap = new HashMap<Button, ToggleScene>();
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.unique_id_test);
+
+ LinearLayout container = (LinearLayout) findViewById(R.id.container);
+ LayoutInflater inflater = getLayoutInflater();
+ Button button = (Button) inflater.inflate(R.layout.button_template, null);
+ container.addView(button);
+ ToggleScene scene = new ToggleScene(container, button);
+ mSceneMap.put(button, scene);
+ button = (Button) inflater.inflate(R.layout.button_template, null);
+ container.addView(button);
+ scene = new ToggleScene(container, button);
+ mSceneMap.put(button, scene);
+ }
+
+ public void sendMessage(View view) {
+ mSceneMap.get(view).changeToScene();
+ }
+
+ class ToggleScene {
+ Scene mScene;
+ Transition mTransition;
+ Button mButton;
+
+ ToggleScene(ViewGroup rootView, Button button) {
+ mScene = new Scene(rootView);
+ mButton = button;
+ mScene.setEnterAction(new Runnable() {
+ @Override
+ public void run() {
+ if (mButton.getLeft() == 0) {
+ mButton.offsetLeftAndRight(500);
+ } else {
+ int width = mButton.getWidth();
+ mButton.setLeft(0);
+ mButton.setRight(width);
+ }
+ }
+ });
+ }
+
+ void changeToScene() {
+ TransitionManager.go(mScene);
+ }
+ }
+}
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 77168f9..fe5c810 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -172,6 +172,7 @@ private:
bool isValidResourceType(const String8& type)
{
return type == "anim" || type == "animator" || type == "interpolator"
+ || type == "transition" || type == "scene"
|| type == "drawable" || type == "layout"
|| type == "values" || type == "xml" || type == "raw"
|| type == "color" || type == "menu" || type == "mipmap";
@@ -932,6 +933,8 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
sp<ResourceTypeSet> anims;
sp<ResourceTypeSet> animators;
sp<ResourceTypeSet> interpolators;
+ sp<ResourceTypeSet> transitions;
+ sp<ResourceTypeSet> scenes;
sp<ResourceTypeSet> xmls;
sp<ResourceTypeSet> raws;
sp<ResourceTypeSet> colors;
@@ -943,6 +946,8 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
ASSIGN_IT(anim);
ASSIGN_IT(animator);
ASSIGN_IT(interpolator);
+ ASSIGN_IT(transition);
+ ASSIGN_IT(scene);
ASSIGN_IT(xml);
ASSIGN_IT(raw);
ASSIGN_IT(color);
@@ -965,6 +970,8 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
!applyFileOverlay(bundle, assets, &anims, "anim") ||
!applyFileOverlay(bundle, assets, &animators, "animator") ||
!applyFileOverlay(bundle, assets, &interpolators, "interpolator") ||
+ !applyFileOverlay(bundle, assets, &transitions, "transition") ||
+ !applyFileOverlay(bundle, assets, &scenes, "scene") ||
!applyFileOverlay(bundle, assets, &xmls, "xml") ||
!applyFileOverlay(bundle, assets, &raws, "raw") ||
!applyFileOverlay(bundle, assets, &colors, "color") ||
@@ -1024,6 +1031,20 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
}
}
+ if (transitions != NULL) {
+ err = makeFileResources(bundle, assets, &table, transitions, "transition");
+ if (err != NO_ERROR) {
+ hasErrors = true;
+ }
+ }
+
+ if (scenes != NULL) {
+ err = makeFileResources(bundle, assets, &table, scenes, "scene");
+ if (err != NO_ERROR) {
+ hasErrors = true;
+ }
+ }
+
if (interpolators != NULL) {
err = makeFileResources(bundle, assets, &table, interpolators, "interpolator");
if (err != NO_ERROR) {
@@ -1168,6 +1189,36 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
err = NO_ERROR;
}
+ if (transitions != NULL) {
+ ResourceDirIterator it(transitions, String8("transition"));
+ while ((err=it.next()) == NO_ERROR) {
+ err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
+ if (err != NO_ERROR) {
+ hasErrors = true;
+ }
+ }
+
+ if (err < NO_ERROR) {
+ hasErrors = true;
+ }
+ err = NO_ERROR;
+ }
+
+ if (scenes != NULL) {
+ ResourceDirIterator it(scenes, String8("scene"));
+ while ((err=it.next()) == NO_ERROR) {
+ err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
+ if (err != NO_ERROR) {
+ hasErrors = true;
+ }
+ }
+
+ if (err < NO_ERROR) {
+ hasErrors = true;
+ }
+ err = NO_ERROR;
+ }
+
if (xmls != NULL) {
ResourceDirIterator it(xmls, String8("xml"));
while ((err=it.next()) == NO_ERROR) {