diff options
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 Binary files differnew file mode 100644 index 0000000..8074c4c --- /dev/null +++ b/tests/TransitionTests/res/drawable-hdpi/icon.png diff --git a/tests/TransitionTests/res/drawable-ldpi/icon.png b/tests/TransitionTests/res/drawable-ldpi/icon.png Binary files differnew file mode 100644 index 0000000..1095584 --- /dev/null +++ b/tests/TransitionTests/res/drawable-ldpi/icon.png diff --git a/tests/TransitionTests/res/drawable-mdpi/icon.png b/tests/TransitionTests/res/drawable-mdpi/icon.png Binary files differnew file mode 100644 index 0000000..a07c69f --- /dev/null +++ b/tests/TransitionTests/res/drawable-mdpi/icon.png diff --git a/tests/TransitionTests/res/drawable-nodpi/arrow_thumbnail.png b/tests/TransitionTests/res/drawable-nodpi/arrow_thumbnail.png Binary files differnew file mode 100644 index 0000000..5cae8f2 --- /dev/null +++ b/tests/TransitionTests/res/drawable-nodpi/arrow_thumbnail.png diff --git a/tests/TransitionTests/res/drawable-nodpi/self_portrait_square.jpg b/tests/TransitionTests/res/drawable-nodpi/self_portrait_square.jpg Binary files differnew file mode 100644 index 0000000..8cce1c1 --- /dev/null +++ b/tests/TransitionTests/res/drawable-nodpi/self_portrait_square.jpg diff --git a/tests/TransitionTests/res/drawable-nodpi/self_portrait_square_100.jpg b/tests/TransitionTests/res/drawable-nodpi/self_portrait_square_100.jpg Binary files differnew file mode 100644 index 0000000..26c0a85 --- /dev/null +++ b/tests/TransitionTests/res/drawable-nodpi/self_portrait_square_100.jpg diff --git a/tests/TransitionTests/res/drawable-nodpi/self_portrait_square_200.jpg b/tests/TransitionTests/res/drawable-nodpi/self_portrait_square_200.jpg Binary files differnew file mode 100644 index 0000000..f18ae5b --- /dev/null +++ b/tests/TransitionTests/res/drawable-nodpi/self_portrait_square_200.jpg 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) { |