summaryrefslogtreecommitdiffstats
path: root/core/java/android/app
diff options
context:
space:
mode:
authorAdam Powell <adamp@google.com>2013-11-06 14:58:58 -0800
committerAdam Powell <adamp@google.com>2013-12-05 10:06:19 -0800
commitcfbe9be5b3b701d95fb24fa0f7c8d9be43eec776 (patch)
treea130c8227af97db3cfe74f61daeb0b3a1181b752 /core/java/android/app
parentf254ed9a98bd343feefab3d5737568539b693605 (diff)
downloadframeworks_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/android/app')
-rw-r--r--core/java/android/app/Activity.java73
-rw-r--r--core/java/android/app/ActivityOptions.java52
-rw-r--r--core/java/android/app/ActivityThread.java37
-rw-r--r--core/java/android/app/ApplicationThreadNative.java19
-rw-r--r--core/java/android/app/IApplicationThread.java9
5 files changed, 163 insertions, 27 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;