summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDianne Hackborn <hackbod@google.com>2012-03-20 11:11:26 -0700
committerDianne Hackborn <hackbod@google.com>2012-03-23 14:13:13 -0700
commit8078d8c8a282ca81344febe7256f63b1e805e3aa (patch)
tree6bfb8ab0a389c2ae344088017bd0ceee1e913660
parent3c4da3cad04ca878a4a37fcca3f3e2ff51d03fcb (diff)
downloadframeworks_base-8078d8c8a282ca81344febe7256f63b1e805e3aa.zip
frameworks_base-8078d8c8a282ca81344febe7256f63b1e805e3aa.tar.gz
frameworks_base-8078d8c8a282ca81344febe7256f63b1e805e3aa.tar.bz2
Add new thumbnail animation.
Use it for recent tasks switching. Not perfect yet by far, but something. Also fix issue #6186758: Twitter crashes after tapping on a tweet on JRM75D Change-Id: I49bf6c94aafde875ac652dedaf96d6c08cc9e7d2
-rw-r--r--api/current.txt6
-rw-r--r--core/java/android/app/Activity.java2
-rw-r--r--core/java/android/app/ActivityManager.java21
-rw-r--r--core/java/android/app/ActivityManagerNative.java12
-rw-r--r--core/java/android/app/ActivityOptions.java194
-rw-r--r--core/java/android/app/IActivityManager.java2
-rw-r--r--core/java/android/view/IWindowManager.aidl2
-rw-r--r--core/java/android/view/animation/Animation.java2
-rw-r--r--core/java/android/view/animation/Transformation.java10
-rw-r--r--core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java20
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java16
-rw-r--r--services/java/com/android/server/am/ActivityRecord.java27
-rw-r--r--services/java/com/android/server/am/ActivityStack.java43
-rw-r--r--services/java/com/android/server/wm/AppWindowToken.java85
-rw-r--r--services/java/com/android/server/wm/WindowAnimator.java9
-rw-r--r--services/java/com/android/server/wm/WindowManagerService.java186
-rw-r--r--tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java2
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java6
19 files changed, 598 insertions, 49 deletions
diff --git a/api/current.txt b/api/current.txt
index a175f8a..de907e8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2744,6 +2744,7 @@ package android.app {
method public static boolean isUserAMonkey();
method public void killBackgroundProcesses(java.lang.String);
method public void moveTaskToFront(int, int);
+ method public void moveTaskToFront(int, int, android.os.Bundle);
method public deprecated void restartPackage(java.lang.String);
field public static final int MOVE_TASK_NO_USER_ACTION = 2; // 0x2
field public static final int MOVE_TASK_WITH_HOME = 1; // 0x1
@@ -2867,9 +2868,14 @@ package android.app {
public class ActivityOptions {
method public void join(android.app.ActivityOptions);
method public static android.app.ActivityOptions makeCustomAnimation(android.content.Context, int, int);
+ method public static android.app.ActivityOptions makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int, android.app.ActivityOptions.OnAnimationStartedListener);
method public android.os.Bundle toBundle();
}
+ public static abstract interface ActivityOptions.OnAnimationStartedListener {
+ method public abstract void onAnimationStarted();
+ }
+
public class AlarmManager {
method public void cancel(android.app.PendingIntent);
method public void set(int, long, android.app.PendingIntent);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index ea32745..b277efb 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -3639,7 +3639,7 @@ public class Activity extends ContextThemeWrapper
*/
public void startActivityFromChild(Activity child, Intent intent,
int requestCode) {
- startActivityFromChild(child, intent, requestCode);
+ startActivityFromChild(child, intent, requestCode, null);
}
/**
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index d056b17..531a695 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -31,6 +31,7 @@ import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
import android.os.Parcel;
@@ -816,6 +817,19 @@ public class ActivityManager {
public static final int MOVE_TASK_NO_USER_ACTION = 0x00000002;
/**
+ * Equivalent to calling {@link #moveTaskToFront(int, int, Bundle)}
+ * with a null options argument.
+ *
+ * @param taskId The identifier of the task to be moved, as found in
+ * {@link RunningTaskInfo} or {@link RecentTaskInfo}.
+ * @param flags Additional operational flags, 0 or more of
+ * {@link #MOVE_TASK_WITH_HOME}.
+ */
+ public void moveTaskToFront(int taskId, int flags) {
+ moveTaskToFront(taskId, flags, null);
+ }
+
+ /**
* Ask that the task associated with a given task ID be moved to the
* front of the stack, so it is now visible to the user. Requires that
* the caller hold permission {@link android.Manifest.permission#REORDER_TASKS}
@@ -825,10 +839,13 @@ public class ActivityManager {
* {@link RunningTaskInfo} or {@link RecentTaskInfo}.
* @param flags Additional operational flags, 0 or more of
* {@link #MOVE_TASK_WITH_HOME}.
+ * @param options Additional options for the operation, either null or
+ * as per {@link Context#startActivity(Intent, android.os.Bundle)
+ * Context.startActivity(Intent, Bundle)}.
*/
- public void moveTaskToFront(int taskId, int flags) {
+ public void moveTaskToFront(int taskId, int flags, Bundle options) {
try {
- ActivityManagerNative.getDefault().moveTaskToFront(taskId, flags);
+ ActivityManagerNative.getDefault().moveTaskToFront(taskId, flags, options);
} catch (RemoteException e) {
// System dead, we will be dead too soon!
}
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index a3cc352..c000bdf 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -510,7 +510,9 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
data.enforceInterface(IActivityManager.descriptor);
int task = data.readInt();
int fl = data.readInt();
- moveTaskToFront(task, fl);
+ Bundle options = data.readInt() != 0
+ ? Bundle.CREATOR.createFromParcel(data) : null;
+ moveTaskToFront(task, fl, options);
reply.writeNoException();
return true;
}
@@ -2134,13 +2136,19 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle();
return list;
}
- public void moveTaskToFront(int task, int flags) throws RemoteException
+ public void moveTaskToFront(int task, int flags, Bundle options) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(task);
data.writeInt(flags);
+ if (options != null) {
+ data.writeInt(1);
+ options.writeToParcel(data, 0);
+ } else {
+ data.writeInt(0);
+ }
mRemote.transact(MOVE_TASK_TO_FRONT_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 03bc338..c637df0 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -17,7 +17,13 @@
package android.app;
import android.content.Context;
+import android.graphics.Bitmap;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.IRemoteCallback;
+import android.os.Message;
+import android.os.RemoteException;
+import android.view.View;
/**
* Helper class for building an options Bundle that can be used with
@@ -32,6 +38,12 @@ public class ActivityOptions {
public static final String KEY_PACKAGE_NAME = "android:packageName";
/**
+ * Type of animation that arguments specify.
+ * @hide
+ */
+ public static final String KEY_ANIM_TYPE = "android:animType";
+
+ /**
* Custom enter animation resource ID.
* @hide
*/
@@ -43,10 +55,45 @@ public class ActivityOptions {
*/
public static final String KEY_ANIM_EXIT_RES_ID = "android:animExitRes";
+ /**
+ * Bitmap for thumbnail animation.
+ * @hide
+ */
+ public static final String KEY_ANIM_THUMBNAIL = "android:animThumbnail";
+
+ /**
+ * Start X position of thumbnail animation.
+ * @hide
+ */
+ public static final String KEY_ANIM_START_X = "android:animStartX";
+
+ /**
+ * Start Y position of thumbnail animation.
+ * @hide
+ */
+ public static final String KEY_ANIM_START_Y = "android:animStartY";
+
+ /**
+ * Callback for when animation is started.
+ * @hide
+ */
+ public static final String KEY_ANIM_START_LISTENER = "android:animStartListener";
+
+ /** @hide */
+ public static final int ANIM_NONE = 0;
+ /** @hide */
+ public static final int ANIM_CUSTOM = 1;
+ /** @hide */
+ public static final int ANIM_THUMBNAIL = 2;
+
private String mPackageName;
- private boolean mIsCustomAnimation;
+ private int mAnimationType = ANIM_NONE;
private int mCustomEnterResId;
private int mCustomExitResId;
+ private Bitmap mThumbnail;
+ private int mStartX;
+ private int mStartY;
+ private IRemoteCallback mAnimationStartedListener;
/**
* Create an ActivityOptions specifying a custom animation to run when
@@ -65,22 +112,79 @@ public class ActivityOptions {
int enterResId, int exitResId) {
ActivityOptions opts = new ActivityOptions();
opts.mPackageName = context.getPackageName();
- opts.mIsCustomAnimation = true;
+ opts.mAnimationType = ANIM_CUSTOM;
opts.mCustomEnterResId = enterResId;
opts.mCustomExitResId = exitResId;
return opts;
}
+ /**
+ * Callback for use with {@link ActivityOptions#makeThumbnailScaleUpAnimation}
+ * to find out when the given animation has started running.
+ */
+ public interface OnAnimationStartedListener {
+ void onAnimationStarted();
+ }
+
+ /**
+ * Create an ActivityOptions specifying an animation where a thumbnail
+ * is scaled from a given position to the new activity window that is
+ * being started.
+ *
+ * @param source The View that this thumbnail is animating from. This
+ * defines the coordinate space for <var>startX</var> and <var>startY</var>.
+ * @param thumbnail The bitmap that will be shown as the initial thumbnail
+ * of the animation.
+ * @param startX The x starting location of the bitmap, in screen coordiantes.
+ * @param startY The y starting location of the bitmap, in screen coordinates.
+ * @param listener Optional OnAnimationStartedListener to find out when the
+ * requested animation has started running. If for some reason the animation
+ * is not executed, the callback will happen immediately.
+ * @return Returns a new ActivityOptions object that you can use to
+ * supply these options as the options Bundle when starting an activity.
+ */
+ public static ActivityOptions makeThumbnailScaleUpAnimation(View source,
+ Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) {
+ ActivityOptions opts = new ActivityOptions();
+ opts.mPackageName = source.getContext().getPackageName();
+ opts.mAnimationType = ANIM_THUMBNAIL;
+ opts.mThumbnail = thumbnail;
+ int[] pts = new int[2];
+ source.getLocationOnScreen(pts);
+ opts.mStartX = pts[0] + startX;
+ opts.mStartY = pts[1] + startY;
+ if (listener != null) {
+ final Handler h = source.getHandler();
+ final OnAnimationStartedListener finalListener = listener;
+ opts.mAnimationStartedListener = new IRemoteCallback.Stub() {
+ @Override public void sendResult(Bundle data) throws RemoteException {
+ h.post(new Runnable() {
+ @Override public void run() {
+ finalListener.onAnimationStarted();
+ }
+ });
+ }
+ };
+ }
+ return opts;
+ }
+
private ActivityOptions() {
}
/** @hide */
public ActivityOptions(Bundle opts) {
mPackageName = opts.getString(KEY_PACKAGE_NAME);
- if (opts.containsKey(KEY_ANIM_ENTER_RES_ID)) {
- mIsCustomAnimation = true;
+ mAnimationType = opts.getInt(KEY_ANIM_TYPE);
+ if (mAnimationType == ANIM_CUSTOM) {
mCustomEnterResId = opts.getInt(KEY_ANIM_ENTER_RES_ID, 0);
mCustomExitResId = opts.getInt(KEY_ANIM_EXIT_RES_ID, 0);
+ } else if (mAnimationType == ANIM_THUMBNAIL) {
+ mThumbnail = (Bitmap)opts.getParcelable(KEY_ANIM_THUMBNAIL);
+ mStartX = opts.getInt(KEY_ANIM_START_X, 0);
+ mStartY = opts.getInt(KEY_ANIM_START_Y, 0);
+ mAnimationStartedListener = IRemoteCallback.Stub.asInterface(
+ opts.getIBinder(KEY_ANIM_START_LISTENER));
}
}
@@ -90,8 +194,8 @@ public class ActivityOptions {
}
/** @hide */
- public boolean isCustomAnimation() {
- return mIsCustomAnimation;
+ public int getAnimationType() {
+ return mAnimationType;
}
/** @hide */
@@ -104,6 +208,43 @@ public class ActivityOptions {
return mCustomExitResId;
}
+ /** @hide */
+ public Bitmap getThumbnail() {
+ return mThumbnail;
+ }
+
+ /** @hide */
+ public int getStartX() {
+ return mStartX;
+ }
+
+ /** @hide */
+ public int getStartY() {
+ return mStartY;
+ }
+
+ /** @hide */
+ public IRemoteCallback getOnAnimationStartListener() {
+ return mAnimationStartedListener;
+ }
+
+ /** @hide */
+ public void abort() {
+ if (mAnimationStartedListener != null) {
+ try {
+ mAnimationStartedListener.sendResult(null);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ /** @hide */
+ public static void abort(Bundle options) {
+ if (options != null) {
+ (new ActivityOptions(options)).abort();
+ }
+ }
+
/**
* Join the values in <var>otherOptions</var> in to this one. Any values
* defined in <var>otherOptions</var> replace those in the base options.
@@ -112,10 +253,27 @@ public class ActivityOptions {
if (otherOptions.mPackageName != null) {
mPackageName = otherOptions.mPackageName;
}
- if (otherOptions.mIsCustomAnimation) {
- mIsCustomAnimation = true;
- mCustomEnterResId = otherOptions.mCustomEnterResId;
- mCustomExitResId = otherOptions.mCustomExitResId;
+ switch (otherOptions.mAnimationType) {
+ case ANIM_CUSTOM:
+ mAnimationType = otherOptions.mAnimationType;
+ mCustomEnterResId = otherOptions.mCustomEnterResId;
+ mCustomExitResId = otherOptions.mCustomExitResId;
+ mThumbnail = null;
+ mAnimationStartedListener = null;
+ break;
+ case ANIM_THUMBNAIL:
+ mAnimationType = otherOptions.mAnimationType;
+ mThumbnail = otherOptions.mThumbnail;
+ mStartX = otherOptions.mStartX;
+ mStartY = otherOptions.mStartY;
+ if (otherOptions.mAnimationStartedListener != null) {
+ try {
+ otherOptions.mAnimationStartedListener.sendResult(null);
+ } catch (RemoteException e) {
+ }
+ }
+ mAnimationStartedListener = otherOptions.mAnimationStartedListener;
+ break;
}
}
@@ -132,9 +290,19 @@ public class ActivityOptions {
if (mPackageName != null) {
b.putString(KEY_PACKAGE_NAME, mPackageName);
}
- if (mIsCustomAnimation) {
- b.putInt(KEY_ANIM_ENTER_RES_ID, mCustomEnterResId);
- b.putInt(KEY_ANIM_EXIT_RES_ID, mCustomExitResId);
+ switch (mAnimationType) {
+ case ANIM_CUSTOM:
+ b.putInt(KEY_ANIM_TYPE, mAnimationType);
+ b.putInt(KEY_ANIM_ENTER_RES_ID, mCustomEnterResId);
+ b.putInt(KEY_ANIM_EXIT_RES_ID, mCustomExitResId);
+ break;
+ case ANIM_THUMBNAIL:
+ b.putInt(KEY_ANIM_TYPE, mAnimationType);
+ 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
+ != null ? mAnimationStartedListener.asBinder() : null);
}
return b;
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 31066b5..a809cc1 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -105,7 +105,7 @@ public interface IActivityManager extends IInterface {
public List getServices(int maxNum, int flags) throws RemoteException;
public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState()
throws RemoteException;
- public void moveTaskToFront(int task, int flags) throws RemoteException;
+ public void moveTaskToFront(int task, int flags, Bundle options) throws RemoteException;
public void moveTaskToBack(int task) throws RemoteException;
public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) throws RemoteException;
public void moveTaskBackwards(int task) throws RemoteException;
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index c54d09e..14cd48f 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -89,6 +89,8 @@ interface IWindowManager
void prepareAppTransition(int transit, boolean alwaysKeepCurrent);
int getPendingAppTransition();
void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim);
+ void overridePendingAppTransitionThumb(in Bitmap srcThumb, int startX, int startY,
+ IRemoteCallback startedCallback);
void executeAppTransition();
void setAppStartingWindow(IBinder token, String pkg, int theme,
in CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index dc8c71b..d92ebcd 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -875,7 +875,7 @@ public abstract class Animation implements Cloneable {
* otherwise.
*
* @param currentTime Where we are in the animation. This is wall clock time.
- * @param outTransformation A tranformation object that is provided by the
+ * @param outTransformation A transformation object that is provided by the
* caller and will be filled in by the animation.
* @param scale Scaling factor to apply to any inputs to the transform operation, such
* pivot points being rotated or scaled around.
diff --git a/core/java/android/view/animation/Transformation.java b/core/java/android/view/animation/Transformation.java
index cf210c8..e8c1d23 100644
--- a/core/java/android/view/animation/Transformation.java
+++ b/core/java/android/view/animation/Transformation.java
@@ -112,6 +112,16 @@ public class Transformation {
}
/**
+ * Like {@link #compose(Transformation)} but does this.postConcat(t) of
+ * the transformation matrix.
+ * @hide
+ */
+ public void postCompose(Transformation t) {
+ mAlpha *= t.getAlpha();
+ mMatrix.postConcat(t.getMatrix());
+ }
+
+ /**
* @return The 3x3 Matrix representing the trnasformation to apply to the
* coordinates of the object being animated
*/
diff --git a/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
index ef7e651..3d46cdd 100644
--- a/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
+++ b/core/java/com/android/internal/app/HeavyWeightSwitcherActivity.java
@@ -123,7 +123,7 @@ public class HeavyWeightSwitcherActivity extends Activity {
private OnClickListener mSwitchOldListener = new OnClickListener() {
public void onClick(View v) {
try {
- ActivityManagerNative.getDefault().moveTaskToFront(mCurTask, 0);
+ ActivityManagerNative.getDefault().moveTaskToFront(mCurTask, 0, null);
} catch (RemoteException e) {
}
finish();
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index 66cb32c..5f314d6 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -19,6 +19,7 @@ package com.android.systemui.recent;
import android.animation.Animator;
import android.animation.LayoutTransition;
import android.app.ActivityManager;
+import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
@@ -656,22 +657,33 @@ public class RecentsPanelView extends FrameLayout implements OnItemClickListener
}
public void handleOnClick(View view) {
- TaskDescription ad = ((ViewHolder) view.getTag()).taskDescription;
+ ViewHolder holder = (ViewHolder)view.getTag();
+ TaskDescription ad = holder.taskDescription;
final Context context = view.getContext();
final ActivityManager am = (ActivityManager)
context.getSystemService(Context.ACTIVITY_SERVICE);
+ holder.thumbnailViewImage.setDrawingCacheEnabled(true);
+ Bitmap bm = holder.thumbnailViewImage.getDrawingCache();
+ ActivityOptions opts = ActivityOptions.makeThumbnailScaleUpAnimation(
+ holder.thumbnailViewImage, bm, 0, 0,
+ new ActivityOptions.OnAnimationStartedListener() {
+ @Override public void onAnimationStarted() {
+ hide(true);
+ }
+ });
if (ad.taskId >= 0) {
// This is an active task; it should just go to the foreground.
- am.moveTaskToFront(ad.taskId, ActivityManager.MOVE_TASK_WITH_HOME);
+ am.moveTaskToFront(ad.taskId, ActivityManager.MOVE_TASK_WITH_HOME,
+ opts.toBundle());
} else {
Intent intent = ad.intent;
intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
| Intent.FLAG_ACTIVITY_TASK_ON_HOME
| Intent.FLAG_ACTIVITY_NEW_TASK);
if (DEBUG) Log.v(TAG, "Starting activity " + intent);
- context.startActivity(intent);
+ context.startActivity(intent, opts.toBundle());
}
- hide(true);
+ holder.thumbnailViewImage.setDrawingCacheEnabled(false);
}
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index b422678..84fcd5e 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -34,6 +34,7 @@ import dalvik.system.Zygote;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
+import android.app.ActivityOptions;
import android.app.ActivityThread;
import android.app.AlertDialog;
import android.app.AppGlobals;
@@ -2353,10 +2354,12 @@ public final class ActivityManagerService extends ActivityManagerNative
synchronized (this) {
ActivityRecord r = mMainStack.isInStackLocked(callingActivity);
if (r == null) {
+ ActivityOptions.abort(options);
return false;
}
if (r.app == null || r.app.thread == null) {
// The caller is not running... d'oh!
+ ActivityOptions.abort(options);
return false;
}
intent = new Intent(intent);
@@ -2393,6 +2396,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (aInfo == null) {
// Nobody who is next!
+ ActivityOptions.abort(options);
return false;
}
@@ -2422,8 +2426,6 @@ public final class ActivityManagerService extends ActivityManagerNative
}
final long origId = Binder.clearCallingIdentity();
- // XXX we are not dealing with propagating grantedUriPermissions...
- // those are not yet exposed to user code, so there is no need.
int res = mMainStack.startActivityLocked(r.app.thread, intent,
r.resolvedType, aInfo, resultTo != null ? resultTo.appToken : null,
resultWho, requestCode, -1, r.launchedFromUid, 0,
@@ -3653,7 +3655,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
lastTask = r.task;
if (r.stack.finishActivityLocked(r, i, Activity.RESULT_CANCELED,
- null, "force-stop")) {
+ null, "force-stop", true)) {
i--;
}
}
@@ -5686,13 +5688,14 @@ public final class ActivityManagerService extends ActivityManagerNative
/**
* TODO: Add mController hook
*/
- public void moveTaskToFront(int task, int flags) {
+ public void moveTaskToFront(int task, int flags, Bundle options) {
enforceCallingPermission(android.Manifest.permission.REORDER_TASKS,
"moveTaskToFront()");
synchronized(this) {
if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
Binder.getCallingUid(), "Task to front")) {
+ ActivityOptions.abort(options);
return;
}
final long origId = Binder.clearCallingIdentity();
@@ -5707,7 +5710,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// we'll just move the home task to the top first.
mMainStack.moveHomeToFrontLocked();
}
- mMainStack.moveTaskToFrontLocked(tr, null);
+ mMainStack.moveTaskToFrontLocked(tr, null, options);
return;
}
for (int i=mMainStack.mHistory.size()-1; i>=0; i--) {
@@ -5721,13 +5724,14 @@ public final class ActivityManagerService extends ActivityManagerNative
// we'll just move the home task to the top first.
mMainStack.moveHomeToFrontLocked();
}
- mMainStack.moveTaskToFrontLocked(hr.task, null);
+ mMainStack.moveTaskToFrontLocked(hr.task, null, options);
return;
}
}
} finally {
Binder.restoreCallingIdentity(origId);
}
+ ActivityOptions.abort(options);
}
}
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 53cb2b0..d60ff2b 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -26,7 +26,6 @@ import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.res.CompatibilityInfo;
-import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.os.Build;
@@ -542,24 +541,38 @@ final class ActivityRecord {
void updateOptionsLocked(Bundle options) {
if (options != null) {
+ if (pendingOptions != null) {
+ pendingOptions.abort();
+ }
pendingOptions = new ActivityOptions(options);
}
}
void applyOptionsLocked() {
if (pendingOptions != null) {
- if (pendingOptions.isCustomAnimation()) {
- service.mWindowManager.overridePendingAppTransition(
- pendingOptions.getPackageName(),
- pendingOptions.getCustomEnterResId(),
- pendingOptions.getCustomExitResId());
+ switch (pendingOptions.getAnimationType()) {
+ case ActivityOptions.ANIM_CUSTOM:
+ service.mWindowManager.overridePendingAppTransition(
+ pendingOptions.getPackageName(),
+ pendingOptions.getCustomEnterResId(),
+ pendingOptions.getCustomExitResId());
+ break;
+ case ActivityOptions.ANIM_THUMBNAIL:
+ service.mWindowManager.overridePendingAppTransitionThumb(
+ pendingOptions.getThumbnail(),
+ pendingOptions.getStartX(), pendingOptions.getStartY(),
+ pendingOptions.getOnAnimationStartListener());
+ break;
}
pendingOptions = null;
}
}
void clearOptionsLocked() {
- pendingOptions = null;
+ if (pendingOptions != null) {
+ pendingOptions.abort();
+ pendingOptions = null;
+ }
}
void removeUriPermissionsLocked() {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 48b4f4f..a01ed25 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -1695,6 +1695,7 @@ final class ActivityStack {
if (VALIDATE_TOKENS) {
validateAppTokensLocked();
}
+ ActivityOptions.abort(options);
return;
}
break;
@@ -1797,6 +1798,7 @@ final class ActivityStack {
// because there is nothing for it to animate on top of.
mService.mWindowManager.addAppToken(addPos, r.appToken, r.task.taskId,
r.info.screenOrientation, r.fullscreen);
+ ActivityOptions.abort(options);
}
if (VALIDATE_TOKENS) {
validateAppTokensLocked();
@@ -2344,6 +2346,7 @@ final class ActivityStack {
// Transfer the result target from the source activity to the new
// one being started, including any failures.
if (requestCode >= 0) {
+ ActivityOptions.abort(options);
return ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
}
resultRecord = sourceRecord.resultTo;
@@ -2375,6 +2378,7 @@ final class ActivityStack {
Activity.RESULT_CANCELED, null);
}
mDismissKeyguardOnNextActivity = false;
+ ActivityOptions.abort(options);
return err;
}
@@ -2425,6 +2429,7 @@ final class ActivityStack {
// We pretend to the caller that it was really started, but
// they will just get a cancel result.
mDismissKeyguardOnNextActivity = false;
+ ActivityOptions.abort(options);
return ActivityManager.START_SUCCESS;
}
}
@@ -2447,6 +2452,7 @@ final class ActivityStack {
pal.startFlags = startFlags;
mService.mPendingActivityLaunches.add(pal);
mDismissKeyguardOnNextActivity = false;
+ ActivityOptions.abort(options);
return ActivityManager.START_SWITCHES_CANCELED;
}
}
@@ -2601,8 +2607,7 @@ final class ActivityStack {
// We really do want to push this one into the
// user's face, right now.
moveHomeToFrontFromLaunchLocked(launchFlags);
- r.updateOptionsLocked(options);
- moveTaskToFrontLocked(taskTop.task, r);
+ moveTaskToFrontLocked(taskTop.task, r, options);
}
}
// If the caller has requested that the target task be
@@ -2618,6 +2623,7 @@ final class ActivityStack {
if (doResume) {
resumeTopActivityLocked(null);
}
+ ActivityOptions.abort(options);
return ActivityManager.START_RETURN_INTENT_TO_CALLER;
}
if ((launchFlags &
@@ -2705,6 +2711,7 @@ final class ActivityStack {
if (doResume) {
resumeTopActivityLocked(null);
}
+ ActivityOptions.abort(options);
return ActivityManager.START_TASK_TO_FRONT;
}
}
@@ -2734,6 +2741,7 @@ final class ActivityStack {
if (doResume) {
resumeTopActivityLocked(null);
}
+ ActivityOptions.abort(options);
if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
// We don't need to start a new activity, and
// the client said not to do anything if that
@@ -2753,6 +2761,7 @@ final class ActivityStack {
r.resultTo, r.resultWho, r.requestCode,
Activity.RESULT_CANCELED, null);
}
+ ActivityOptions.abort(options);
return ActivityManager.START_CLASS_NOT_FOUND;
}
@@ -2794,6 +2803,7 @@ final class ActivityStack {
if (doResume) {
resumeTopActivityLocked(null);
}
+ ActivityOptions.abort(options);
return ActivityManager.START_DELIVERED_TO_TOP;
}
} else if (!addingToTask &&
@@ -2948,6 +2958,7 @@ final class ActivityStack {
Slog.w(TAG, "Unable to find app for caller " + caller
+ " (pid=" + realCallingPid + ") when starting: "
+ intent.toString());
+ ActivityOptions.abort(options);
return ActivityManager.START_PERMISSION_DENIED;
}
}
@@ -3480,6 +3491,15 @@ final class ActivityStack {
*/
final boolean finishActivityLocked(ActivityRecord r, int index,
int resultCode, Intent resultData, String reason) {
+ return finishActivityLocked(r, index, resultCode, resultData, reason, false);
+ }
+
+ /**
+ * @return Returns true if this activity has been removed from the history
+ * list, or false if it is still in the list and will be removed later.
+ */
+ final boolean finishActivityLocked(ActivityRecord r, int index,
+ int resultCode, Intent resultData, String reason, boolean immediate) {
if (r.finishing) {
Slog.w(TAG, "Duplicate finish request for " + r);
return false;
@@ -3521,7 +3541,10 @@ final class ActivityStack {
mService.mCancelledThumbnails.add(r);
}
- if (mResumedActivity == r) {
+ if (immediate) {
+ return finishCurrentActivityLocked(r, index,
+ FINISH_IMMEDIATELY) == null;
+ } else if (mResumedActivity == r) {
boolean endTask = index <= 0
|| (mHistory.get(index-1)).task != r.task;
if (DEBUG_TRANSITION) Slog.v(TAG,
@@ -3887,12 +3910,12 @@ final class ActivityStack {
}
}
if (homeTask != null) {
- moveTaskToFrontLocked(homeTask, null);
+ moveTaskToFrontLocked(homeTask, null, null);
}
}
- final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord reason) {
+ final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord reason, Bundle options) {
if (DEBUG_SWITCH) Slog.v(TAG, "moveTaskToFront: " + tr);
final int task = tr.taskId;
@@ -3900,6 +3923,7 @@ final class ActivityStack {
if (top < 0 || (mHistory.get(top)).task.taskId == task) {
// nothing to do!
+ ActivityOptions.abort(options);
return;
}
@@ -3941,7 +3965,16 @@ final class ActivityStack {
if (r != null) {
mNoAnimActivities.add(r);
}
+ ActivityOptions.abort(options);
} else {
+ if (options != null) {
+ ActivityRecord r = topRunningActivityLocked(null);
+ if (r != null && r.state != ActivityState.RESUMED) {
+ r.updateOptionsLocked(options);
+ } else {
+ ActivityOptions.abort(options);
+ }
+ }
mService.mWindowManager.prepareAppTransition(
WindowManagerPolicy.TRANSIT_TASK_TO_FRONT, false);
}
diff --git a/services/java/com/android/server/wm/AppWindowToken.java b/services/java/com/android/server/wm/AppWindowToken.java
index 0e110be..c29ef3f 100644
--- a/services/java/com/android/server/wm/AppWindowToken.java
+++ b/services/java/com/android/server/wm/AppWindowToken.java
@@ -21,10 +21,12 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import com.android.server.wm.WindowManagerService.H;
import android.content.pm.ActivityInfo;
+import android.graphics.Matrix;
import android.os.Message;
import android.os.RemoteException;
import android.util.Slog;
import android.view.IApplicationToken;
+import android.view.Surface;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManagerPolicy;
@@ -90,6 +92,7 @@ class AppWindowToken extends WindowToken {
boolean animating;
Animation animation;
+ boolean animInitialized;
boolean hasTransformation;
final Transformation transformation = new Transformation();
@@ -105,6 +108,15 @@ class AppWindowToken extends WindowToken {
boolean startingMoved;
boolean firstWindowDrawn;
+ // Special surface for thumbnail animation.
+ Surface thumbnail;
+ int thumbnailTransactionSeq;
+ int thumbnailX;
+ int thumbnailY;
+ int thumbnailLayer;
+ Animation thumbnailAnimation;
+ final Transformation thumbnailTransformation = new Transformation();
+
// Input application handle used by the input dispatcher.
final InputApplicationHandle mInputApplicationHandle;
@@ -116,11 +128,12 @@ class AppWindowToken extends WindowToken {
mInputApplicationHandle = new InputApplicationHandle(this);
}
- public void setAnimation(Animation anim) {
+ public void setAnimation(Animation anim, boolean initialized) {
if (WindowManagerService.localLOGV) Slog.v(
WindowManagerService.TAG, "Setting animation in " + this + ": " + anim);
animation = anim;
animating = false;
+ animInitialized = initialized;
anim.restrictDuration(WindowManagerService.MAX_ANIMATION_DURATION);
anim.scaleCurrentDuration(service.mTransitionAnimationScale);
int zorder = anim.getZAdjustment();
@@ -146,6 +159,7 @@ class AppWindowToken extends WindowToken {
if (WindowManagerService.localLOGV) Slog.v(
WindowManagerService.TAG, "Setting dummy animation in " + this);
animation = WindowManagerService.sDummyAnimation;
+ animInitialized = false;
}
}
@@ -153,15 +167,28 @@ class AppWindowToken extends WindowToken {
if (animation != null) {
animation = null;
animating = true;
+ animInitialized = false;
+ }
+ clearThumbnail();
+ }
+
+ public void clearThumbnail() {
+ if (thumbnail != null) {
+ thumbnail.destroy();
+ thumbnail = null;
}
}
void updateLayers() {
final int N = allAppWindows.size();
final int adj = animLayerAdjustment;
+ thumbnailLayer = -1;
for (int i=0; i<N; i++) {
WindowState w = allAppWindows.get(i);
w.mAnimLayer = w.mLayer + adj;
+ if (w.mAnimLayer > thumbnailLayer) {
+ thumbnailLayer = w.mAnimLayer;
+ }
if (WindowManagerService.DEBUG_LAYERS) Slog.v(WindowManagerService.TAG, "Updating layer " + w + ": "
+ w.mAnimLayer);
if (w == service.mInputMethodTarget && !service.mInputMethodTargetWaitingAnim) {
@@ -203,6 +230,38 @@ class AppWindowToken extends WindowToken {
return isAnimating;
}
+ private void stepThumbnailAnimation(long currentTime) {
+ thumbnailTransformation.clear();
+ thumbnailAnimation.getTransformation(currentTime, thumbnailTransformation);
+ thumbnailTransformation.getMatrix().preTranslate(thumbnailX, thumbnailY);
+ final boolean screenAnimation = service.mAnimator.mScreenRotationAnimation != null
+ && service.mAnimator.mScreenRotationAnimation.isAnimating();
+ if (screenAnimation) {
+ thumbnailTransformation.postCompose(
+ service.mAnimator.mScreenRotationAnimation.getEnterTransformation());
+ }
+ // cache often used attributes locally
+ final float tmpFloats[] = service.mTmpFloats;
+ thumbnailTransformation.getMatrix().getValues(tmpFloats);
+ if (WindowManagerService.SHOW_TRANSACTIONS) service.logSurface(thumbnail,
+ "thumbnail", "POS " + tmpFloats[Matrix.MTRANS_X]
+ + ", " + tmpFloats[Matrix.MTRANS_Y], null);
+ thumbnail.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]);
+ if (WindowManagerService.SHOW_TRANSACTIONS) service.logSurface(thumbnail,
+ "thumbnail", "alpha=" + thumbnailTransformation.getAlpha()
+ + " layer=" + thumbnailLayer
+ + " matrix=[" + tmpFloats[Matrix.MSCALE_X]
+ + "," + tmpFloats[Matrix.MSKEW_Y]
+ + "][" + tmpFloats[Matrix.MSKEW_X]
+ + "," + tmpFloats[Matrix.MSCALE_Y] + "]", null);
+ thumbnail.setAlpha(thumbnailTransformation.getAlpha());
+ // The thumbnail is layered below the window immediately above this
+ // token's anim layer.
+ thumbnail.setLayer(thumbnailLayer + WindowManagerService.WINDOW_LAYER_MULTIPLIER
+ - WindowManagerService.LAYER_OFFSET_THUMBNAIL);
+ thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
+ tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
+ }
private boolean stepAnimation(long currentTime) {
if (animation == null) {
@@ -215,6 +274,7 @@ class AppWindowToken extends WindowToken {
": more=" + more + ", xform=" + transformation);
if (!more) {
animation = null;
+ clearThumbnail();
if (WindowManagerService.DEBUG_ANIM) Slog.v(
WindowManagerService.TAG, "Finished animation in " + this +
" @ " + currentTime);
@@ -243,12 +303,22 @@ class AppWindowToken extends WindowToken {
" @ " + currentTime + ": dw=" + dw + " dh=" + dh
+ " scale=" + service.mTransitionAnimationScale
+ " allDrawn=" + allDrawn + " animating=" + animating);
- animation.initialize(dw, dh, dw, dh);
+ if (!animInitialized) {
+ animation.initialize(dw, dh, dw, dh);
+ }
animation.setStartTime(currentTime);
animating = true;
+ if (thumbnail != null) {
+ thumbnail.show();
+ thumbnailAnimation.setStartTime(currentTime);
+ }
}
if (stepAnimation(currentTime)) {
- // we're done!
+ // animation isn't over, step any thumbnail and that's
+ // it for now.
+ if (thumbnail != null) {
+ stepThumbnailAnimation(currentTime);
+ }
return true;
}
}
@@ -440,6 +510,15 @@ class AppWindowToken extends WindowToken {
pw.print(" startingDisplayed="); pw.print(startingDisplayed);
pw.print(" startingMoved"); pw.println(startingMoved);
}
+ if (thumbnail != null) {
+ pw.print(prefix); pw.print("thumbnail="); pw.print(thumbnail);
+ pw.print(" x="); pw.print(thumbnailX);
+ pw.print(" y="); pw.print(thumbnailY);
+ pw.print(" layer="); pw.println(thumbnailLayer);
+ pw.print(prefix); pw.print("thumbnailAnimation="); pw.println(thumbnailAnimation);
+ pw.print(prefix); pw.print("thumbnailTransformation=");
+ pw.println(thumbnailTransformation.toShortString());
+ }
}
@Override
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index b3dbee1..058e09e 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -273,6 +273,15 @@ public class WindowAnimator {
w.performShowLocked();
mPendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
}
+ if (atoken != null && atoken.thumbnail != null) {
+ if (atoken.thumbnailTransactionSeq != mTransactionSequence) {
+ atoken.thumbnailTransactionSeq = mTransactionSequence;
+ atoken.thumbnailLayer = 0;
+ }
+ if (atoken.thumbnailLayer < w.mAnimLayer) {
+ atoken.thumbnailLayer = w.mAnimLayer;
+ }
+ }
} // end forall windows
}
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 26367d2..7f74b5f 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -117,8 +117,12 @@ import android.view.WindowManagerImpl;
import android.view.WindowManagerPolicy;
import android.view.WindowManager.LayoutParams;
import android.view.WindowManagerPolicy.FakeWindow;
+import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
+import android.view.animation.AnimationSet;
import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.view.animation.ScaleAnimation;
import android.view.animation.Transformation;
import java.io.BufferedWriter;
@@ -194,6 +198,18 @@ public class WindowManagerService extends IWindowManager.Stub
static final int LAYER_OFFSET_DIM = 1;
/**
+ * Blur surface layer is immediately below dim layer.
+ */
+ static final int LAYER_OFFSET_BLUR = 2;
+
+ /**
+ * Animation thumbnail is as far as possible below the window above
+ * the thumbnail (or in other words as far as possible above the window
+ * below it).
+ */
+ static final int LAYER_OFFSET_THUMBNAIL = WINDOW_LAYER_MULTIPLIER-1;
+
+ /**
* Layer at which to put the rotation freeze snapshot.
*/
static final int FREEZE_LAYER = (TYPE_LAYER_MULTIPLIER * 200) + 1;
@@ -479,8 +495,12 @@ public class WindowManagerService extends IWindowManager.Stub
// made visible or hidden at the next transition.
int mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET;
String mNextAppTransitionPackage;
+ Bitmap mNextAppTransitionThumbnail;
+ IRemoteCallback mNextAppTransitionCallback;
int mNextAppTransitionEnter;
int mNextAppTransitionExit;
+ int mNextAppTransitionStartX;
+ int mNextAppTransitionStartY;
boolean mAppTransitionReady = false;
boolean mAppTransitionRunning = false;
boolean mAppTransitionTimeout = false;
@@ -2441,6 +2461,15 @@ public class WindowManagerService extends IWindowManager.Stub
Slog.i(TAG, str);
}
}
+
+ static void logSurface(Surface s, String title, String msg, RuntimeException where) {
+ String str = " SURFACE " + s + ": " + msg + " / " + title;
+ if (where != null) {
+ Slog.i(TAG, str, where);
+ } else {
+ Slog.i(TAG, str);
+ }
+ }
void setTransparentRegionWindow(Session session, IWindow client, Region region) {
long origId = Binder.clearCallingIdentity();
@@ -3081,6 +3110,63 @@ public class WindowManagerService extends IWindowManager.Stub
return null;
}
+ private Animation createThumbnailAnimationLocked(int transit,
+ boolean enter, boolean thumb) {
+ Animation a;
+ final float thumbWidth = mNextAppTransitionThumbnail.getWidth();
+ final float thumbHeight = mNextAppTransitionThumbnail.getHeight();
+ // Pick the desired duration. If this is an inter-activity transition,
+ // it is the standard duration for that. Otherwise we use the longer
+ // task transition duration.
+ int duration;
+ switch (transit) {
+ case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN:
+ case WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE:
+ duration = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_shortAnimTime);
+ break;
+ default:
+ duration = 500;
+ break;
+
+ }
+ if (thumb) {
+ // Animation for zooming thumbnail from its initial size to
+ // filling the screen.
+ Animation scale = new ScaleAnimation(
+ 1, mAppDisplayWidth/thumbWidth,
+ 1, mAppDisplayHeight/thumbHeight,
+ mNextAppTransitionStartX + thumbWidth/2,
+ mNextAppTransitionStartY + thumbHeight/2);
+ AnimationSet set = new AnimationSet(true);
+ Animation alpha = new AlphaAnimation(1, 0);
+ scale.setDuration(duration);
+ set.addAnimation(scale);
+ alpha.setDuration(duration);
+ set.addAnimation(alpha);
+ a = set;
+ } else if (enter) {
+ // Entering app zooms out from the center of the thumbnail.
+ a = new ScaleAnimation(
+ thumbWidth/mAppDisplayWidth, 1,
+ thumbHeight/mAppDisplayHeight, 1,
+ mNextAppTransitionStartX + thumbWidth/2,
+ mNextAppTransitionStartY + thumbHeight/2);
+ a.setDuration(duration);
+ } else {
+ // Exiting app just holds in place.
+ a = new AlphaAnimation(1, 1);
+ a.setDuration(duration);
+ }
+ a.setFillAfter(true);
+ final Interpolator interpolator = AnimationUtils.loadInterpolator(mContext,
+ com.android.internal.R.interpolator.decelerate_quint);
+ a.setInterpolator(interpolator);
+ a.initialize(mAppDisplayWidth, mAppDisplayHeight,
+ mAppDisplayWidth, mAppDisplayHeight);
+ return a;
+ }
+
private boolean applyAnimationLocked(AppWindowToken wtoken,
WindowManager.LayoutParams lp, int transit, boolean enter) {
// Only apply an animation if the display isn't frozen. If it is
@@ -3089,7 +3175,11 @@ public class WindowManagerService extends IWindowManager.Stub
// is running.
if (okToDisplay()) {
Animation a;
- if (mNextAppTransitionPackage != null) {
+ boolean initialized = false;
+ if (mNextAppTransitionThumbnail != null) {
+ a = createThumbnailAnimationLocked(transit, enter, false);
+ initialized = true;
+ } else if (mNextAppTransitionPackage != null) {
a = loadAnimation(mNextAppTransitionPackage, enter ?
mNextAppTransitionEnter : mNextAppTransitionExit);
} else {
@@ -3161,7 +3251,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
Slog.v(TAG, "Loaded animation " + a + " for " + wtoken, e);
}
- wtoken.setAnimation(a);
+ wtoken.setAnimation(a, initialized);
}
} else {
wtoken.clearAnimation();
@@ -3689,11 +3779,23 @@ public class WindowManagerService extends IWindowManager.Stub
int enterAnim, int exitAnim) {
if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
mNextAppTransitionPackage = packageName;
+ mNextAppTransitionThumbnail = null;
mNextAppTransitionEnter = enterAnim;
mNextAppTransitionExit = exitAnim;
}
}
+ public void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX,
+ int startY, IRemoteCallback startedCallback) {
+ if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
+ mNextAppTransitionPackage = null;
+ mNextAppTransitionThumbnail = srcThumb;
+ mNextAppTransitionStartX = startX;
+ mNextAppTransitionStartY = startY;
+ mNextAppTransitionCallback = startedCallback;
+ }
+ }
+
public void executeAppTransition() {
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
"executeAppTransition()")) {
@@ -3837,6 +3939,19 @@ public class WindowManagerService extends IWindowManager.Stub
mH.sendMessageAtFrontOfQueue(m);
return;
}
+ if (ttoken.thumbnail != null) {
+ // The old token is animating with a thumbnail, transfer
+ // that to the new token.
+ if (wtoken.thumbnail != null) {
+ wtoken.thumbnail.destroy();
+ }
+ wtoken.thumbnail = ttoken.thumbnail;
+ wtoken.thumbnailX = ttoken.thumbnailX;
+ wtoken.thumbnailY = ttoken.thumbnailY;
+ wtoken.thumbnailLayer = ttoken.thumbnailLayer;
+ wtoken.thumbnailAnimation = ttoken.thumbnailAnimation;
+ ttoken.thumbnail = null;
+ }
}
}
@@ -4232,6 +4347,7 @@ public class WindowManagerService extends IWindowManager.Stub
// Make sure there is no animation running on this token,
// so any windows associated with it will be removed as
// soon as their animations are complete
+ wtoken.clearAnimation();
wtoken.animation = null;
wtoken.animating = false;
}
@@ -7784,11 +7900,15 @@ public class WindowManagerService extends IWindowManager.Stub
animLp = null;
}
+ AppWindowToken topOpeningApp = null;
+ int topOpeningLayer = 0;
+
NN = mOpeningApps.size();
for (i=0; i<NN; i++) {
AppWindowToken wtoken = mOpeningApps.get(i);
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
"Now opening app" + wtoken);
+ wtoken.clearThumbnail();
wtoken.reportedVisible = false;
wtoken.inPendingTransaction = false;
wtoken.animation = null;
@@ -7797,12 +7917,26 @@ public class WindowManagerService extends IWindowManager.Stub
wtoken.updateReportedVisibilityLocked();
wtoken.waitingToShow = false;
mAnimator.mAnimating |= wtoken.showAllWindowsLocked();
+ if (animLp != null) {
+ int layer = -1;
+ for (int j=0; j<wtoken.windows.size(); j++) {
+ WindowState win = wtoken.windows.get(j);
+ if (win.mAnimLayer > layer) {
+ layer = win.mAnimLayer;
+ }
+ }
+ if (topOpeningApp == null || layer > topOpeningLayer) {
+ topOpeningApp = wtoken;
+ topOpeningLayer = layer;
+ }
+ }
}
NN = mClosingApps.size();
for (i=0; i<NN; i++) {
AppWindowToken wtoken = mClosingApps.get(i);
if (DEBUG_APP_TRANSITIONS) Slog.v(TAG,
"Now closing app" + wtoken);
+ wtoken.clearThumbnail();
wtoken.inPendingTransaction = false;
wtoken.animation = null;
setTokenVisibilityLocked(wtoken, animLp, false,
@@ -7815,7 +7949,47 @@ public class WindowManagerService extends IWindowManager.Stub
wtoken.allDrawn = true;
}
+ if (mNextAppTransitionThumbnail != null && topOpeningApp != null
+ && topOpeningApp.animation != null) {
+ // This thumbnail animation is very special, we need to have
+ // an extra surface with the thumbnail included with the animation.
+ Rect dirty = new Rect(0, 0, mNextAppTransitionThumbnail.getWidth(),
+ mNextAppTransitionThumbnail.getHeight());
+ try {
+ Surface surface = new Surface(mFxSession, Process.myPid(),
+ "thumbnail anim", 0, dirty.width(), dirty.height(),
+ PixelFormat.TRANSLUCENT, Surface.HIDDEN);
+ topOpeningApp.thumbnail = surface;
+ if (SHOW_TRANSACTIONS) Slog.i(TAG, " THUMBNAIL "
+ + surface + ": CREATE");
+ Surface drawSurface = new Surface();
+ drawSurface.copyFrom(surface);
+ Canvas c = drawSurface.lockCanvas(dirty);
+ c.drawBitmap(mNextAppTransitionThumbnail, 0, 0, null);
+ drawSurface.unlockCanvasAndPost(c);
+ drawSurface.release();
+ topOpeningApp.thumbnailLayer = topOpeningLayer;
+ Animation anim = createThumbnailAnimationLocked(transit, true, true);
+ topOpeningApp.thumbnailAnimation = anim;
+ anim.restrictDuration(MAX_ANIMATION_DURATION);
+ anim.scaleCurrentDuration(mTransitionAnimationScale);
+ topOpeningApp.thumbnailX = mNextAppTransitionStartX;
+ topOpeningApp.thumbnailY = mNextAppTransitionStartY;
+ } catch (Surface.OutOfResourcesException e) {
+ Slog.e(TAG, "Can't allocate thumbnail surface w=" + dirty.width()
+ + " h=" + dirty.height(), e);
+ topOpeningApp.clearThumbnail();
+ }
+ }
+
mNextAppTransitionPackage = null;
+ mNextAppTransitionThumbnail = null;
+ if (mNextAppTransitionCallback != null) {
+ try {
+ mNextAppTransitionCallback.sendResult(null);
+ } catch (RemoteException e) {
+ }
+ }
mOpeningApps.clear();
mClosingApps.clear();
@@ -8400,6 +8574,7 @@ public class WindowManagerService extends IWindowManager.Stub
// Make sure there is no animation running on this token,
// so any windows associated with it will be removed as
// soon as their animations are complete
+ token.clearAnimation();
token.animation = null;
token.animating = false;
if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG,
@@ -8839,6 +9014,7 @@ public class WindowManagerService extends IWindowManager.Stub
if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
mNextAppTransition = WindowManagerPolicy.TRANSIT_UNSET;
mNextAppTransitionPackage = null;
+ mNextAppTransitionThumbnail = null;
mAppTransitionReady = true;
}
@@ -9399,6 +9575,12 @@ public class WindowManagerService extends IWindowManager.Stub
pw.print(" mNextAppTransitionExit=0x");
pw.print(Integer.toHexString(mNextAppTransitionExit));
}
+ if (mNextAppTransitionThumbnail != null) {
+ pw.print(" mNextAppTransitionThumbnail=");
+ pw.print(mNextAppTransitionThumbnail);
+ pw.print(" mNextAppTransitionStartX="); pw.print(mNextAppTransitionStartX);
+ pw.print(" mNextAppTransitionStartY="); pw.println(mNextAppTransitionStartY);
+ }
pw.print(" mStartingIconInTransition="); pw.print(mStartingIconInTransition);
pw.print(", mSkipAppTransitionAnimation="); pw.println(mSkipAppTransitionAnimation);
}
diff --git a/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java
index 4cacbc4..cceed16 100644
--- a/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/ActivityManagerPermissionTests.java
@@ -39,7 +39,7 @@ public class ActivityManagerPermissionTests extends TestCase {
@SmallTest
public void testREORDER_TASKS() {
try {
- mAm.moveTaskToFront(0, 0);
+ mAm.moveTaskToFront(0, 0, null);
fail("IActivityManager.moveTaskToFront did not throw SecurityException as"
+ " expected");
} catch (SecurityException e) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
index bef2c95..8b1d41a 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
@@ -340,6 +340,12 @@ public class BridgeWindowManager implements IWindowManager {
}
@Override
+ public void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, int startY,
+ IRemoteCallback startedCallback) throws RemoteException {
+ // TODO Auto-generated method stub
+ }
+
+ @Override
public void pauseKeyDispatching(IBinder arg0) throws RemoteException {
// TODO Auto-generated method stub