diff options
| author | Adam Powell <adamp@google.com> | 2013-11-06 14:58:58 -0800 |
|---|---|---|
| committer | Adam Powell <adamp@google.com> | 2013-12-05 10:06:19 -0800 |
| commit | cfbe9be5b3b701d95fb24fa0f7c8d9be43eec776 (patch) | |
| tree | a130c8227af97db3cfe74f61daeb0b3a1181b752 /core/java | |
| parent | f254ed9a98bd343feefab3d5737568539b693605 (diff) | |
| download | frameworks_base-cfbe9be5b3b701d95fb24fa0f7c8d9be43eec776.zip frameworks_base-cfbe9be5b3b701d95fb24fa0f7c8d9be43eec776.tar.gz frameworks_base-cfbe9be5b3b701d95fb24fa0f7c8d9be43eec776.tar.bz2 | |
Add support for cross-activity scenes and transitions
* Add theme attributes for specifying a top-level TransitionManager
for an activity window.
* Add window feature for automatic content transitions. This
automatically assigns/creates a Scene for setContentView calls.
* Add named transitions. This allows apps to define APIs for
handshake-agreements about which exit/entrance transitions to play.
* Add new transition type for ActivityOptions. This lets the system
use ActivityOptions to communicate transition specifics and
arguments to the called activity.
* Have ActivityManager pass appropriate ActivityOptions through to the
called Activity. Have the called activity call back into the caller
to let it know which transition of a possible requested set was
chosen.
Still to do:
* Define and pass arguments for transitions. This will require
defining a Parcelable version of TransitionValues and deciding how
much leeway apps should have for these things.
* Determine how to appropriately filter the ActivityOptions bundle so
that only appropriate data reaches the target.
* Determine if generalizing the auto-Scenes functionality to
ViewGroups is appropriate.
Change-Id: I10684b926129ab2fbc1adec9ef31767237acae79
Diffstat (limited to 'core/java')
| -rw-r--r-- | core/java/android/app/Activity.java | 73 | ||||
| -rw-r--r-- | core/java/android/app/ActivityOptions.java | 52 | ||||
| -rw-r--r-- | core/java/android/app/ActivityThread.java | 37 | ||||
| -rw-r--r-- | core/java/android/app/ApplicationThreadNative.java | 19 | ||||
| -rw-r--r-- | core/java/android/app/IApplicationThread.java | 9 | ||||
| -rw-r--r-- | core/java/android/transition/TransitionInflater.java | 30 | ||||
| -rw-r--r-- | core/java/android/transition/TransitionManager.java | 142 | ||||
| -rw-r--r-- | core/java/android/view/Window.java | 45 |
8 files changed, 375 insertions, 32 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index d34b05d..698f06a 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -17,6 +17,9 @@ 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.SuperNotCalledException; import com.android.internal.app.ActionBarImpl; @@ -1990,6 +1993,41 @@ public class Activity extends ContextThemeWrapper } /** + * Retrieve the {@link TransitionManager} responsible for default transitions in this window. + * Requires {@link Window#FEATURE_CONTENT_TRANSITIONS}. + * + * <p>This method will return non-null after content has been initialized (e.g. by using + * {@link #setContentView}) if {@link Window#FEATURE_CONTENT_TRANSITIONS} has been granted.</p> + * + * @return This window's content TransitionManager or null if none is set. + */ + public TransitionManager getContentTransitionManager() { + return getWindow().getTransitionManager(); + } + + /** + * Set the {@link TransitionManager} to use for default transitions in this window. + * Requires {@link Window#FEATURE_CONTENT_TRANSITIONS}. + * + * @param tm The TransitionManager to use for scene changes. + */ + public void setContentTransitionManager(TransitionManager tm) { + getWindow().setTransitionManager(tm); + } + + /** + * Retrieve the {@link Scene} representing this window's current content. + * Requires {@link Window#FEATURE_CONTENT_TRANSITIONS}. + * + * <p>This method will return null if the current content is not represented by a Scene.</p> + * + * @return Current Scene being shown or null + */ + public Scene getContentScene() { + return getWindow().getContentScene(); + } + + /** * Sets whether this activity is finished when touched outside its window's * bounds. */ @@ -3408,7 +3446,29 @@ public class Activity extends ContextThemeWrapper * @see #startActivity */ public void startActivityForResult(Intent intent, int requestCode) { - startActivityForResult(intent, requestCode, null); + final TransitionManager tm = getWindow().getTransitionManager(); + final Scene currScene = getWindow().getContentScene(); + final String[] targetSceneNames = currScene != null && tm != null ? + tm.getTargetSceneNames(currScene) : null; + + if (targetSceneNames == null || targetSceneNames.length == 0) { + startActivityForResult(intent, requestCode, null); + } else { + // TODO Capture the scene transition args and send along + final ActivityOptions opts = ActivityOptions.makeSceneTransitionAnimation( + targetSceneNames, null, + new ActivityOptions.OnSceneTransitionStartedListener() { + @Override public void onSceneTransitionStarted(String destSceneName) { + final Transition t = tm.getNamedTransition(currScene, destSceneName); + // TODO Fill this in to notify the outgoing activity that it should + // treat this as a sync point for the transition - the target + // transition has started. + Log.d(TAG, "Scene transition to scene " + destSceneName + + " transition " + t); + } + }, mHandler); + startActivityForResult(intent, requestCode, opts.toBundle()); + } } /** @@ -5229,6 +5289,16 @@ public class Activity extends ContextThemeWrapper CharSequence title, Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, Configuration config) { + attach(context, aThread, instr, token, ident, application, intent, info, title, parent, id, + lastNonConfigurationInstances, config, null); + } + + final void attach(Context context, ActivityThread aThread, + Instrumentation instr, IBinder token, int ident, + Application application, Intent intent, ActivityInfo info, + CharSequence title, Activity parent, String id, + NonConfigurationInstances lastNonConfigurationInstances, + Configuration config, Bundle options) { attachBaseContext(context); mFragments.attachActivity(this, mContainer, null); @@ -5265,6 +5335,7 @@ public class Activity extends ContextThemeWrapper mWindow.setContainer(mParent.getWindow()); } mWindowManager = mWindow.getWindowManager(); + mWindow.setTransitionOptions(options); mCurrentConfig = config; } diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 44f6859..582ce3c 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -22,6 +22,8 @@ import android.os.Bundle; import android.os.Handler; import android.os.IRemoteCallback; import android.os.RemoteException; +import android.text.TextUtils; +import android.util.Log; import android.view.View; /** @@ -30,6 +32,8 @@ import android.view.View; * Context.startActivity(Intent, Bundle)} and related methods. */ public class ActivityOptions { + private static final String TAG = "ActivityOptions"; + /** * The package name that created the options. * @hide @@ -481,8 +485,19 @@ public class ActivityOptions { } /** @hide */ - public IRemoteCallback getOnSceneTransitionStartedListener() { - return mSceneTransitionStartedListener; + public void dispatchSceneTransitionStarted(String destScene) { + if (mSceneTransitionStartedListener != null) { + Bundle data = null; + if (!TextUtils.isEmpty(destScene)) { + data = new Bundle(); + data.putString(KEY_DEST_SCENE_NAME_CHOSEN, destScene); + } + try { + mSceneTransitionStartedListener.sendResult(data); + } catch (RemoteException e) { + Log.e(TAG, "Caught exception dispatching scene transition start", e); + } + } } /** @hide */ @@ -493,6 +508,12 @@ public class ActivityOptions { } catch (RemoteException e) { } } + if (mSceneTransitionStartedListener != null) { + try { + mSceneTransitionStartedListener.sendResult(null); + } catch (RemoteException e) { + } + } } /** @hide */ @@ -572,6 +593,8 @@ public class ActivityOptions { } mSceneTransitionStartedListener = otherOptions.mSceneTransitionStartedListener; mDestSceneNames = otherOptions.mDestSceneNames; + mTransitionArgs = otherOptions.mTransitionArgs; + mThumbnail = null; mAnimationStartedListener = null; break; } @@ -595,7 +618,7 @@ public class ActivityOptions { b.putInt(KEY_ANIM_TYPE, mAnimationType); b.putInt(KEY_ANIM_ENTER_RES_ID, mCustomEnterResId); b.putInt(KEY_ANIM_EXIT_RES_ID, mCustomExitResId); - b.putIBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener + b.putBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener != null ? mAnimationStartedListener.asBinder() : null); break; case ANIM_SCALE_UP: @@ -611,10 +634,31 @@ public class ActivityOptions { b.putParcelable(KEY_ANIM_THUMBNAIL, mThumbnail); b.putInt(KEY_ANIM_START_X, mStartX); b.putInt(KEY_ANIM_START_Y, mStartY); - b.putIBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener + b.putBinder(KEY_ANIM_START_LISTENER, mAnimationStartedListener != null ? mAnimationStartedListener.asBinder() : null); break; + case ANIM_SCENE_TRANSITION: + b.putInt(KEY_ANIM_TYPE, mAnimationType); + b.putStringArray(KEY_DEST_SCENE_NAMES, mDestSceneNames); + b.putBundle(KEY_SCENE_TRANSITION_ARGS, mTransitionArgs); + b.putBinder(KEY_SCENE_TRANSITION_START_LISTENER, mSceneTransitionStartedListener + != null ? mSceneTransitionStartedListener.asBinder() : null); + break; } return b; } + + /** + * Return the filtered options only meant to be seen by the target activity itself + * @hide + */ + public ActivityOptions forTargetActivity() { + if (mAnimationType == ANIM_SCENE_TRANSITION) { + final ActivityOptions result = new ActivityOptions(); + result.update(this); + return result; + } + + return null; + } } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 5e3dc02..e667bad 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -56,6 +56,7 @@ import android.os.DropBoxManager; import android.os.Environment; import android.os.Handler; import android.os.IBinder; +import android.os.IRemoteCallback; import android.os.Looper; import android.os.Message; import android.os.MessageQueue; @@ -68,12 +69,15 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; +import android.transition.Scene; +import android.transition.TransitionManager; import android.util.AndroidRuntimeException; import android.util.ArrayMap; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; import android.util.LogPrinter; +import android.util.Pair; import android.util.PrintWriterPrinter; import android.util.Slog; import android.util.SuperNotCalledException; @@ -284,6 +288,7 @@ public final class ActivityThread { boolean isForward; int pendingConfigChanges; boolean onlyLocalRequest; + Bundle activityOptions; View mPendingRemoveWindow; WindowManager mPendingRemoveWindowManager; @@ -576,9 +581,10 @@ public final class ActivityThread { } public final void scheduleResumeActivity(IBinder token, int processState, - boolean isForward) { + boolean isForward, Bundle resumeArgs) { updateProcessState(processState, false); - sendMessage(H.RESUME_ACTIVITY, token, isForward ? 1 : 0); + sendMessage(H.RESUME_ACTIVITY, new Pair<IBinder, Bundle>(token, resumeArgs), + isForward ? 1 : 0); } public final void scheduleSendResult(IBinder token, List<ResultInfo> results) { @@ -594,7 +600,8 @@ public final class ActivityThread { ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo, int procState, Bundle state, List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, boolean notResumed, boolean isForward, - String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler) { + String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler, + Bundle resumeArgs) { updateProcessState(procState, false); @@ -616,6 +623,7 @@ public final class ActivityThread { r.profileFile = profileName; r.profileFd = profileFd; r.autoStopProfiler = autoStopProfiler; + r.activityOptions = resumeArgs; updatePendingConfiguration(curConfig); @@ -1189,7 +1197,7 @@ public final class ActivityThread { switch (msg.what) { case LAUNCH_ACTIVITY: { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); - ActivityClientRecord r = (ActivityClientRecord)msg.obj; + final ActivityClientRecord r = (ActivityClientRecord) msg.obj; r.packageInfo = getPackageInfoNoCheck( r.activityInfo.applicationInfo, r.compatInfo); @@ -1235,7 +1243,8 @@ public final class ActivityThread { break; case RESUME_ACTIVITY: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityResume"); - handleResumeActivity((IBinder)msg.obj, true, + final Pair<IBinder, Bundle> resumeArgs = (Pair<IBinder, Bundle>) msg.obj; + handleResumeActivity(resumeArgs.first, resumeArgs.second, true, msg.arg1 != 0, true); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; @@ -2032,7 +2041,7 @@ public final class ActivityThread { + ", comp=" + name + ", token=" + token); } - return performLaunchActivity(r, null); + return performLaunchActivity(r, null, null); } public final Activity getActivity(IBinder token) { @@ -2085,7 +2094,8 @@ public final class ActivityThread { sendMessage(H.CLEAN_UP_CONTEXT, cci); } - private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { + private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent, + Bundle options) { // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")"); ActivityInfo aInfo = r.activityInfo; @@ -2143,7 +2153,7 @@ public final class ActivityThread { + r.activityInfo.name + " with config " + config); activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, - r.embeddedID, r.lastNonConfigurationInstances, config); + r.embeddedID, r.lastNonConfigurationInstances, config, options); if (customIntent != null) { activity.mIntent = customIntent; @@ -2242,12 +2252,13 @@ public final class ActivityThread { if (localLOGV) Slog.v( TAG, "Handling launch of " + r); - Activity a = performLaunchActivity(r, customIntent); + + Activity a = performLaunchActivity(r, customIntent, r.activityOptions); if (a != null) { r.createdConfig = new Configuration(mConfiguration); Bundle oldState = r.state; - handleResumeActivity(r.token, false, r.isForward, + handleResumeActivity(r.token, r.activityOptions, false, r.isForward, !r.activity.mFinished && !r.startsNotResumed); if (!r.activity.mFinished && r.startsNotResumed) { @@ -2808,12 +2819,13 @@ public final class ActivityThread { r.mPendingRemoveWindowManager = null; } - final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, - boolean reallyResume) { + final void handleResumeActivity(IBinder token, Bundle resumeArgs, + boolean clearHide, boolean isForward, boolean reallyResume) { // If we are getting ready to gc after going to the background, well // we are back active so skip it. unscheduleGcIdler(); + // TODO Push resumeArgs into the activity for consideration ActivityClientRecord r = performResumeActivity(token, clearHide); if (r != null) { @@ -3734,6 +3746,7 @@ public final class ActivityThread { } } r.startsNotResumed = tmp.startsNotResumed; + r.activityOptions = null; handleLaunchActivity(r, currentIntent); } diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index 347d43f..c8f1280 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -113,7 +113,8 @@ public abstract class ApplicationThreadNative extends Binder IBinder b = data.readStrongBinder(); int procState = data.readInt(); boolean isForward = data.readInt() != 0; - scheduleResumeActivity(b, procState, isForward); + Bundle resumeArgs = data.readBundle(); + scheduleResumeActivity(b, procState, isForward, resumeArgs); return true; } @@ -145,8 +146,10 @@ public abstract class ApplicationThreadNative extends Binder ParcelFileDescriptor profileFd = data.readInt() != 0 ? ParcelFileDescriptor.CREATOR.createFromParcel(data) : null; boolean autoStopProfiler = data.readInt() != 0; + Bundle resumeArgs = data.readBundle(); scheduleLaunchActivity(intent, b, ident, info, curConfig, compatInfo, procState, state, - ri, pi, notResumed, isForward, profileName, profileFd, autoStopProfiler); + ri, pi, notResumed, isForward, profileName, profileFd, autoStopProfiler, + resumeArgs); return true; } @@ -696,13 +699,15 @@ class ApplicationThreadProxy implements IApplicationThread { data.recycle(); } - public final void scheduleResumeActivity(IBinder token, int procState, boolean isForward) + public final void scheduleResumeActivity(IBinder token, int procState, boolean isForward, + Bundle resumeArgs) throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); data.writeStrongBinder(token); data.writeInt(procState); data.writeInt(isForward ? 1 : 0); + data.writeBundle(resumeArgs); mRemote.transact(SCHEDULE_RESUME_ACTIVITY_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); data.recycle(); @@ -722,9 +727,10 @@ class ApplicationThreadProxy implements IApplicationThread { public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo, int procState, Bundle state, List<ResultInfo> pendingResults, - List<Intent> pendingNewIntents, boolean notResumed, boolean isForward, - String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler) - throws RemoteException { + List<Intent> pendingNewIntents, boolean notResumed, boolean isForward, + String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler, + Bundle resumeArgs) + throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); intent.writeToParcel(data, 0); @@ -747,6 +753,7 @@ class ApplicationThreadProxy implements IApplicationThread { data.writeInt(0); } data.writeInt(autoStopProfiler ? 1 : 0); + data.writeBundle(resumeArgs); mRemote.transact(SCHEDULE_LAUNCH_ACTIVITY_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); data.recycle(); diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java index d0cc1bb..1ea9d87 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -50,15 +50,16 @@ public interface IApplicationThread extends IInterface { int configChanges) throws RemoteException; void scheduleWindowVisibility(IBinder token, boolean showWindow) throws RemoteException; void scheduleSleeping(IBinder token, boolean sleeping) throws RemoteException; - void scheduleResumeActivity(IBinder token, int procState, boolean isForward) + void scheduleResumeActivity(IBinder token, int procState, boolean isForward, Bundle resumeArgs) throws RemoteException; void scheduleSendResult(IBinder token, List<ResultInfo> results) throws RemoteException; void scheduleLaunchActivity(Intent intent, IBinder token, int ident, ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo, int procState, Bundle state, List<ResultInfo> pendingResults, - List<Intent> pendingNewIntents, boolean notResumed, boolean isForward, - String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler) - throws RemoteException; + List<Intent> pendingNewIntents, boolean notResumed, boolean isForward, + String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler, + Bundle resumeArgs) + throws RemoteException; void scheduleRelaunchActivity(IBinder token, List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, int configChanges, boolean notResumed, Configuration config) throws RemoteException; diff --git a/core/java/android/transition/TransitionInflater.java b/core/java/android/transition/TransitionInflater.java index 9f77d5e..9fa554c 100644 --- a/core/java/android/transition/TransitionInflater.java +++ b/core/java/android/transition/TransitionInflater.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; +import android.text.TextUtils; import android.util.AttributeSet; import android.util.Xml; import android.view.InflateException; @@ -291,19 +292,40 @@ public class TransitionInflater { 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); if (transitionId >= 0) { Transition transition = inflateTransition(transitionId); if (transition != null) { if (fromScene != null) { - if (toScene == null){ - throw new RuntimeException("No matching toScene for given fromScene " + - "for transition ID " + transitionId); - } else { + 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 " + + "for transition ID " + transitionId); + } + } } } a.recycle(); diff --git a/core/java/android/transition/TransitionManager.java b/core/java/android/transition/TransitionManager.java index 3bf6790..0106f7f 100644 --- a/core/java/android/transition/TransitionManager.java +++ b/core/java/android/transition/TransitionManager.java @@ -67,9 +67,15 @@ public class TransitionManager { private static Transition sDefaultTransition = new AutoTransition(); + private static final String[] EMPTY_STRINGS = new String[0]; + ArrayMap<Scene, Transition> mSceneTransitions = 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>>>>(); @@ -218,6 +224,141 @@ public class TransitionManager { } /** + * 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 fromName and toScene or null + * if no association exists in this TransitionManager + * + * @see #setTransition(String, Scene, Transition) + */ + 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 + * @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 + */ + 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); + } + + /** * This private utility class is used to listen for both OnPreDraw and * OnAttachStateChange events. OnPreDraw events are the main ones we care * about since that's what triggers the transition to take place. @@ -329,7 +470,6 @@ public class TransitionManager { // Auto transition if there is no transition declared for the Scene, but there is // a root or parent view changeScene(scene, getTransition(scene)); - } /** diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 11d8d36..2f62431 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -27,6 +27,8 @@ import android.net.Uri; import android.os.Bundle; import android.os.IBinder; import android.os.SystemProperties; +import android.transition.Scene; +import android.transition.TransitionManager; import android.view.accessibility.AccessibilityEvent; /** @@ -1333,4 +1335,47 @@ public abstract class Window { * @param event A key or touch event to inject to this window. */ public void injectInputEvent(InputEvent event) { } + + /** + * Retrieve the {@link TransitionManager} responsible for for default transitions + * in this window. Requires {@link #FEATURE_CONTENT_TRANSITIONS}. + * + * <p>This method will return non-null after content has been initialized (e.g. by using + * {@link #setContentView}) if {@link #FEATURE_CONTENT_TRANSITIONS} has been granted.</p> + * + * @return This window's content TransitionManager or null if none is set. + */ + public TransitionManager getTransitionManager() { + return null; + } + + /** + * Set the {@link TransitionManager} to use for default transitions in this window. + * Requires {@link #FEATURE_CONTENT_TRANSITIONS}. + * + * @param tm The TransitionManager to use for scene changes. + */ + public void setTransitionManager(TransitionManager tm) { + throw new UnsupportedOperationException(); + } + + /** + * Retrieve the {@link Scene} representing this window's current content. + * Requires {@link #FEATURE_CONTENT_TRANSITIONS}. + * + * <p>This method will return null if the current content is not represented by a Scene.</p> + * + * @return Current Scene being shown or null + */ + public Scene getContentScene() { + return null; + } + + /** + * Set options that can affect the transition behavior within this window. + * @param options Options to set or null for none + */ + public void setTransitionOptions(Bundle options) { + throw new UnsupportedOperationException(); + } } |
