diff options
author | Dianne Hackborn <hackbod@google.com> | 2012-03-20 11:11:26 -0700 |
---|---|---|
committer | Dianne Hackborn <hackbod@google.com> | 2012-03-23 14:13:13 -0700 |
commit | 8078d8c8a282ca81344febe7256f63b1e805e3aa (patch) | |
tree | 6bfb8ab0a389c2ae344088017bd0ceee1e913660 | |
parent | 3c4da3cad04ca878a4a37fcca3f3e2ff51d03fcb (diff) | |
download | frameworks_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
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 |