diff options
| -rw-r--r-- | api/current.txt | 26 | ||||
| -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 | ||||
| -rw-r--r-- | policy/src/com/android/internal/policy/impl/PhoneWindow.java | 345 |
16 files changed, 446 insertions, 591 deletions
diff --git a/api/current.txt b/api/current.txt index 53655fa5e85d..f03023a3cb0b 100644 --- a/api/current.txt +++ b/api/current.txt @@ -345,7 +345,7 @@ package android { field public static final int canRetrieveWindowContent = 16843653; // 0x1010385 field public static final int candidatesTextStyleSpans = 16843312; // 0x1010230 field public static final deprecated int capitalize = 16843113; // 0x1010169 - field public static final int castsShadow = 16843775; // 0x10103ff + field public static final int castsShadow = 16843777; // 0x1010401 field public static final int category = 16843752; // 0x10103e8 field public static final int centerBright = 16842956; // 0x10100cc field public static final int centerColor = 16843275; // 0x101020b @@ -542,6 +542,7 @@ package android { field public static final int fromAlpha = 16843210; // 0x10101ca field public static final int fromDegrees = 16843187; // 0x10101b3 field public static final int fromScene = 16843741; // 0x10103dd + field public static final int fromSceneName = 16843773; // 0x10103fd field public static final int fromXDelta = 16843206; // 0x10101c6 field public static final int fromXScale = 16843202; // 0x10101c2 field public static final int fromYDelta = 16843208; // 0x10101c8 @@ -889,7 +890,7 @@ package android { field public static final int required = 16843406; // 0x101028e field public static final int requiredAccountType = 16843734; // 0x10103d6 field public static final int requiredForAllUsers = 16843728; // 0x10103d0 - field public static final int requiredForProfile = 16843776; // 0x1010400 + field public static final int requiredForProfile = 16843778; // 0x1010402 field public static final int requiresFadingEdge = 16843685; // 0x10103a5 field public static final int requiresSmallestWidthDp = 16843620; // 0x1010364 field public static final int resizeMode = 16843619; // 0x1010363 @@ -960,7 +961,7 @@ package android { field public static final int shadowRadius = 16843108; // 0x1010164 field public static final int shape = 16843162; // 0x101019a field public static final int shareInterpolator = 16843195; // 0x10101bb - field public static final int sharedElementName = 16843773; // 0x10103fd + field public static final int sharedElementName = 16843775; // 0x10103ff field public static final int sharedUserId = 16842763; // 0x101000b field public static final int sharedUserLabel = 16843361; // 0x1010261 field public static final int shouldDisableView = 16843246; // 0x10101ee @@ -1144,6 +1145,7 @@ package android { field public static final int toAlpha = 16843211; // 0x10101cb field public static final int toDegrees = 16843188; // 0x10101b4 field public static final int toScene = 16843742; // 0x10103de + field public static final int toSceneName = 16843774; // 0x10103fe field public static final int toXDelta = 16843207; // 0x10101c7 field public static final int toXScale = 16843203; // 0x10101c3 field public static final int toYDelta = 16843209; // 0x10101c9 @@ -1159,7 +1161,7 @@ package android { field public static final int transformPivotX = 16843552; // 0x1010320 field public static final int transformPivotY = 16843553; // 0x1010321 field public static final int transition = 16843743; // 0x10103df - field public static final int transitionGroup = 16843774; // 0x10103fe + field public static final int transitionGroup = 16843776; // 0x1010400 field public static final int transitionOrdering = 16843744; // 0x10103e0 field public static final int translationX = 16843554; // 0x1010322 field public static final int translationY = 16843555; // 0x1010323 @@ -3039,6 +3041,7 @@ package android.app { method public int getTaskId(); method public final java.lang.CharSequence getTitle(); method public final int getTitleColor(); + method public android.os.Bundle getTransitionArgs(); method public final int getVolumeControlStream(); method public android.view.Window getWindow(); method public android.view.WindowManager getWindowManager(); @@ -3060,8 +3063,6 @@ package android.app { method public void onAttachFragment(android.app.Fragment); method public void onAttachedToWindow(); method public void onBackPressed(); - method public void onCaptureSharedElementEnd(); - method public void onCaptureSharedElementStart(android.transition.Transition); method protected void onChildTitleChanged(android.app.Activity, java.lang.CharSequence); method public void onConfigurationChanged(android.content.res.Configuration); method public void onContentChanged(); @@ -3136,6 +3137,7 @@ package android.app { method public void setContentView(android.view.View); method public void setContentView(android.view.View, android.view.ViewGroup.LayoutParams); method public final void setDefaultKeyMode(int); + method public void setEarlyBackgroundTransition(boolean); method public final void setFeatureDrawable(int, android.graphics.drawable.Drawable); method public final void setFeatureDrawableAlpha(int, int); method public final void setFeatureDrawableResource(int, int); @@ -3160,9 +3162,7 @@ package android.app { method public final deprecated void showDialog(int); method public final deprecated boolean showDialog(int, android.os.Bundle); method public android.view.ActionMode startActionMode(android.view.ActionMode.Callback); - method public void startActivity(android.content.Intent, android.util.Pair<android.view.View, java.lang.String>...); method public void startActivityForResult(android.content.Intent, int); - method public void startActivityForResult(android.content.Intent, int, android.util.Pair<android.view.View, java.lang.String>...); method public void startActivityForResult(android.content.Intent, int, android.os.Bundle); method public void startActivityFromChild(android.app.Activity, android.content.Intent, int); method public void startActivityFromChild(android.app.Activity, android.content.Intent, int, android.os.Bundle); @@ -3178,6 +3178,7 @@ package android.app { method public boolean startNextMatchingActivity(android.content.Intent); method public boolean startNextMatchingActivity(android.content.Intent, android.os.Bundle); method public void startSearch(java.lang.String, boolean, android.os.Bundle, boolean); + method protected void startSharedElementTransition(android.os.Bundle); method public deprecated void stopManagingCursor(android.database.Cursor); method public void takeKeyEvents(boolean); method public void triggerSearch(java.lang.String, android.os.Bundle); @@ -3348,6 +3349,7 @@ package android.app { public class ActivityOptions { method public static android.app.ActivityOptions makeCustomAnimation(android.content.Context, int, int); method public static android.app.ActivityOptions makeScaleUpAnimation(android.view.View, int, int, int, int); + method public static android.app.ActivityOptions makeSceneTransitionAnimation(android.os.Bundle); method public static android.app.ActivityOptions makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int); method public android.os.Bundle toBundle(); method public void update(android.app.ActivityOptions); @@ -26818,11 +26820,15 @@ package android.transition { ctor public TransitionManager(); method public static void beginDelayedTransition(android.view.ViewGroup); method public static void beginDelayedTransition(android.view.ViewGroup, android.transition.Transition); + method public android.transition.Transition getNamedTransition(java.lang.String, android.transition.Scene); + method public android.transition.Transition getNamedTransition(android.transition.Scene, java.lang.String); + method public java.lang.String[] getTargetSceneNames(android.transition.Scene); method public static void go(android.transition.Scene); method public static void go(android.transition.Scene, android.transition.Transition); - method public void setExitTransition(android.transition.Scene, android.transition.Transition); method public void setTransition(android.transition.Scene, android.transition.Transition); method public void setTransition(android.transition.Scene, android.transition.Scene, android.transition.Transition); + method public void setTransition(android.transition.Scene, java.lang.String, android.transition.Transition); + method public void setTransition(java.lang.String, android.transition.Scene, android.transition.Transition); method public void transitionTo(android.transition.Scene); } @@ -29793,7 +29799,6 @@ package android.view { method public abstract boolean isFloating(); method public abstract boolean isShortcutKey(int, android.view.KeyEvent); method public final void makeActive(); - method public void mapTransitionTargets(java.util.Map<java.lang.String, java.lang.String>); method protected abstract void onActive(); method public abstract void onConfigurationChanged(android.content.res.Configuration); method public abstract void openPanel(int, android.view.KeyEvent); @@ -29832,7 +29837,6 @@ package android.view { method public abstract void setTitle(java.lang.CharSequence); method public abstract deprecated void setTitleColor(int); method public void setTransitionManager(android.transition.TransitionManager); - method public void setTriggerEarlyEnterTransition(boolean); method public void setType(int); method public void setUiOptions(int); method public void setUiOptions(int, int); diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index c79a0a59b3cc..9a3c2908d0eb 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -18,10 +18,8 @@ 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; @@ -95,7 +93,6 @@ 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 @@ -3449,53 +3446,7 @@ public class Activity extends ContextThemeWrapper * @see #startActivity */ public void startActivityForResult(Intent intent, int requestCode) { - 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); + startActivityForResult(intent, requestCode, null); } /** @@ -3533,6 +3484,14 @@ 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( @@ -3705,25 +3664,7 @@ public class Activity extends ContextThemeWrapper */ @Override public void startActivity(Intent intent) { - 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); + startActivity(intent, null); } /** @@ -4779,8 +4720,7 @@ 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); } /** @@ -5390,6 +5330,12 @@ 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); } @@ -5403,16 +5349,6 @@ 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(); - } }; } @@ -5606,23 +5542,53 @@ public class Activity extends ContextThemeWrapper } /** - * 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. + * 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> * - * @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. + * @return The Bundle passed into Bundle parameter of + * {@link android.app.ActivityOptions#makeSceneTransitionAnimation(android.os.Bundle)} + * in the calling Activity. */ - public void onCaptureSharedElementStart(Transition transition) { + 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) { } /** - * 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. + * 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) */ - public void onCaptureSharedElementEnd() { + public void setEarlyBackgroundTransition(boolean fadeEarly) { + if (mTransitionActivityOptions == null) { + return; + } + mWindow.setEarlyBackgroundTransition(fadeEarly); } /** diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 32cc30be9fc3..3f97c40149fe 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -16,6 +16,7 @@ package android.app; +import android.animation.Animator; import android.content.Context; import android.graphics.Bitmap; import android.os.Bundle; @@ -23,10 +24,12 @@ 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 @@ -97,6 +100,12 @@ 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. @@ -111,10 +120,9 @@ public class ActivityOptions { private static final String KEY_TRANSITION_TARGET_LISTENER = "android:transitionTargetListener"; /** - * 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. + * The shared element's texture ID (TODO: not used yet). */ - private static final String KEY_SHARED_ELEMENT_NAMES = "android:shared_element_names"; + private static final String KEY_SHARED_ELEMENT_TEXTURE_ID = "android:sharedElementTextureId"; /** @hide */ public static final int ANIM_NONE = 0; @@ -138,9 +146,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 @@ -218,7 +226,7 @@ public class ActivityOptions { /** @hide */ public interface ActivityTransitionTarget { - void sharedElementTransitionComplete(Bundle transitionArgs); + void sharedElementTransitionComplete(); void exitTransitionComplete(); } @@ -344,6 +352,8 @@ 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 @@ -358,16 +368,15 @@ 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> * - * @hide + * @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) */ - public static ActivityOptions makeSceneTransitionAnimation(Transition exitTransition, - ArrayList<String> sharedElementNames, Transition sharedElementTransition, - SharedElementSource sharedElementSource) { + public static ActivityOptions makeSceneTransitionAnimation(Bundle args) { ActivityOptions opts = new ActivityOptions(); opts.mAnimationType = ANIM_SCENE_TRANSITION; - opts.mTransitionCompleteListener = new ExitTransitionListener(exitTransition, - sharedElementTransition, sharedElementSource); - opts.mSharedElementNames = sharedElementNames; + opts.mTransitionArgs = args; return opts; } @@ -403,9 +412,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; } } @@ -456,16 +465,17 @@ public class ActivityOptions { } /** @hide */ - public IRemoteCallback getOnAnimationStartListener() { - return mAnimationStartedListener; + public Bundle getSceneTransitionArgs() { + return mTransitionArgs; } /** @hide */ - public ArrayList<String> getSharedElementNames() { return mSharedElementNames; } + public IRemoteCallback getOnAnimationStartListener() { + return mAnimationStartedListener; + } /** @hide */ - public void dispatchSceneTransitionStarted(final ActivityTransitionTarget target, - ArrayList<String> sharedElementNames) { + public void dispatchSceneTransitionStarted(final ActivityTransitionTarget target) { boolean listenerSent = false; if (mTransitionCompleteListener != null) { IRemoteCallback callback = new IRemoteCallback.Stub() { @@ -474,13 +484,13 @@ public class ActivityOptions { if (data == null) { target.exitTransitionComplete(); } else { - target.sharedElementTransitionComplete(data); + // TODO: Use texture id + target.sharedElementTransitionComplete(); } } }; 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; @@ -489,23 +499,12 @@ public class ActivityOptions { } } if (!listenerSent) { - target.sharedElementTransitionComplete(null); + target.sharedElementTransitionComplete(); 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 { @@ -531,7 +530,6 @@ public class ActivityOptions { if (otherOptions.mPackageName != null) { mPackageName = otherOptions.mPackageName; } - mSharedElementNames = null; switch (otherOptions.mAnimationType) { case ANIM_CUSTOM: mAnimationType = otherOptions.mAnimationType; @@ -546,6 +544,7 @@ public class ActivityOptions { } mAnimationStartedListener = otherOptions.mAnimationStartedListener; mTransitionCompleteListener = null; + mTransitionArgs = null; break; case ANIM_SCALE_UP: mAnimationType = otherOptions.mAnimationType; @@ -561,6 +560,7 @@ public class ActivityOptions { } mAnimationStartedListener = null; mTransitionCompleteListener = null; + mTransitionArgs = null; break; case ANIM_THUMBNAIL_SCALE_UP: case ANIM_THUMBNAIL_SCALE_DOWN: @@ -576,13 +576,14 @@ 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; } } @@ -626,11 +627,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; @@ -652,28 +653,31 @@ public class ActivityOptions { /** @hide */ public interface SharedElementSource { - Bundle getSharedElementExitState(); - void acceptedSharedElements(ArrayList<String> sharedElementNames); - void hideSharedElements(); + 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); } private static class ExitTransitionListener extends IRemoteCallback.Stub - implements Transition.TransitionListener { + implements Transition.TransitionListener, Animator.AnimatorListener { + private ArrayList<Animator> mSharedElementAnimators = new ArrayList<Animator>(); private boolean mSharedElementNotified; private Transition mExitTransition; - private Transition mSharedElementTransition; private IRemoteCallback mTransitionCompleteCallback; private boolean mExitComplete; - private boolean mSharedElementComplete; private SharedElementSource mSharedElementSource; - public ExitTransitionListener(Transition exitTransition, Transition sharedElementTransition, - SharedElementSource sharedElementSource) { + public ExitTransitionListener(Transition transition, SharedElementSource sharedElementSource) { mSharedElementSource = sharedElementSource; - mExitTransition = exitTransition; + mExitTransition = transition; mExitTransition.addListener(this); - mSharedElementTransition = sharedElementTransition; - mSharedElementTransition.addListener(this); } @Override @@ -681,36 +685,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) { - if (transition == mExitTransition) { - mExitComplete = true; - notifyExit(); - mExitTransition.removeListener(this); - } else { - mSharedElementComplete = true; - notifySharedElement(); - mSharedElementTransition.removeListener(this); - } + mExitComplete = true; + notifyExit(); + mExitTransition.removeListener(this); } @Override public void onTransitionCancel(Transition transition) { - onTransitionEnd(transition); + mExitComplete = true; + notifyExit(); + mExitTransition.removeListener(this); } @Override @@ -721,13 +725,34 @@ 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 && mSharedElementComplete + if (!mSharedElementNotified && mSharedElementAnimators.isEmpty() && mTransitionCompleteCallback != null) { mSharedElementNotified = true; try { - Bundle sharedElementState = mSharedElementSource.getSharedElementExitState(); - mTransitionCompleteCallback.sendResult(sharedElementState); + Bundle bundle = new Bundle(); + bundle.putInt(KEY_SHARED_ELEMENT_TEXTURE_ID, mSharedElementSource.getTextureId()); + mTransitionCompleteCallback.sendResult(bundle); } 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 9f1e72dfe7f5..fd3f9b3c86f7 100644 --- a/core/java/android/transition/Transition.java +++ b/core/java/android/transition/Transition.java @@ -552,7 +552,8 @@ public abstract class Transition implements Cloneable { return false; } - private static ArrayMap<Animator, AnimationInfo> getRunningAnimators() { + /** @hide */ + public static ArrayMap<Animator, AnimationInfo> getRunningAnimators() { ArrayMap<Animator, AnimationInfo> runningAnimators = sRunningAnimators.get(); if (runningAnimators == null) { runningAnimators = new ArrayMap<Animator, AnimationInfo>(); @@ -1112,32 +1113,30 @@ public abstract class Transition implements Cloneable { } } } - if (view.getParent() instanceof ViewGroup) { - TransitionValues values = new TransitionValues(); - values.view = view; - if (start) { - captureStartValues(values); + 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); + } } else { - captureEndValues(values); + mStartValues.itemIdValues.put(itemId, 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 { + if (!isListViewItem) { + mEndValues.viewValues.put(view, values); + if (id >= 0) { + mEndValues.idValues.put((int) id, values); } } else { - if (!isListViewItem) { - mEndValues.viewValues.put(view, values); - if (id >= 0) { - mEndValues.idValues.put((int) id, values); - } - } else { - mEndValues.itemIdValues.put(itemId, values); - } + 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 912f2ed88962..9fa554c3b3a6 100644 --- a/core/java/android/transition/TransitionInflater.java +++ b/core/java/android/transition/TransitionInflater.java @@ -285,27 +285,46 @@ 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); - Scene fromScene = (fromId < 0) ? null: Scene.getSceneForLayout(sceneRoot, fromId, mContext); + if (fromId >= 0) fromScene = Scene.getSceneForLayout(sceneRoot, fromId, mContext); int toId = a.getResourceId( com.android.internal.R.styleable.TransitionManager_toScene, -1); - Scene toScene = (toId < 0) ? null : Scene.getSceneForLayout(sceneRoot, toId, mContext); - + 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); if (transitionId >= 0) { Transition transition = inflateTransition(transitionId); if (transition != null) { - if (fromScene == null) { - if (toScene == null) { - throw new RuntimeException("No matching fromScene or toScene " + - "for transition ID " + transitionId); + 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 { - transitionManager.setTransition(toScene, transition); + throw new RuntimeException("No matching toScene for given fromSceneName " + + "for transition ID " + transitionId); } - } 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 f3abfb07a8f8..0106f7fbe70e 100644 --- a/core/java/android/transition/TransitionManager.java +++ b/core/java/android/transition/TransitionManager.java @@ -70,9 +70,12 @@ 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>>>>(); @@ -119,21 +122,6 @@ 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. * @@ -181,9 +169,6 @@ public class TransitionManager { } } transition = mSceneTransitions.get(scene); - if (transition == null && sceneRoot != null) { - transition = mExitSceneTransitions.get(Scene.getCurrentScene(sceneRoot)); - } return (transition != null) ? transition : sDefaultTransition; } @@ -239,31 +224,138 @@ public class TransitionManager { } /** - * Retrieve the transition to a target defined scene if one has been + * Retrieve the transition from a named scene 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 toScene or null + * @return Transition corresponding to the given fromName and toScene or null * if no association exists in this TransitionManager * - * @see #setTransition(Scene, Transition) - * @hide + * @see #setTransition(String, Scene, Transition) */ - public Transition getEnterTransition(Scene toScene) { - return mSceneTransitions.get(toScene); + public Transition getNamedTransition(String fromName, Scene toScene) { + ArrayMap<Scene, Transition> m = mNameSceneTransitions.get(fromName); + if (m != null) { + return m.get(toScene); + } + return null; } /** * 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 - * @return Transition corresponding to the given fromScene or null + * @param toName Name of the target scene + * @return Transition corresponding to the given fromScene and toName or null * if no association exists in this TransitionManager - * @hide */ - public Transition getExitTransition(Scene fromScene) { - return mExitSceneTransitions.get(fromScene); + 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); } /** diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 99aee29e194c..e9082c39233a 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -101,9 +101,7 @@ 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; @@ -18844,33 +18842,6 @@ 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 cf5e8cfc84aa..9cd3c9ddd25f 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -31,7 +31,6 @@ 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; @@ -51,8 +50,6 @@ 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; @@ -2303,13 +2300,14 @@ 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 or if {@link #getSharedElementName()} is not null. + * is not null. + * @see android.app.ActivityOptions#makeSceneTransitionAnimation(android.os.Bundle) */ public boolean isTransitionGroup() { if ((mGroupFlags & FLAG_IS_TRANSITION_GROUP_SET) != 0) { return ((mGroupFlags & FLAG_IS_TRANSITION_GROUP) != 0); } else { - return getBackground() != null || getSharedElementName() != null; + return getBackground() != null; } } @@ -2320,6 +2318,7 @@ 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; @@ -5881,37 +5880,6 @@ 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 4943a400819f..11740abc3d3b 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -29,12 +29,9 @@ 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 @@ -1389,43 +1386,30 @@ 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 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. + * 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. * - * @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. + * @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 */ - public void setTriggerEarlyEnterTransition(boolean triggerEarly) { + public void setEarlyBackgroundTransition(boolean fadeEarly) { } /** * Start the exit transition. * @hide */ - 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) { + public void startExitTransition(ActivityOptions activityOptions) { } } diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java index cc51a8bcc83b..0a8049536bc4 100644 --- a/core/java/com/android/internal/app/ActionBarImpl.java +++ b/core/java/com/android/internal/app/ActionBarImpl.java @@ -57,7 +57,6 @@ import android.widget.SpinnerAdapter; import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.Map; /** * ActionBarImpl is the ActionBar implementation used @@ -356,10 +355,6 @@ 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 d2fe9fa64d65..e495e5326e3d 100644 --- a/core/res/res/layout-xlarge/screen_action_bar.xml +++ b/core/res/res/layout-xlarge/screen_action_bar.xml @@ -34,7 +34,6 @@ 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 7b9a20bd49fd..b1889a2997f3 100644 --- a/core/res/res/layout/screen_action_bar.xml +++ b/core/res/res/layout/screen_action_bar.xml @@ -33,7 +33,6 @@ 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 d02cc8bd9383..e3364d195b16 100644 --- a/core/res/res/layout/screen_custom_title.xml +++ b/core/res/res/layout/screen_custom_title.xml @@ -31,7 +31,6 @@ 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 1c5be420c8df..bfd75658651f 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -4760,6 +4760,14 @@ <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 3106daa12fa1..c814d25d8e49 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2103,6 +2103,8 @@ <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" /> diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index 86105ea97c46..c73d90a9044a 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -29,8 +29,6 @@ import android.transition.Scene; import android.transition.Transition; import android.transition.TransitionInflater; import android.transition.TransitionManager; -import android.transition.TransitionSet; -import android.util.ArrayMap; import android.view.ViewConfiguration; import com.android.internal.R; @@ -107,9 +105,6 @@ import android.widget.TextView; import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; /** * Android-specific Window. @@ -125,13 +120,6 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private static final long MAX_TRANSITION_START_WAIT = 500; private static final long MAX_TRANSITION_FINISH_WAIT = 1000; - private static final String KEY_SCREEN_X = "shared_element:screenX"; - private static final String KEY_SCREEN_Y = "shared_element:screenY"; - private static final String KEY_TRANSLATION_Z = "shared_element:translationZ"; - private static final String KEY_WIDTH = "shared_element:width"; - private static final String KEY_HEIGHT = "shared_element:height"; - private static final String KEY_NAME = "shared_element:name"; - /** * Simple callback used by the context menu and its submenus. The options * menu submenus do not use this (their behavior is more complex). @@ -251,8 +239,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private ActivityOptions mActivityOptions; private SceneTransitionListener mSceneTransitionListener; - private boolean mTriggerEarly = true; - private Map<String, String> mSharedElementsMap; + private boolean mFadeEarly = true; static class WindowManagerHolder { static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface( @@ -2575,11 +2562,6 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return super.fitSystemWindows(insets); } - @Override - public boolean isTransitionGroup() { - return false; - } - private void updateStatusGuard(Rect insets) { boolean showStatusGuard = false; // Show the status guard when the non-overlay contextual action bar is showing @@ -4006,196 +3988,78 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } @Override - public void setTriggerEarlyEnterTransition(boolean triggerEarly) { - mTriggerEarly = triggerEarly; + public void setEarlyBackgroundTransition(boolean fadeEarly) { + mFadeEarly = fadeEarly; } @Override - public void mapTransitionTargets(Map<String, String> sharedElementNames) { - mSharedElementsMap = sharedElementNames; - } - - @Override - public Bundle startExitTransition(Map<String, View> sharedElements) { - if (mContentScene == null) { - return null; - } - Transition transition = mTransitionManager.getExitTransition(mContentScene); + public void startExitTransition(ActivityOptions activityOptions) { + Transition transition = mTransitionManager.getNamedTransition(getContentScene(), "null"); if (transition == null) { - return null; + transition = TransitionManager.getDefaultTransition().clone(); } - - // Find exiting Views and shared elements - final ArrayList<View> transitioningViews = new ArrayList<View>(); - mDecor.captureTransitioningViews(transitioningViews); - transitioningViews.removeAll(sharedElements.values()); - - Transition exitTransition = cloneAndSetTransitionTargets(transition, - transitioningViews, true); - Transition sharedElementTransition = cloneAndSetTransitionTargets(transition, - transitioningViews, false); - - // transitionSet is the total exit transition, including hero animation. - TransitionSet transitionSet = new TransitionSet(); - transitionSet.addTransition(exitTransition); - transitionSet.addTransition(sharedElementTransition); - - ActivityOptions activityOptions = createExitActivityOptions(sharedElements, - sharedElementTransition, exitTransition); - - // Start exiting the Views that need to exit - TransitionManager.beginDelayedTransition(mDecor, transitionSet); - setViewVisibility(transitioningViews, View.INVISIBLE); - - return activityOptions.toBundle(); - } - - private ActivityOptions createExitActivityOptions(final Map<String, View> sharedElements, - Transition sharedElementTransition, Transition exitTransition) { - - // Schedule capturing of the shared element state - final Bundle sharedElementArgs = new Bundle(); - captureTerminalSharedElementState(sharedElements, sharedElementArgs); - - ActivityOptions.SharedElementSource sharedElementSource - = new ActivityOptions.SharedElementSource() { + activityOptions.setExitTransition(transition, new ActivityOptions.SharedElementSource() { @Override - public Bundle getSharedElementExitState() { - return sharedElementArgs; - } - - @Override - public void acceptedSharedElements(ArrayList<String> sharedElementNames) { - if (sharedElementNames.size() == sharedElements.size()) { - return; // They were all accepted - } - Transition transition = mTransitionManager.getExitTransition(mContentScene).clone(); - TransitionManager.beginDelayedTransition(mDecor, transition); - for (String name: sharedElements.keySet()) { - if (!sharedElementNames.contains(name)) { - sharedElements.get(name).setVisibility(View.INVISIBLE); - } - } - sharedElements.keySet().retainAll(sharedElementNames); - } - - @Override - public void hideSharedElements() { - if (sharedElements != null) { - setViewVisibility(sharedElements.values(), View.INVISIBLE); - } - } - }; - - ArrayList<String> names = new ArrayList<String>(sharedElements.keySet()); - return ActivityOptions.makeSceneTransitionAnimation( - exitTransition, names, sharedElementTransition, sharedElementSource); - } - - private void captureTerminalSharedElementState(final Map<String, View> sharedElements, - final Bundle sharedElementArgs) { - mDecor.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { - @Override - public boolean onPreDraw() { - mDecor.getViewTreeObserver().removeOnPreDrawListener(this); - int[] tempLoc = new int[2]; - for (String name: sharedElements.keySet()) { - View sharedElement = sharedElements.get(name); - captureSharedElementState(sharedElement, name, sharedElementArgs, tempLoc); - } - return true; + public int getTextureId() { + // TODO: move shared elements to a layer and return the texture id + recurseHideExitingSharedElements(mContentParent); + return 0; } }); + ViewGroup sceneRoot = getContentScene().getSceneRoot(); + TransitionManager.beginDelayedTransition(sceneRoot, transition); + recurseExitNonSharedElements(mContentParent); } - private static Transition cloneAndSetTransitionTargets(Transition transition, - List<View> views, boolean add) { - transition = transition.clone(); - if (!transition.getTargetIds().isEmpty() || !transition.getTargets().isEmpty()) { - TransitionSet set = new TransitionSet(); - set.addTransition(transition); - transition = set; - } - for (View view: views) { - if (add) { - transition.addTarget(view); + private static void recurseExitNonSharedElements(ViewGroup viewGroup) { + int numChildren = viewGroup.getChildCount(); + for (int i = 0; i < numChildren; i++) { + View child = viewGroup.getChildAt(i); + if (child.getSharedElementName() != null || (child.getVisibility() != View.VISIBLE)) { + continue; + } + if (child instanceof ViewGroup && !((ViewGroup)child).isTransitionGroup()) { + recurseExitNonSharedElements((ViewGroup) child); } else { - transition.excludeTarget(view, true); + child.setVisibility(View.INVISIBLE); } } - return transition; } - private static void setViewVisibility(Collection<View> views, int visibility) { - for (View view : views) { - view.setVisibility(visibility); + private static void recurseHideViews(ViewGroup viewGroup, ArrayList<View> nonSharedElements, + ArrayList<View> sharedElements) { + int numChildren = viewGroup.getChildCount(); + for (int i = 0; i < numChildren; i++) { + View child = viewGroup.getChildAt(i); + if (child.getVisibility() != View.VISIBLE) { + continue; + } + if (child.getSharedElementName() != null) { + sharedElements.add(child); + child.setVisibility(View.INVISIBLE); + } else if (child instanceof ViewGroup && !((ViewGroup)child).isTransitionGroup()) { + recurseHideViews((ViewGroup) child, nonSharedElements, sharedElements); + } else { + nonSharedElements.add(child); + child.setVisibility(View.INVISIBLE); + } } } - /** - * Sets the captured values from a previous - * {@link #captureSharedElementState(android.view.View, String, android.os.Bundle, int[])} - * @param view The View to apply placement changes to. - * @param name The shared element name given from the source Activity. - * @param transitionArgs A <code>Bundle</code> containing all placementinformation for named - * shared elements in the scene. - * @param tempLoc A temporary int[2] for capturing the current location of views. - */ - private static void setSharedElementState(View view, String name, Bundle transitionArgs, - int[] tempLoc) { - Bundle sharedElementBundle = transitionArgs.getBundle(name); - if (sharedElementBundle == null) { - return; + private static void recurseHideExitingSharedElements(ViewGroup viewGroup) { + int numChildren = viewGroup.getChildCount(); + for (int i = 0; i < numChildren; i++) { + View child = viewGroup.getChildAt(i); + if (child.getVisibility() != View.VISIBLE) { + continue; + } + if (child.getSharedElementName() != null) { + child.setVisibility(View.INVISIBLE); + } else if (child instanceof ViewGroup) { + ViewGroup childViewGroup = (ViewGroup) child; + recurseHideExitingSharedElements(childViewGroup); + } } - - int x = sharedElementBundle.getInt(KEY_SCREEN_X); - view.getLocationOnScreen(tempLoc); - int offsetX = x - tempLoc[0]; - view.offsetLeftAndRight(offsetX); - - int width = sharedElementBundle.getInt(KEY_WIDTH); - view.setRight(view.getLeft() + width); - - int y = sharedElementBundle.getInt(KEY_SCREEN_Y); - int offsetY = y - tempLoc[1]; - view.offsetTopAndBottom(offsetY); - - int height = sharedElementBundle.getInt(KEY_HEIGHT); - view.setBottom(view.getTop() + height); - - float z = sharedElementBundle.getFloat(KEY_TRANSLATION_Z); - view.setTranslationZ(z); - } - - /** - * Captures placement information for Views with a shared element name for - * Activity Transitions. - * @param view The View to capture the placement information for. - * @param name The shared element name in the target Activity to apply the placement - * information for. - * @param transitionArgs Bundle to store shared element placement information. - * @param tempLoc A temporary int[2] for capturing the current location of views. - * @see #setSharedElementState(android.view.View, String, android.os.Bundle, int[]) - */ - private static void captureSharedElementState(View view, String name, Bundle transitionArgs, - int[] tempLoc) { - Bundle sharedElementBundle = new Bundle(); - view.getLocationOnScreen(tempLoc); - float scaleX = view.getScaleX(); - sharedElementBundle.putInt(KEY_SCREEN_X, tempLoc[0]); - int width = Math.round(view.getWidth() * scaleX); - sharedElementBundle.putInt(KEY_WIDTH, width); - - float scaleY = view.getScaleY(); - sharedElementBundle.putInt(KEY_SCREEN_Y, tempLoc[1]); - int height= Math.round(view.getHeight() * scaleY); - sharedElementBundle.putInt(KEY_HEIGHT, height); - - sharedElementBundle.putFloat(KEY_TRANSLATION_Z, view.getTranslationZ()); - - sharedElementBundle.putString(KEY_NAME, view.getSharedElementName()); - - transitionArgs.putBundle(name, sharedElementBundle); } /** @@ -4216,57 +4080,46 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private boolean mAllDone; private Handler mHandler = new Handler(); private boolean mEnterTransitionStarted; - private ArrayMap<String, View> mSharedElementTargets = new ArrayMap<String, View>(); - private ArrayList<View> mEnteringViews = new ArrayList<View>(); + private ArrayList<View> mSharedElements = new ArrayList<View>(); public EnterScene() { mSceneTransitionListener.nullPendingTransition(); Drawable background = getDecorView().getBackground(); if (background != null) { + setBackgroundDrawable(null); background.setAlpha(0); - mDecor.drawableChanged(); + setBackgroundDrawable(background); } mSceneTransitionListener.convertToTranslucent(); } @Override public boolean onPreDraw() { - ViewTreeObserver observer = mDecor.getViewTreeObserver(); + ViewTreeObserver observer = mContentParent.getViewTreeObserver(); observer.removeOnPreDrawListener(this); if (!mEnterTransitionStarted && mSceneTransitionListener != null) { mEnterTransitionStarted = true; - mDecor.captureTransitioningViews(mEnteringViews); - ArrayList<String> sharedElementNames = mActivityOptions.getSharedElementNames(); - if (sharedElementNames != null) { - mDecor.findSharedElements(mSharedElementTargets); - if (mSharedElementsMap != null) { - for (Map.Entry<String, String> entry : mSharedElementsMap.entrySet()) { - View sharedElement = mSharedElementTargets.remove(entry.getValue()); - if (sharedElement != null) { - mSharedElementTargets.put(entry.getKey(), sharedElement); - } - } - } - mSharedElementTargets.keySet().retainAll(sharedElementNames); - mEnteringViews.removeAll(mSharedElementTargets.values()); + ArrayList<View> enteringViews = new ArrayList<View>(); + recurseHideViews(mContentParent, enteringViews, mSharedElements); + Transition transition = getTransitionManager().getNamedTransition("null", + mContentScene); + if (transition == null) { + transition = TransitionManager.getDefaultTransition().clone(); } - - setViewVisibility(mEnteringViews, View.INVISIBLE); - setViewVisibility(mSharedElementTargets.values(), View.INVISIBLE); - if (mTriggerEarly) { - beginEnterScene(); + TransitionManager.beginDelayedTransition(mContentParent, transition); + for (View hidden : enteringViews) { + hidden.setVisibility(View.VISIBLE); } observer.addOnPreDrawListener(this); } else { mHandler.postDelayed(this, MAX_TRANSITION_START_WAIT); - mActivityOptions.dispatchSceneTransitionStarted(this, - new ArrayList<String>(mSharedElementTargets.keySet())); + mActivityOptions.dispatchSceneTransitionStarted(this); } return true; } public void start() { - ViewTreeObserver observer = mDecor.getViewTreeObserver(); + ViewTreeObserver observer = mContentParent.getViewTreeObserver(); observer.addOnPreDrawListener(this); } @@ -4276,43 +4129,25 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } @Override - public void sharedElementTransitionComplete(Bundle transitionArgs) { + public void sharedElementTransitionComplete() { if (!mSharedElementReadyReceived) { mSharedElementReadyReceived = true; mHandler.removeCallbacks(this); mHandler.postDelayed(this, MAX_TRANSITION_FINISH_WAIT); - if (!mSharedElementTargets.isEmpty()) { - Transition transition = getTransitionManager().getEnterTransition( - mContentScene); - if (transition == null) { - transition = TransitionManager.getDefaultTransition(); - } - transition = transition.clone(); - if (transitionArgs == null) { - TransitionManager.beginDelayedTransition(mDecor, transition); - setViewVisibility(mSharedElementTargets.values(), View.VISIBLE); - } else { - int[] tempLoc = new int[2]; - for (Map.Entry<String, View> entry: mSharedElementTargets.entrySet()) { - setSharedElementState(entry.getValue(), entry.getKey(), transitionArgs, - tempLoc); - } - setViewVisibility(mSharedElementTargets.values(), View.VISIBLE); - mSceneTransitionListener.sharedElementStart(transition); - mDecor.getViewTreeObserver().addOnPreDrawListener( - new ViewTreeObserver.OnPreDrawListener() { - @Override - public boolean onPreDraw() { - mDecor.getViewTreeObserver().removeOnPreDrawListener(this); - mSceneTransitionListener.sharedElementEnd(); - mActivityOptions.dispatchSharedElementsReady(); - return true; - } - }); - TransitionManager.beginDelayedTransition(mDecor, transition); - } + for (View sharedElement: mSharedElements) { + sharedElement.setVisibility(View.VISIBLE); } - if (mTriggerEarly) { + mSharedElements.clear(); + mContentParent.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + mContentParent.getViewTreeObserver().removeOnPreDrawListener(this); + mSceneTransitionListener.enterSharedElement( + mActivityOptions.getSceneTransitionArgs()); + return false; + } + }); + if (mFadeEarly) { fadeInBackground(); } } @@ -4335,10 +4170,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return; } mAllDone = true; - sharedElementTransitionComplete(null); + sharedElementTransitionComplete(); mHandler.removeCallbacks(this); - if (!mTriggerEarly) { - beginEnterScene(); + if (!mFadeEarly) { fadeInBackground(); } } @@ -4359,14 +4193,5 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { @Override public void onAnimationRepeat(Animator animation) { } - - private void beginEnterScene() { - Transition transition = getTransitionManager().getEnterTransition(mContentScene); - if (transition == null) { - transition = TransitionManager.getDefaultTransition().clone(); - } - TransitionManager.beginDelayedTransition(mDecor, transition); - setViewVisibility(mEnteringViews, View.VISIBLE); - } } } |