diff options
author | George Mount <mount@google.com> | 2014-02-06 14:12:53 -0800 |
---|---|---|
committer | George Mount <mount@google.com> | 2014-02-25 11:12:16 -0800 |
commit | f10587faadb9080a7bf9991cbe04bac5525da482 (patch) | |
tree | 61d5195d6d05d119c81bc75cfc659de4a7df5514 /core | |
parent | 875e2101d71afe7e4acf10b061c942fbf7294775 (diff) | |
download | frameworks_base-f10587faadb9080a7bf9991cbe04bac5525da482.zip frameworks_base-f10587faadb9080a7bf9991cbe04bac5525da482.tar.gz frameworks_base-f10587faadb9080a7bf9991cbe04bac5525da482.tar.bz2 |
Change Activity Scene Transitions to be more automatic.
Shared element transitions are enabled by default
when the Window has a TransitionManager.
Shared element location and size are captured and
transferred to the target Activity.
ActionBar is treated as a shared element.
Change-Id: I0f22ea4e5cbe80254e848444e3f235cb742684f4
Diffstat (limited to 'core')
-rw-r--r-- | core/java/android/app/Activity.java | 150 | ||||
-rw-r--r-- | core/java/android/app/ActivityOptions.java | 153 | ||||
-rw-r--r-- | core/java/android/transition/Transition.java | 45 | ||||
-rw-r--r-- | core/java/android/transition/TransitionInflater.java | 43 | ||||
-rw-r--r-- | core/java/android/transition/TransitionManager.java | 150 | ||||
-rw-r--r-- | core/java/android/view/View.java | 29 | ||||
-rw-r--r-- | core/java/android/view/ViewGroup.java | 40 | ||||
-rw-r--r-- | core/java/android/view/Window.java | 38 | ||||
-rw-r--r-- | core/java/com/android/internal/app/ActionBarImpl.java | 5 | ||||
-rw-r--r-- | core/res/res/layout-xlarge/screen_action_bar.xml | 1 | ||||
-rw-r--r-- | core/res/res/layout/screen_action_bar.xml | 1 | ||||
-rw-r--r-- | core/res/res/layout/screen_custom_title.xml | 1 | ||||
-rw-r--r-- | core/res/res/values/attrs.xml | 8 | ||||
-rw-r--r-- | core/res/res/values/public.xml | 2 |
14 files changed, 320 insertions, 346 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 4f0683c..b3c10da 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -18,8 +18,10 @@ package android.app; import android.annotation.NonNull; import android.transition.Scene; +import android.transition.Transition; import android.transition.TransitionManager; import android.util.ArrayMap; +import android.util.Pair; import android.util.SuperNotCalledException; import com.android.internal.app.ActionBarImpl; import com.android.internal.policy.PolicyManager; @@ -93,6 +95,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.HashMap; +import java.util.Map; /** * An activity is a single, focused thing that the user can do. Almost all @@ -3446,7 +3449,53 @@ public class Activity extends ContextThemeWrapper * @see #startActivity */ public void startActivityForResult(Intent intent, int requestCode) { - startActivityForResult(intent, requestCode, null); + ArrayMap<String, View> sharedElements = new ArrayMap<String, View>(); + + if (mActionBar != null) { + mActionBar.captureSharedElements(sharedElements); + } + Bundle opts = mWindow.startExitTransition(sharedElements); + startActivityForResult(intent, requestCode, opts); + } + + /** + * Same as {@link #startActivityForResult(android.content.Intent, int)} except that + * shared element state is passed to the called Activity during the Activity Scene Transition. + * The Activity must have a TransitionManager with a Transition associated with exiting + * the current Scene. + * @param intent The intent to start. + * @param requestCode If >= 0, this code will be returned in + * onActivityResult() when the activity exits. + * @param sharedElements Views to be transitioned to the called Activity and their + * names as used in the called Activity. + * Views must not have null shared element name, however, if the + * Pair has a null name, the shared element name will be reused + * for the launched Activity's shared element name. + * @see android.transition.TransitionManager#setExitTransition(android.transition.Scene, android.transition.Transition) + * @see View#setSharedElementName(String) + */ + public void startActivityForResult(Intent intent, int requestCode, + Pair<View, String>... sharedElements) { + ArrayMap<String, View> sharedElementMap = new ArrayMap<String, View>(); + if (sharedElements != null) { + for (Pair<View, String> sharedElement: sharedElements) { + View view = sharedElement.first; + String sharedElementName = view.getSharedElementName(); + if (sharedElementName == null) { + throw new IllegalArgumentException("sharedElement must have a non-null " + + "sharedElementName"); + } + String name = sharedElement.second == null + ? sharedElementName : sharedElement.second; + sharedElementMap.put(name, view); + } + } + if (mActionBar != null) { + mActionBar.captureSharedElements(sharedElementMap); + } + + Bundle options = mWindow.startExitTransition(sharedElementMap); + startActivityForResult(intent, requestCode, options); } /** @@ -3484,14 +3533,6 @@ public class Activity extends ContextThemeWrapper * @see #startActivity */ public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) { - TransitionManager tm = getContentTransitionManager(); - if (tm != null && options != null) { - ActivityOptions activityOptions = new ActivityOptions(options); - if (activityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) { - getWindow().startExitTransition(activityOptions); - options = activityOptions.toBundle(); - } - } if (mParent == null) { Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity( @@ -3664,7 +3705,25 @@ public class Activity extends ContextThemeWrapper */ @Override public void startActivity(Intent intent) { - startActivity(intent, null); + startActivity(intent, (Pair<View, String>[]) null); + } + + /** + * Same as {@link #startActivity(android.content.Intent)} except that shared element + * state is passed to the called Activity during the Activity Scene Transition. + * The Activity must have a TransitionManager with a Transition associated with exiting + * the current Scene. + * @param intent The intent to start. + * @param sharedElements Views to be transitioned to the called Activity and their + * names as used in the called Activity. + * Views must not have null shared element name, however, if the + * Pair has a null name, the shared element name will be reused + * for the launched Activity's shared element name. + * @see android.transition.TransitionManager#setExitTransition(android.transition.Scene, android.transition.Transition) + * @see View#setSharedElementName(String) + */ + public void startActivity(Intent intent, Pair<View, String>... sharedElements) { + startActivityForResult(intent, -1, sharedElements); } /** @@ -4720,7 +4779,8 @@ public class Activity extends ContextThemeWrapper */ public final void setProgressBarIndeterminate(boolean indeterminate) { getWindow().setFeatureInt(Window.FEATURE_PROGRESS, - indeterminate ? Window.PROGRESS_INDETERMINATE_ON : Window.PROGRESS_INDETERMINATE_OFF); + indeterminate ? Window.PROGRESS_INDETERMINATE_ON + : Window.PROGRESS_INDETERMINATE_OFF); } /** @@ -5330,12 +5390,6 @@ public class Activity extends ContextThemeWrapper mTransitionActivityOptions = activityOptions; sceneTransitionListener = new Window.SceneTransitionListener() { @Override - public void enterSharedElement(Bundle transitionArgs) { - startSharedElementTransition(transitionArgs); - mTransitionActivityOptions = null; - } - - @Override public void nullPendingTransition() { overridePendingTransition(0, 0); } @@ -5349,6 +5403,16 @@ public class Activity extends ContextThemeWrapper public void convertToTranslucent() { Activity.this.convertToTranslucent(null); } + + @Override + public void sharedElementStart(Transition transition) { + Activity.this.onCaptureSharedElementStart(transition); + } + + @Override + public void sharedElementEnd() { + Activity.this.onCaptureSharedElementEnd(); + } }; } @@ -5542,53 +5606,23 @@ public class Activity extends ContextThemeWrapper } /** - * Gets the entering Activity transition args. Will be null if - * {@link android.app.ActivityOptions#makeSceneTransitionAnimation(android.os.Bundle)} was - * not used to pass a Bundle to startActivity. The Bundle passed to that method in the - * calling Activity is returned here. - * <p>After startSharedElementTransition is called, this method will return null.</p> + * Called when setting up Activity Scene transitions when the start state for shared + * elements has been captured. Override this method to modify the start position of shared + * elements for the entry Transition. * - * @return The Bundle passed into Bundle parameter of - * {@link android.app.ActivityOptions#makeSceneTransitionAnimation(android.os.Bundle)} - * in the calling Activity. + * @param transition The <code>Transition</code> being used to change + * bounds of shared elements in the source Activity to + * the bounds defined by the entering Scene. */ - public Bundle getTransitionArgs() { - if (mTransitionActivityOptions == null) { - return null; - } - return mTransitionActivityOptions.getSceneTransitionArgs(); - } - - /** - * Override to transfer a shared element from a calling Activity to this Activity. - * Shared elements will be made VISIBLE before this call. The Activity is responsible - * for transitioning the shared elements from their location to the eventual destination. - * The shared element will be laid out a the destination when this method is called. - * - * @param transitionArgs The same as returned from {@link #getTransitionArgs()}, this should - * contain information from the calling Activity to tell where the - * shared element should be placed. - */ - protected void startSharedElementTransition(Bundle transitionArgs) { + public void onCaptureSharedElementStart(Transition transition) { } /** - * Controls how the background fade is triggered when there is an entering Activity transition. - * If fadeEarly is true, the Window background will fade in as soon as the shared elements are - * ready to switch. If fadeEarly is false, the background will fade only after the calling - * Activity's exit transition completes. By default, the Window will fade in when the calling - * Activity's exit transition completes. - * - * @param fadeEarly Set to true to fade out the exiting Activity as soon as the shared elements - * are transferred. Set to false to fade out the exiting Activity as soon as - * the shared element is transferred. - * @see android.app.ActivityOptions#makeSceneTransitionAnimation(android.os.Bundle) + * Called when setting up Activity Scene transitions when the final state for + * shared elements state has been captured. Override this method to modify the destination + * position of shared elements for the entry Transition. */ - public void setEarlyBackgroundTransition(boolean fadeEarly) { - if (mTransitionActivityOptions == null) { - return; - } - mWindow.setEarlyBackgroundTransition(fadeEarly); + public void onCaptureSharedElementEnd() { } /** diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 3f97c40..32cc30b 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -16,7 +16,6 @@ package android.app; -import android.animation.Animator; import android.content.Context; import android.graphics.Bitmap; import android.os.Bundle; @@ -24,12 +23,10 @@ import android.os.Handler; import android.os.IRemoteCallback; import android.os.RemoteException; import android.transition.Transition; -import android.util.ArrayMap; import android.util.Log; import android.view.View; import java.util.ArrayList; -import java.util.Map; /** * Helper class for building an options Bundle that can be used with @@ -100,12 +97,6 @@ public class ActivityOptions { public static final String KEY_ANIM_START_LISTENER = "android:animStartListener"; /** - * Arguments for the scene transition about to begin. - * @hide - */ - public static final String KEY_SCENE_TRANSITION_ARGS = "android:sceneTransitionArgs"; - - /** * For Activity transitions, the calling Activity's TransitionListener used to * notify the called Activity when the shared element and the exit transitions * complete. @@ -120,9 +111,10 @@ public class ActivityOptions { private static final String KEY_TRANSITION_TARGET_LISTENER = "android:transitionTargetListener"; /** - * The shared element's texture ID (TODO: not used yet). + * The names of shared elements that are transitioned to the started Activity. + * This is also the name of shared elements that the started Activity accepted. */ - private static final String KEY_SHARED_ELEMENT_TEXTURE_ID = "android:sharedElementTextureId"; + private static final String KEY_SHARED_ELEMENT_NAMES = "android:shared_element_names"; /** @hide */ public static final int ANIM_NONE = 0; @@ -146,9 +138,9 @@ public class ActivityOptions { private int mStartY; private int mStartWidth; private int mStartHeight; - private Bundle mTransitionArgs; private IRemoteCallback mAnimationStartedListener; private IRemoteCallback mTransitionCompleteListener; + private ArrayList<String> mSharedElementNames; /** * Create an ActivityOptions specifying a custom animation to run when @@ -226,7 +218,7 @@ public class ActivityOptions { /** @hide */ public interface ActivityTransitionTarget { - void sharedElementTransitionComplete(); + void sharedElementTransitionComplete(Bundle transitionArgs); void exitTransitionComplete(); } @@ -352,8 +344,6 @@ public class ActivityOptions { * When visual elements are to carry between Activities, args should be used to tell the called * Activity about the location and size. * - * TODO: Provide facility to capture layout and bitmap of shared elements. - * * <p>When * {@link android.app.Activity#startActivities(android.content.Intent[], android.os.Bundle)} * is used with the {@link #toBundle()} result, the Activity's content scene will automatically @@ -368,15 +358,16 @@ public class ActivityOptions { * enabled on the calling Activity to cause an exit transition. The same must be in * the called Activity to get an entering transition.</p> * - * @param args Contains information for transferring a view between this Activity and the - * target Activity. Will be used by the called Activity to transition the - * view to its eventual destination - * @see android.app.Activity#startSharedElementTransition(android.os.Bundle) + * @hide */ - public static ActivityOptions makeSceneTransitionAnimation(Bundle args) { + public static ActivityOptions makeSceneTransitionAnimation(Transition exitTransition, + ArrayList<String> sharedElementNames, Transition sharedElementTransition, + SharedElementSource sharedElementSource) { ActivityOptions opts = new ActivityOptions(); opts.mAnimationType = ANIM_SCENE_TRANSITION; - opts.mTransitionArgs = args; + opts.mTransitionCompleteListener = new ExitTransitionListener(exitTransition, + sharedElementTransition, sharedElementSource); + opts.mSharedElementNames = sharedElementNames; return opts; } @@ -412,9 +403,9 @@ public class ActivityOptions { break; case ANIM_SCENE_TRANSITION: - mTransitionArgs = opts.getBundle(KEY_SCENE_TRANSITION_ARGS); mTransitionCompleteListener = IRemoteCallback.Stub.asInterface( opts.getBinder(KEY_TRANSITION_COMPLETE_LISTENER)); + mSharedElementNames = opts.getStringArrayList(KEY_SHARED_ELEMENT_NAMES); break; } } @@ -465,17 +456,16 @@ public class ActivityOptions { } /** @hide */ - public Bundle getSceneTransitionArgs() { - return mTransitionArgs; - } - - /** @hide */ public IRemoteCallback getOnAnimationStartListener() { return mAnimationStartedListener; } /** @hide */ - public void dispatchSceneTransitionStarted(final ActivityTransitionTarget target) { + public ArrayList<String> getSharedElementNames() { return mSharedElementNames; } + + /** @hide */ + public void dispatchSceneTransitionStarted(final ActivityTransitionTarget target, + ArrayList<String> sharedElementNames) { boolean listenerSent = false; if (mTransitionCompleteListener != null) { IRemoteCallback callback = new IRemoteCallback.Stub() { @@ -484,13 +474,13 @@ public class ActivityOptions { if (data == null) { target.exitTransitionComplete(); } else { - // TODO: Use texture id - target.sharedElementTransitionComplete(); + target.sharedElementTransitionComplete(data); } } }; Bundle bundle = new Bundle(); bundle.putBinder(KEY_TRANSITION_TARGET_LISTENER, callback.asBinder()); + bundle.putStringArrayList(KEY_SHARED_ELEMENT_NAMES, sharedElementNames); try { mTransitionCompleteListener.sendResult(bundle); listenerSent = true; @@ -499,12 +489,23 @@ public class ActivityOptions { } } if (!listenerSent) { - target.sharedElementTransitionComplete(); + target.sharedElementTransitionComplete(null); target.exitTransitionComplete(); } } /** @hide */ + public void dispatchSharedElementsReady() { + if (mTransitionCompleteListener != null) { + try { + mTransitionCompleteListener.sendResult(null); + } catch (RemoteException e) { + Log.w(TAG, "Couldn't synchronize shared elements", e); + } + } + } + + /** @hide */ public void abort() { if (mAnimationStartedListener != null) { try { @@ -530,6 +531,7 @@ public class ActivityOptions { if (otherOptions.mPackageName != null) { mPackageName = otherOptions.mPackageName; } + mSharedElementNames = null; switch (otherOptions.mAnimationType) { case ANIM_CUSTOM: mAnimationType = otherOptions.mAnimationType; @@ -544,7 +546,6 @@ public class ActivityOptions { } mAnimationStartedListener = otherOptions.mAnimationStartedListener; mTransitionCompleteListener = null; - mTransitionArgs = null; break; case ANIM_SCALE_UP: mAnimationType = otherOptions.mAnimationType; @@ -560,7 +561,6 @@ public class ActivityOptions { } mAnimationStartedListener = null; mTransitionCompleteListener = null; - mTransitionArgs = null; break; case ANIM_THUMBNAIL_SCALE_UP: case ANIM_THUMBNAIL_SCALE_DOWN: @@ -576,14 +576,13 @@ public class ActivityOptions { } mAnimationStartedListener = otherOptions.mAnimationStartedListener; mTransitionCompleteListener = null; - mTransitionArgs = null; break; case ANIM_SCENE_TRANSITION: mAnimationType = otherOptions.mAnimationType; mTransitionCompleteListener = otherOptions.mTransitionCompleteListener; - mTransitionArgs = otherOptions.mTransitionArgs; mThumbnail = null; mAnimationStartedListener = null; + mSharedElementNames = otherOptions.mSharedElementNames; break; } } @@ -627,11 +626,11 @@ public class ActivityOptions { break; case ANIM_SCENE_TRANSITION: b.putInt(KEY_ANIM_TYPE, mAnimationType); - b.putBundle(KEY_SCENE_TRANSITION_ARGS, mTransitionArgs); if (mTransitionCompleteListener != null) { b.putBinder(KEY_TRANSITION_COMPLETE_LISTENER, mTransitionCompleteListener.asBinder()); } + b.putStringArrayList(KEY_SHARED_ELEMENT_NAMES, mSharedElementNames); break; } return b; @@ -653,31 +652,28 @@ public class ActivityOptions { /** @hide */ public interface SharedElementSource { - int getTextureId(); - } - - /** - * In the calling Activity when transitioning out, sets the Transition to listen for - * changes. - * @hide - */ - public void setExitTransition(Transition transition, SharedElementSource sharedElementSource) { - mTransitionCompleteListener = new ExitTransitionListener(transition, sharedElementSource); + Bundle getSharedElementExitState(); + void acceptedSharedElements(ArrayList<String> sharedElementNames); + void hideSharedElements(); } private static class ExitTransitionListener extends IRemoteCallback.Stub - implements Transition.TransitionListener, Animator.AnimatorListener { - private ArrayList<Animator> mSharedElementAnimators = new ArrayList<Animator>(); + implements Transition.TransitionListener { private boolean mSharedElementNotified; private Transition mExitTransition; + private Transition mSharedElementTransition; private IRemoteCallback mTransitionCompleteCallback; private boolean mExitComplete; + private boolean mSharedElementComplete; private SharedElementSource mSharedElementSource; - public ExitTransitionListener(Transition transition, SharedElementSource sharedElementSource) { + public ExitTransitionListener(Transition exitTransition, Transition sharedElementTransition, + SharedElementSource sharedElementSource) { mSharedElementSource = sharedElementSource; - mExitTransition = transition; + mExitTransition = exitTransition; mExitTransition.addListener(this); + mSharedElementTransition = sharedElementTransition; + mSharedElementTransition.addListener(this); } @Override @@ -685,36 +681,36 @@ public class ActivityOptions { if (data != null) { mTransitionCompleteCallback = IRemoteCallback.Stub.asInterface( data.getBinder(KEY_TRANSITION_TARGET_LISTENER)); + ArrayList<String> sharedElementNames + = data.getStringArrayList(KEY_SHARED_ELEMENT_NAMES); + mSharedElementSource.acceptedSharedElements(sharedElementNames); notifySharedElement(); notifyExit(); + } else { + mSharedElementSource.hideSharedElements(); } } @Override public void onTransitionStart(Transition transition) { - ArrayMap<Animator, Transition.AnimationInfo> runningAnimators - = Transition.getRunningAnimators(); - for (Map.Entry<Animator, Transition.AnimationInfo> entry : runningAnimators.entrySet()) { - if (entry.getValue().view.getSharedElementName() != null) { - mSharedElementAnimators.add(entry.getKey()); - entry.getKey().addListener(this); - } - } - notifySharedElement(); } @Override public void onTransitionEnd(Transition transition) { - mExitComplete = true; - notifyExit(); - mExitTransition.removeListener(this); + if (transition == mExitTransition) { + mExitComplete = true; + notifyExit(); + mExitTransition.removeListener(this); + } else { + mSharedElementComplete = true; + notifySharedElement(); + mSharedElementTransition.removeListener(this); + } } @Override public void onTransitionCancel(Transition transition) { - mExitComplete = true; - notifyExit(); - mExitTransition.removeListener(this); + onTransitionEnd(transition); } @Override @@ -725,34 +721,13 @@ public class ActivityOptions { public void onTransitionResume(Transition transition) { } - @Override - public void onAnimationStart(Animator animation) { - } - - @Override - public void onAnimationEnd(Animator animation) { - mSharedElementAnimators.remove(animation); - notifySharedElement(); - } - - @Override - public void onAnimationCancel(Animator animation) { - mSharedElementAnimators.remove(animation); - notifySharedElement(); - } - - @Override - public void onAnimationRepeat(Animator animation) { - } - private void notifySharedElement() { - if (!mSharedElementNotified && mSharedElementAnimators.isEmpty() + if (!mSharedElementNotified && mSharedElementComplete && mTransitionCompleteCallback != null) { mSharedElementNotified = true; try { - Bundle bundle = new Bundle(); - bundle.putInt(KEY_SHARED_ELEMENT_TEXTURE_ID, mSharedElementSource.getTextureId()); - mTransitionCompleteCallback.sendResult(bundle); + Bundle sharedElementState = mSharedElementSource.getSharedElementExitState(); + mTransitionCompleteCallback.sendResult(sharedElementState); } catch (RemoteException e) { Log.w(TAG, "Couldn't notify that the transition ended", e); } diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java index fd3f9b3..9f1e72d 100644 --- a/core/java/android/transition/Transition.java +++ b/core/java/android/transition/Transition.java @@ -552,8 +552,7 @@ public abstract class Transition implements Cloneable { return false; } - /** @hide */ - public static ArrayMap<Animator, AnimationInfo> getRunningAnimators() { + private static ArrayMap<Animator, AnimationInfo> getRunningAnimators() { ArrayMap<Animator, AnimationInfo> runningAnimators = sRunningAnimators.get(); if (runningAnimators == null) { runningAnimators = new ArrayMap<Animator, AnimationInfo>(); @@ -1113,30 +1112,32 @@ public abstract class Transition implements Cloneable { } } } - TransitionValues values = new TransitionValues(); - values.view = view; - if (start) { - captureStartValues(values); - } else { - captureEndValues(values); - } - if (start) { - if (!isListViewItem) { - mStartValues.viewValues.put(view, values); - if (id >= 0) { - mStartValues.idValues.put((int) id, values); - } + if (view.getParent() instanceof ViewGroup) { + TransitionValues values = new TransitionValues(); + values.view = view; + if (start) { + captureStartValues(values); } else { - mStartValues.itemIdValues.put(itemId, values); + captureEndValues(values); } - } else { - if (!isListViewItem) { - mEndValues.viewValues.put(view, values); - if (id >= 0) { - mEndValues.idValues.put((int) id, values); + if (start) { + if (!isListViewItem) { + mStartValues.viewValues.put(view, values); + if (id >= 0) { + mStartValues.idValues.put((int) id, values); + } + } else { + mStartValues.itemIdValues.put(itemId, values); } } else { - mEndValues.itemIdValues.put(itemId, values); + if (!isListViewItem) { + mEndValues.viewValues.put(view, values); + if (id >= 0) { + mEndValues.idValues.put((int) id, values); + } + } else { + mEndValues.itemIdValues.put(itemId, values); + } } } if (view instanceof ViewGroup) { diff --git a/core/java/android/transition/TransitionInflater.java b/core/java/android/transition/TransitionInflater.java index 9fa554c..912f2ed 100644 --- a/core/java/android/transition/TransitionInflater.java +++ b/core/java/android/transition/TransitionInflater.java @@ -285,46 +285,27 @@ public class TransitionInflater { com.android.internal.R.styleable.TransitionManager); int transitionId = a.getResourceId( com.android.internal.R.styleable.TransitionManager_transition, -1); - Scene fromScene = null, toScene = null; int fromId = a.getResourceId( com.android.internal.R.styleable.TransitionManager_fromScene, -1); - if (fromId >= 0) fromScene = Scene.getSceneForLayout(sceneRoot, fromId, mContext); + Scene fromScene = (fromId < 0) ? null: Scene.getSceneForLayout(sceneRoot, fromId, mContext); int toId = a.getResourceId( com.android.internal.R.styleable.TransitionManager_toScene, -1); - if (toId >= 0) toScene = Scene.getSceneForLayout(sceneRoot, toId, mContext); - String fromName = a.getString( - com.android.internal.R.styleable.TransitionManager_fromSceneName); - String toName = a.getString( - com.android.internal.R.styleable.TransitionManager_toSceneName); + Scene toScene = (toId < 0) ? null : Scene.getSceneForLayout(sceneRoot, toId, mContext); + if (transitionId >= 0) { Transition transition = inflateTransition(transitionId); if (transition != null) { - if (fromScene != null) { - boolean hasDest = false; - if (toScene != null) { - transitionManager.setTransition(fromScene, toScene, transition); - hasDest = true; - } - - if (!TextUtils.isEmpty(toName)) { - transitionManager.setTransition(fromScene, toName, transition); - hasDest = true; - } - - if (!hasDest) { - throw new RuntimeException("No matching toScene or toSceneName for given " + - "fromScene for transition ID " + transitionId); - } - } else if (toId >= 0) { - transitionManager.setTransition(toScene, transition); - } - if (fromName != null) { - if (toScene != null) { - transitionManager.setTransition(fromName, toScene, transition); - } else { - throw new RuntimeException("No matching toScene for given fromSceneName " + + if (fromScene == null) { + if (toScene == null) { + throw new RuntimeException("No matching fromScene or toScene " + "for transition ID " + transitionId); + } else { + transitionManager.setTransition(toScene, transition); } + } else if (toScene == null) { + transitionManager.setExitTransition(fromScene, transition); + } else { + transitionManager.setTransition(fromScene, toScene, transition); } } } diff --git a/core/java/android/transition/TransitionManager.java b/core/java/android/transition/TransitionManager.java index 0106f7f..f3abfb0 100644 --- a/core/java/android/transition/TransitionManager.java +++ b/core/java/android/transition/TransitionManager.java @@ -70,12 +70,9 @@ public class TransitionManager { private static final String[] EMPTY_STRINGS = new String[0]; ArrayMap<Scene, Transition> mSceneTransitions = new ArrayMap<Scene, Transition>(); + ArrayMap<Scene, Transition> mExitSceneTransitions = new ArrayMap<Scene, Transition>(); ArrayMap<Scene, ArrayMap<Scene, Transition>> mScenePairTransitions = new ArrayMap<Scene, ArrayMap<Scene, Transition>>(); - ArrayMap<Scene, ArrayMap<String, Transition>> mSceneNameTransitions = - new ArrayMap<Scene, ArrayMap<String, Transition>>(); - ArrayMap<String, ArrayMap<Scene, Transition>> mNameSceneTransitions = - new ArrayMap<String, ArrayMap<Scene, Transition>>(); private static ThreadLocal<WeakReference<ArrayMap<ViewGroup, ArrayList<Transition>>>> sRunningTransitions = new ThreadLocal<WeakReference<ArrayMap<ViewGroup, ArrayList<Transition>>>>(); @@ -122,6 +119,21 @@ public class TransitionManager { } /** + * Sets a specific transition to occur when the given scene is exited. This + * has the lowest priority -- if a Scene-to-Scene transition or + * Scene enter transition can be applied, it will. + * + * @param scene The scene which, when exited, will cause the given + * transition to run. + * @param transition The transition that will play when the given scene is + * exited. A value of null will result in the default behavior of + * using the default transition instead. + */ + public void setExitTransition(Scene scene, Transition transition) { + mExitSceneTransitions.put(scene, transition); + } + + /** * Sets a specific transition to occur when the given pair of scenes is * exited/entered. * @@ -169,6 +181,9 @@ public class TransitionManager { } } transition = mSceneTransitions.get(scene); + if (transition == null && sceneRoot != null) { + transition = mExitSceneTransitions.get(Scene.getCurrentScene(sceneRoot)); + } return (transition != null) ? transition : sDefaultTransition; } @@ -224,138 +239,31 @@ public class TransitionManager { } /** - * Retrieve the transition from a named scene to a target defined scene if one has been + * Retrieve the transition to a target defined scene if one has been * associated with this TransitionManager. * - * <p>A named scene is an indirect link for a transition. Fundamentally a named - * scene represents a potentially arbitrary intersection point of two otherwise independent - * transitions. Activity A may define a transition from scene X to "com.example.scene.FOO" - * while activity B may define a transition from scene "com.example.scene.FOO" to scene Y. - * In this way applications may define an API for more sophisticated transitions between - * caller and called activities very similar to the way that <code>Intent</code> extras - * define APIs for arguments and data propagation between activities.</p> - * - * @param fromName Named scene that this transition corresponds to * @param toScene Target scene that this transition will move to - * @return Transition corresponding to the given fromName and toScene or null + * @return Transition corresponding to the given toScene or null * if no association exists in this TransitionManager * - * @see #setTransition(String, Scene, Transition) + * @see #setTransition(Scene, Transition) + * @hide */ - public Transition getNamedTransition(String fromName, Scene toScene) { - ArrayMap<Scene, Transition> m = mNameSceneTransitions.get(fromName); - if (m != null) { - return m.get(toScene); - } - return null; + public Transition getEnterTransition(Scene toScene) { + return mSceneTransitions.get(toScene); } /** * Retrieve the transition from a defined scene to a target named scene if one has been * associated with this TransitionManager. * - * <p>A named scene is an indirect link for a transition. Fundamentally a named - * scene represents a potentially arbitrary intersection point of two otherwise independent - * transitions. Activity A may define a transition from scene X to "com.example.scene.FOO" - * while activity B may define a transition from scene "com.example.scene.FOO" to scene Y. - * In this way applications may define an API for more sophisticated transitions between - * caller and called activities very similar to the way that <code>Intent</code> extras - * define APIs for arguments and data propagation between activities.</p> - * * @param fromScene Scene that this transition starts from - * @param toName Name of the target scene - * @return Transition corresponding to the given fromScene and toName or null + * @return Transition corresponding to the given fromScene or null * if no association exists in this TransitionManager + * @hide */ - public Transition getNamedTransition(Scene fromScene, String toName) { - ArrayMap<String, Transition> m = mSceneNameTransitions.get(fromScene); - if (m != null) { - return m.get(toName); - } - return null; - } - - /** - * Retrieve the supported target named scenes when transitioning away from the given scene. - * - * <p>A named scene is an indirect link for a transition. Fundamentally a named - * scene represents a potentially arbitrary intersection point of two otherwise independent - * transitions. Activity A may define a transition from scene X to "com.example.scene.FOO" - * while activity B may define a transition from scene "com.example.scene.FOO" to scene Y. - * In this way applications may define an API for more sophisticated transitions between - * caller and called activities very similar to the way that <code>Intent</code> extras - * define APIs for arguments and data propagation between activities.</p> - * - * @param fromScene Scene to transition from - * @return An array of Strings naming each supported transition starting from - * <code>fromScene</code>. If no transitions to a named scene from the given - * scene are supported this function will return a String[] of length 0. - * - * @see #setTransition(Scene, String, Transition) - */ - public String[] getTargetSceneNames(Scene fromScene) { - final ArrayMap<String, Transition> m = mSceneNameTransitions.get(fromScene); - if (m == null) { - return EMPTY_STRINGS; - } - final int count = m.size(); - final String[] result = new String[count]; - for (int i = 0; i < count; i++) { - result[i] = m.keyAt(i); - } - return result; - } - - /** - * Set a transition from a specific scene to a named scene. - * - * <p>A named scene is an indirect link for a transition. Fundamentally a named - * scene represents a potentially arbitrary intersection point of two otherwise independent - * transitions. Activity A may define a transition from scene X to "com.example.scene.FOO" - * while activity B may define a transition from scene "com.example.scene.FOO" to scene Y. - * In this way applications may define an API for more sophisticated transitions between - * caller and called activities very similar to the way that <code>Intent</code> extras - * define APIs for arguments and data propagation between activities.</p> - * - * @param fromScene Scene to transition from - * @param toName Named scene to transition to - * @param transition Transition to use - * - * @see #getTargetSceneNames(Scene) - */ - public void setTransition(Scene fromScene, String toName, Transition transition) { - ArrayMap<String, Transition> m = mSceneNameTransitions.get(fromScene); - if (m == null) { - m = new ArrayMap<String, Transition>(); - mSceneNameTransitions.put(fromScene, m); - } - m.put(toName, transition); - } - - /** - * Set a transition from a named scene to a concrete scene. - * - * <p>A named scene is an indirect link for a transition. Fundamentally a named - * scene represents a potentially arbitrary intersection point of two otherwise independent - * transitions. Activity A may define a transition from scene X to "com.example.scene.FOO" - * while activity B may define a transition from scene "com.example.scene.FOO" to scene Y. - * In this way applications may define an API for more sophisticated transitions between - * caller and called activities very similar to the way that <code>Intent</code> extras - * define APIs for arguments and data propagation between activities.</p> - * - * @param fromName Named scene to transition from - * @param toScene Scene to transition to - * @param transition Transition to use - * - * @see #getNamedTransition(String, Scene) - */ - public void setTransition(String fromName, Scene toScene, Transition transition) { - ArrayMap<Scene, Transition> m = mNameSceneTransitions.get(fromName); - if (m == null) { - m = new ArrayMap<Scene, Transition>(); - mNameSceneTransitions.put(fromName, m); - } - m.put(toScene, transition); + public Transition getExitTransition(Scene fromScene) { + return mExitSceneTransitions.get(fromScene); } /** diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 0665264..effc9a6 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -101,7 +101,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicInteger; @@ -18809,6 +18811,33 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } + /** + * Gets the Views in the hierarchy affected by entering and exiting Activity Scene transitions. + * @param transitioningViews This View will be added to transitioningViews if it is VISIBLE and + * a normal View or a ViewGroup with + * {@link android.view.ViewGroup#isTransitionGroup()} true. + * @hide + */ + public void captureTransitioningViews(List<View> transitioningViews) { + if (getVisibility() == View.VISIBLE) { + transitioningViews.add(this); + } + } + + /** + * Adds all Views that have {@link #getSharedElementName()} non-null to sharedElements. + * @param sharedElements Will contain all Views in the hierarchy having a shared element name. + * @hide + */ + public void findSharedElements(Map<String, View> sharedElements) { + if (getVisibility() == VISIBLE) { + String sharedElementName = getSharedElementName(); + if (sharedElementName != null) { + sharedElements.put(sharedElementName, this); + } + } + } + // // Properties // diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 9cd3c9d..cf5e8cf 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -31,6 +31,7 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; import android.os.Build; +import android.os.Bundle; import android.os.Parcelable; import android.os.SystemClock; import android.util.AttributeSet; @@ -50,6 +51,8 @@ import com.android.internal.util.Predicate; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; +import java.util.List; +import java.util.Map; import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; @@ -2300,14 +2303,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * individually during the transition. * @return True if the ViewGroup should be acted on together during an Activity transition. * The default value is false when the background is null and true when the background - * is not null. - * @see android.app.ActivityOptions#makeSceneTransitionAnimation(android.os.Bundle) + * is not null or if {@link #getSharedElementName()} is not null. */ public boolean isTransitionGroup() { if ((mGroupFlags & FLAG_IS_TRANSITION_GROUP_SET) != 0) { return ((mGroupFlags & FLAG_IS_TRANSITION_GROUP) != 0); } else { - return getBackground() != null; + return getBackground() != null || getSharedElementName() != null; } } @@ -2318,7 +2320,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * in Activity transitions. If false, the ViewGroup won't transition, * only its children. If true, the entire ViewGroup will transition * together. - * @see android.app.ActivityOptions#makeSceneTransitionAnimation(android.os.Bundle) */ public void setTransitionGroup(boolean isTransitionGroup) { mGroupFlags |= FLAG_IS_TRANSITION_GROUP_SET; @@ -5880,6 +5881,37 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager protected void onSetLayoutParams(View child, LayoutParams layoutParams) { } + /** @hide */ + @Override + public void captureTransitioningViews(List<View> transitioningViews) { + if (getVisibility() != View.VISIBLE) { + return; + } + if (isTransitionGroup()) { + transitioningViews.add(this); + } else { + int count = getChildCount(); + for (int i = 0; i < count; i++) { + View child = getChildAt(i); + child.captureTransitioningViews(transitioningViews); + } + } + } + + /** @hide */ + @Override + public void findSharedElements(Map<String, View> sharedElements) { + if (getVisibility() != VISIBLE) { + return; + } + super.findSharedElements(sharedElements); + int count = getChildCount(); + for (int i = 0; i < count; i++) { + View child = getChildAt(i); + child.findSharedElements(sharedElements); + } + } + /** * LayoutParams are used by views to tell their parents how they want to be * laid out. See diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 11740ab..4943a40 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -29,9 +29,12 @@ import android.os.Bundle; import android.os.IBinder; import android.os.SystemProperties; import android.transition.Scene; +import android.transition.Transition; import android.transition.TransitionManager; import android.view.accessibility.AccessibilityEvent; +import java.util.Map; + /** * Abstract base class for a top-level window look and behavior policy. An * instance of this class should be used as the top-level view added to the @@ -1386,30 +1389,43 @@ public abstract class Window { * @hide */ public interface SceneTransitionListener { - void enterSharedElement(Bundle transitionArgs); void nullPendingTransition(); void convertFromTranslucent(); void convertToTranslucent(); + void sharedElementStart(Transition transition); + void sharedElementEnd(); } /** - * Controls how the background fade is triggered. If fadeEarly is true, the Window background - * will fade in as soon as the shared elements are ready to switch. If fadeEarly is false, - * the background will fade only after the calling Activity's exit transition completes. - * By default, the Window will fade in when the calling Activity's exit transition completes. + * Controls when the Activity enter scene is triggered and the background is faded in. If + * triggerEarly is true, the enter scene will begin as soon as possible and the background + * will fade in when all shared elements are ready to begin transitioning. If triggerEarly is + * false, the Activity enter scene and background fade will be triggered when the calling + * Activity's exit transition completes. * - * @param fadeEarly Set to true to fade out the exiting Activity as soon as the shared elements - * are transferred. Set to false to fade out the exiting Activity as soon as - * the shared element is transferred. - * @hide + * @param triggerEarly Set to true to have the Activity enter scene transition in as early as + * possible or set to false to wait for the calling Activity to exit first. */ - public void setEarlyBackgroundTransition(boolean fadeEarly) { + public void setTriggerEarlyEnterTransition(boolean triggerEarly) { } /** * Start the exit transition. * @hide */ - public void startExitTransition(ActivityOptions activityOptions) { + public Bundle startExitTransition(Map<String, View> sharedElements) { + return null; + } + + /** + * On entering Activity Scene transitions, shared element names may be mapped from a + * source Activity's specified name to a unique shared element name in the View hierarchy. + * Under most circumstances, mapping is not necessary - a single View will have the + * shared element name given by the calling Activity. However, if there are several similar + * Views (e.g. in a ListView), the correct shared element must be mapped. + * @param sharedElementNames A mapping from the calling Activity's assigned shared element + * name to a unique shared element name in the View hierarchy. + */ + public void mapTransitionTargets(Map<String, String> sharedElementNames) { } } diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java index 0a80495..cc51a8b 100644 --- a/core/java/com/android/internal/app/ActionBarImpl.java +++ b/core/java/com/android/internal/app/ActionBarImpl.java @@ -57,6 +57,7 @@ import android.widget.SpinnerAdapter; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Map; /** * ActionBarImpl is the ActionBar implementation used @@ -355,6 +356,10 @@ public class ActionBarImpl extends ActionBar { setSubtitle(mContext.getString(resId)); } + public void captureSharedElements(Map<String, View> sharedElements) { + mContainerView.findSharedElements(sharedElements); + } + public void setSelectedNavigationItem(int position) { switch (mActionView.getNavigationMode()) { case NAVIGATION_MODE_TABS: diff --git a/core/res/res/layout-xlarge/screen_action_bar.xml b/core/res/res/layout-xlarge/screen_action_bar.xml index e495e53..d2fe9fa 100644 --- a/core/res/res/layout-xlarge/screen_action_bar.xml +++ b/core/res/res/layout-xlarge/screen_action_bar.xml @@ -34,6 +34,7 @@ the Action Bar enabled overlaying application content. android:layout_height="wrap_content" android:layout_alignParentTop="true" style="?android:attr/actionBarStyle" + android:sharedElementName="android:action_bar" android:gravity="top"> <com.android.internal.widget.ActionBarView android:id="@+id/action_bar" diff --git a/core/res/res/layout/screen_action_bar.xml b/core/res/res/layout/screen_action_bar.xml index b1889a2..7b9a20b 100644 --- a/core/res/res/layout/screen_action_bar.xml +++ b/core/res/res/layout/screen_action_bar.xml @@ -33,6 +33,7 @@ This is an optimized layout for a screen with the Action Bar enabled. android:layout_height="wrap_content" android:layout_alignParentTop="true" style="?android:attr/actionBarStyle" + android:sharedElementName="android:action_bar" android:gravity="top"> <com.android.internal.widget.ActionBarView android:id="@+id/action_bar" diff --git a/core/res/res/layout/screen_custom_title.xml b/core/res/res/layout/screen_custom_title.xml index e3364d1..d02cc8b 100644 --- a/core/res/res/layout/screen_custom_title.xml +++ b/core/res/res/layout/screen_custom_title.xml @@ -31,6 +31,7 @@ This is a custom layout for a screen. <FrameLayout android:id="@android:id/title_container" android:layout_width="match_parent" android:layout_height="?android:attr/windowTitleSize" + android:sharedElementName="android:title" style="?android:attr/windowTitleBackgroundStyle"> </FrameLayout> <FrameLayout android:id="@android:id/content" diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index bfd7565..1c5be42 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -4760,14 +4760,6 @@ <attr name="fromScene" format="reference" /> <!-- The destination scene in this scene change. --> <attr name="toScene" format="reference" /> - <!-- The name of the originating scene in this scene change. - Apps should treat this name as an API in the same sense - that an Intent action or extra key is. --> - <attr name="fromSceneName" format="string" /> - <!-- The name of the destination scene in this scene change. - Apps should treat this name as an API in the same sense - that an Intent action or extra key is. --> - <attr name="toSceneName" format="string" /> </declare-styleable> <!-- ========================== --> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 44ad5ee..66858fa 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2103,8 +2103,6 @@ <public type="attr" name="controlY1" /> <public type="attr" name="controlX2" /> <public type="attr" name="controlY2" /> - <public type="attr" name="fromSceneName" /> - <public type="attr" name="toSceneName" /> <public type="attr" name="sharedElementName" /> <public type="attr" name="transitionGroup" /> <public type="attr" name="castsShadow" /> |