OSDN Git Service

Reduce jank during pinned stack animation
authorWale Ogunwale <ogunwale@google.com>
Sat, 6 Feb 2016 21:58:29 +0000 (13:58 -0800)
committerWale Ogunwale <ogunwale@google.com>
Tue, 9 Feb 2016 20:48:11 +0000 (12:48 -0800)
- Don’t launch Pip overlay activities during pinned stack animation.
This causes extra CPU load and takes a way resources from the running
animation.
- Finish Pip overlay activities before starting pinned stack resize
animation. Reduces the amount of work the system needs to do to keep
the overlays in-sync with the other activities in the pinned stack.
- Use AM.resizeStack with null bounds to take Pip to fullscreen so that
we can animate the bounds changed.
- Also, fixed Activity.enterPictureInPicture API to animate the transition
if Pip is entered from the app instead of Pip manager.

Bug: 25672053
Change-Id: I82399c10f1b8c675ea3861ba973dc8ecfbfbe50f

14 files changed:
core/java/android/app/ActivityManagerNative.java
core/java/android/app/IActivityManager.java
core/java/android/app/ITaskStackListener.aidl
packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java
packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
services/core/java/com/android/server/am/ActivityManagerService.java
services/core/java/com/android/server/am/ActivityStackSupervisor.java
services/core/java/com/android/server/am/ActivityStarter.java
services/core/java/com/android/server/wm/TaskStack.java

index 1954774..99852b8 100644 (file)
@@ -2882,6 +2882,11 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
             reply.writeInt(isForeground ? 1 : 0);
             return true;
         }
+        case NOTIFY_PINNED_STACK_ANIMATION_ENDED_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            reply.writeNoException();
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -6732,5 +6737,15 @@ class ActivityManagerProxy implements IActivityManager
         return isForeground;
     };
 
+    @Override
+    public void notifyPinnedStackAnimationEnded() throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        mRemote.transact(NOTIFY_PINNED_STACK_ANIMATION_ENDED_TRANSACTION, data, reply, 0);
+        data.recycle();
+        reply.recycle();
+    };
+
     private IBinder mRemote;
 }
index b5ca6ee..98ce273 100644 (file)
@@ -597,6 +597,8 @@ public interface IActivityManager extends IInterface {
 
     public boolean supportsLocalVoiceInteraction() throws RemoteException;
 
+    public void notifyPinnedStackAnimationEnded() throws RemoteException;
+
     /*
      * Private non-Binder interfaces
      */
@@ -972,4 +974,5 @@ public interface IActivityManager extends IInterface {
     int START_LOCAL_VOICE_INTERACTION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 363;
     int STOP_LOCAL_VOICE_INTERACTION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 364;
     int SUPPORTS_LOCAL_VOICE_INTERACTION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 365;
+    int NOTIFY_PINNED_STACK_ANIMATION_ENDED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 366;
 }
index fa11234..6432558 100644 (file)
@@ -30,4 +30,9 @@ oneway interface ITaskStackListener {
      * brought to the front or a new Intent is delivered to it.
      */
     void onPinnedActivityRestartAttempt();
+
+    /**
+     * Called whenever the pinned stack is done animating a resize.
+     */
+    void onPinnedStackAnimationEnded();
 }
index 5890b5f..9da5c2b 100644 (file)
@@ -124,6 +124,10 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener
         public void onPinnedActivityRestartAttempt() {
         }
 
+        @Override
+        public void onPinnedStackAnimationEnded() {
+        }
+
         /** Preloads the next task */
         public void run() {
             RecentsConfiguration config = Recents.getConfiguration();
index 42ebfa9..f3201d0 100644 (file)
@@ -318,13 +318,13 @@ public class RecentsTvActivity extends Activity implements OnPreDrawListener {
         switch (keyCode) {
             case KeyEvent.KEYCODE_DPAD_UP: {
                 SystemServicesProxy ssp = Recents.getSystemServices();
-                PipManager.getInstance().showPipMenu();
+                PipManager.getInstance().resizePinnedStack(PipManager.STATE_PIP_MENU);
                 ssp.focusPinnedStack();
                 return true;
             }
             case KeyEvent.KEYCODE_DPAD_DOWN: {
                 SystemServicesProxy ssp = Recents.getSystemServices();
-                PipManager.getInstance().showPipOverlay(false);
+                PipManager.getInstance().resizePinnedStack(PipManager.STATE_PIP_OVERLAY);
                 ssp.focusHomeStack();
                 return true;
             }
index e20936b..08cd053 100644 (file)
@@ -109,6 +109,10 @@ public class CarStatusBar extends PhoneStatusBar {
         }
 
         @Override
+        public void onPinnedStackAnimationEnded() {
+        }
+
+        @Override
         public void onTaskStackChanged() {
             mHandler.removeCallbacks(this);
             mHandler.post(this);
index 3e47d85..3c30410 100644 (file)
@@ -28,6 +28,7 @@ import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.res.Resources;
 import android.graphics.Rect;
+import android.os.Debug;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.util.Log;
@@ -53,13 +54,17 @@ public class PipManager {
 
     private static final int MAX_RUNNING_TASKS_COUNT = 10;
 
-    private static final int STATE_NO_PIP = 0;
-    private static final int STATE_PIP_OVERLAY = 1;
-    private static final int STATE_PIP_MENU = 2;
+    public static final int STATE_NO_PIP = 0;
+    public static final int STATE_PIP_OVERLAY = 1;
+    public static final int STATE_PIP_MENU = 2;
 
     private static final int TASK_ID_NO_PIP = -1;
     private static final int INVALID_RESOURCE_TYPE = -1;
 
+    public static final int SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH = 0x1;
+    public static final int SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_OVERLAY_ACTIVITY_FINISH = 0x2;
+    private int mSuspendPipResizingReason;
+
     private Context mContext;
     private IActivityManager mActivityManager;
     private int mState = STATE_NO_PIP;
@@ -87,7 +92,8 @@ public class PipManager {
             }
             if (DEBUG) Log.d(TAG, "PINNED_STACK:" + stackInfo);
             mPipTaskId = stackInfo.taskIds[stackInfo.taskIds.length - 1];
-            showPipOverlay(false);
+            // Set state to overlay so we show it when the pinned stack animation ends.
+            mState = STATE_PIP_OVERLAY;
             launchPipOnboardingActivityIfNeeded();
         }
     };
@@ -105,6 +111,23 @@ public class PipManager {
             movePipToFullscreen();
         }
     };
+    private final Runnable mOnPinnedStackAnimationEnded = new Runnable() {
+        @Override
+        public void run() {
+            if (mState == STATE_PIP_OVERLAY) {
+                showPipOverlay();
+            } else if (mState == STATE_PIP_MENU) {
+                showPipMenu();
+            }
+        }
+    };
+
+    private final Runnable mResizePinnedStackRunnable = new Runnable() {
+        @Override
+        public void run() {
+            resizePinnedStack(mState);
+        }
+    };
 
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
@@ -164,7 +187,7 @@ public class PipManager {
         if (!hasPipTasks()) {
             startPip();
         } else if (mState == STATE_PIP_OVERLAY) {
-            showPipMenu();
+            resizePinnedStack(STATE_PIP_MENU);
         }
     }
 
@@ -210,11 +233,7 @@ public class PipManager {
         for (int i = mListeners.size() - 1; i >= 0; --i) {
             mListeners.get(i).onMoveToFullscreen();
         }
-        try {
-            mActivityManager.moveTasksToFullscreenStack(PINNED_STACK_ID, true);
-        } catch (RemoteException e) {
-            Log.e(TAG, "moveTasksToFullscreenStack failed", e);
-        }
+        resizePinnedStack(mState);
     }
 
     /**
@@ -222,25 +241,83 @@ public class PipManager {
      * stack to the default PIP bound {@link com.android.internal.R.string
      * .config_defaultPictureInPictureBounds}.
      */
-    public void showPipOverlay(boolean resizeStack) {
+    private void showPipOverlay() {
         if (DEBUG) Log.d(TAG, "showPipOverlay()");
         mState = STATE_PIP_OVERLAY;
         Intent intent = new Intent(mContext, PipOverlayActivity.class);
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         final ActivityOptions options = ActivityOptions.makeBasic();
         options.setLaunchStackId(PINNED_STACK_ID);
-        if (resizeStack) {
-            options.setLaunchBounds(mPipBound);
-        }
         mContext.startActivity(intent, options.toBundle());
     }
 
     /**
+     * Suspends resizing operation on the Pip until {@link #resumePipResizing} is called
+     * @param reason The reason for suspending resizing operations on the Pip.
+     */
+    public void suspendPipResizing(int reason) {
+        if (DEBUG) Log.d(TAG,
+                "suspendPipResizing() reason=" + reason + " callers=" + Debug.getCallers(2));
+        mSuspendPipResizingReason |= reason;
+    }
+
+    /**
+     * Resumes resizing operation on the Pip that was previously suspended.
+     * @param reason The reason resizing operations on the Pip was suspended.
+     */
+    public void resumePipResizing(int reason) {
+        if ((mSuspendPipResizingReason & reason) == 0) {
+            return;
+        }
+        if (DEBUG) Log.d(TAG,
+                "resumePipResizing() reason=" + reason + " callers=" + Debug.getCallers(2));
+        mSuspendPipResizingReason &= ~reason;
+        mHandler.post(mResizePinnedStackRunnable);
+    }
+
+    /**
+     * Resize the Pip to the appropriate size for the input state.
+     * @param state In Pip state also used to determine the new size for the Pip.
+     */
+    public void resizePinnedStack(int state) {
+        if (DEBUG) Log.d(TAG, "resizePinnedStack() state=" + state);
+        mState = state;
+        Rect bounds;
+        for (int i = mListeners.size() - 1; i >= 0; --i) {
+            mListeners.get(i).onPipResizeAboutToStart();
+        }
+        switch (mState) {
+            case STATE_PIP_MENU:
+                bounds = mMenuModePipBound;
+                break;
+            case STATE_NO_PIP:
+                bounds = null;
+                break;
+            default:
+                bounds = mPipBound;
+                break;
+        }
+
+        if (mSuspendPipResizingReason != 0) {
+            if (DEBUG) Log.d(TAG,
+                    "resizePinnedStack() deferring mSuspendPipResizingReason=" +
+                            mSuspendPipResizingReason);
+            return;
+        }
+
+        try {
+            mActivityManager.resizeStack(PINNED_STACK_ID, bounds, true, true, true);
+        } catch (RemoteException e) {
+            Log.e(TAG, "showPipMenu failed", e);
+        }
+    }
+
+    /**
      * Shows PIP menu UI by launching {@link PipMenuActivity}. It also locates the pinned
      * stack to the centered PIP bound {@link com.android.internal.R.string
      * .config_centeredPictureInPictureBounds}.
      */
-    public void showPipMenu() {
+    private void showPipMenu() {
         if (DEBUG) Log.d(TAG, "showPipMenu()");
         mState = STATE_PIP_MENU;
         for (int i = mListeners.size() - 1; i >= 0; --i) {
@@ -250,20 +327,13 @@ public class PipManager {
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         final ActivityOptions options = ActivityOptions.makeBasic();
         options.setLaunchStackId(PINNED_STACK_ID);
-        options.setLaunchBounds(mMenuModePipBound);
         mContext.startActivity(intent, options.toBundle());
     }
 
-    /**
-     * Adds {@link Listener}.
-     */
     public void addListener(Listener listener) {
         mListeners.add(listener);
     }
 
-    /**
-     * Removes {@link Listener}.
-     */
     public void removeListener(Listener listener) {
         mListeners.remove(listener);
     }
@@ -338,32 +408,36 @@ public class PipManager {
         @Override
         public void onActivityPinned()  throws RemoteException {
             // Post the message back to the UI thread.
+            if (DEBUG) Log.d(TAG, "onActivityPinned()");
             mHandler.post(mOnActivityPinnedRunnable);
         }
 
         @Override
         public void onPinnedActivityRestartAttempt() {
             // Post the message back to the UI thread.
+            if (DEBUG) Log.d(TAG, "onPinnedActivityRestartAttempt()");
             mHandler.post(mOnPinnedActivityRestartAttempt);
         }
+
+        @Override
+        public void onPinnedStackAnimationEnded() {
+            if (DEBUG) Log.d(TAG, "onPinnedStackAnimationEnded()");
+            mHandler.post(mOnPinnedStackAnimationEnded);
+        }
     }
 
     /**
      * A listener interface to receive notification on changes in PIP.
      */
     public interface Listener {
-        /**
-         * Invoked when a PIPed activity is closed.
-         */
+        /** Invoked when a PIPed activity is closed. */
         void onPipActivityClosed();
-        /**
-         * Invoked when the PIP menu gets shown.
-         */
+        /** Invoked when the PIP menu gets shown. */
         void onShowPipMenu();
-        /**
-         * Invoked when the PIPed activity is returned back to the fullscreen.
-         */
+        /** Invoked when the PIPed activity is returned back to the fullscreen. */
         void onMoveToFullscreen();
+        /** Invoked when we are above to start resizing the Pip. */
+        void onPipResizeAboutToStart();
     }
 
     /**
index 15c55f5..7e229d4 100644 (file)
@@ -54,7 +54,7 @@ public class PipMenuActivity extends Activity implements PipManager.Listener {
         findViewById(R.id.cancel).setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                mPipManager.showPipOverlay(true);
+                mPipManager.resizePinnedStack(PipManager.STATE_PIP_OVERLAY);
                 finish();
             }
         });
@@ -62,13 +62,15 @@ public class PipMenuActivity extends Activity implements PipManager.Listener {
 
     @Override
     protected void onDestroy() {
-        mPipManager.removeListener(this);
         super.onDestroy();
+        mPipManager.removeListener(this);
+        mPipManager.resumePipResizing(
+                PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH);
     }
 
     @Override
     public void onBackPressed() {
-        mPipManager.showPipOverlay(true);
+        mPipManager.resizePinnedStack(PipManager.STATE_PIP_OVERLAY);
         finish();
     }
 
@@ -84,4 +86,11 @@ public class PipMenuActivity extends Activity implements PipManager.Listener {
     public void onMoveToFullscreen() {
         finish();
     }
+
+    @Override
+    public void onPipResizeAboutToStart() {
+        finish();
+        mPipManager.suspendPipResizing(
+                PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH);
+    }
 }
index a0b913a..6f71c92 100644 (file)
@@ -62,4 +62,8 @@ public class PipOnboardingActivity extends Activity implements PipManager.Listen
     public void onMoveToFullscreen() {
         finish();
     }
+
+    @Override
+    public void onPipResizeAboutToStart() {
+    }
 }
index bc59a8c..b407935 100644 (file)
@@ -19,8 +19,8 @@ package com.android.systemui.tv.pip;
 import android.app.Activity;
 import android.os.Bundle;
 import android.os.Handler;
-import android.view.View;
 
+import android.view.View;
 import com.android.systemui.R;
 
 /**
@@ -30,25 +30,37 @@ public class PipOverlayActivity extends Activity implements PipManager.Listener
     private static final String TAG = "PipOverlayActivity";
     private static final boolean DEBUG = false;
 
-    private static final long SHOW_GUIDE_OVERLAY_VIEW_DURATION_MS = 2000;
+    private static final long SHOW_GUIDE_OVERLAY_VIEW_DURATION_MS = 4000;
 
     private final PipManager mPipManager = PipManager.getInstance();
     private final Handler mHandler = new Handler();
+    private View mGuideOverlayView;
+    private final Runnable mHideGuideOverlayRunnable = new Runnable() {
+        public void run() {
+            mGuideOverlayView.setVisibility(View.INVISIBLE);
+        }
+    };
 
     @Override
     protected void onCreate(Bundle bundle) {
         super.onCreate(bundle);
         setContentView(R.layout.tv_pip_overlay);
+        mGuideOverlayView = findViewById(R.id.guide_overlay);
         mPipManager.addListener(this);
-        final View overlayView = findViewById(R.id.guide_overlay);
-        // TODO: apply animation
-        overlayView.setVisibility(View.VISIBLE);
-        mHandler.postDelayed(new Runnable() {
-            @Override
-            public void run() {
-                overlayView.setVisibility(View.INVISIBLE);
-            }
-        }, SHOW_GUIDE_OVERLAY_VIEW_DURATION_MS);
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        mHandler.removeCallbacks(mHideGuideOverlayRunnable);
+        mHandler.postDelayed(mHideGuideOverlayRunnable, SHOW_GUIDE_OVERLAY_VIEW_DURATION_MS);
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        mHandler.removeCallbacks(mHideGuideOverlayRunnable);
+        finish();
     }
 
     @Override
@@ -56,6 +68,8 @@ public class PipOverlayActivity extends Activity implements PipManager.Listener
         super.onDestroy();
         mHandler.removeCallbacksAndMessages(null);
         mPipManager.removeListener(this);
+        mPipManager.resumePipResizing(
+                PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_OVERLAY_ACTIVITY_FINISH);
     }
 
     @Override
@@ -72,4 +86,11 @@ public class PipOverlayActivity extends Activity implements PipManager.Listener
     public void onMoveToFullscreen() {
         finish();
     }
+
+    @Override
+    public void onPipResizeAboutToStart() {
+        finish();
+        mPipManager.suspendPipResizing(
+                PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_OVERLAY_ACTIVITY_FINISH);
+    }
 }
index 393edb6..0413667 100644 (file)
@@ -1451,6 +1451,7 @@ public final class ActivityManagerService extends ActivityManagerNative
     static final int VR_MODE_CHANGE_MSG = 63;
     static final int NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG = 64;
     static final int NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG = 65;
+    static final int NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG = 66;
 
     static final int FIRST_ACTIVITY_STACK_MSG = 100;
     static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1979,6 +1980,20 @@ public final class ActivityManagerService extends ActivityManagerNative
                 }
                 break;
             }
+            case NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG: {
+                synchronized (ActivityManagerService.this) {
+                    for (int i = mTaskStackListeners.beginBroadcast() - 1; i >= 0; i--) {
+                        try {
+                            // Make a one-way callback to the listener
+                            mTaskStackListeners.getBroadcastItem(i).onPinnedStackAnimationEnded();
+                        } catch (RemoteException e){
+                            // Handled by the RemoteCallbackList
+                        }
+                    }
+                    mTaskStackListeners.finishBroadcast();
+                }
+                break;
+            }
             case NOTIFY_CLEARTEXT_NETWORK_MSG: {
                 final int uid = msg.arg1;
                 final byte[] firstPacket = (byte[]) msg.obj;
@@ -7161,8 +7176,8 @@ public final class ActivityManagerService extends ActivityManagerNative
                 final Rect bounds = (mStackSupervisor.getStack(PINNED_STACK_ID) == null)
                         ? mDefaultPinnedStackBounds : null;
 
-                mStackSupervisor.moveActivityToStackLocked(
-                        r, PINNED_STACK_ID, "enterPictureInPicture", bounds);
+                mStackSupervisor.moveActivityToPinnedStackLocked(
+                        r, "enterPictureInPicture", bounds);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -11052,6 +11067,16 @@ public final class ActivityManagerService extends ActivityManagerNative
         mHandler.obtainMessage(NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG).sendToTarget();
     }
 
+    /** Notifies all listeners when the pinned stack animation ends. */
+    @Override
+    public void notifyPinnedStackAnimationEnded() {
+        synchronized (this) {
+            mHandler.removeMessages(NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG);
+            mHandler.obtainMessage(
+                    NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG).sendToTarget();
+        }
+    }
+
     @Override
     public void notifyCleartextNetwork(int uid, byte[] firstPacket) {
         mHandler.obtainMessage(NOTIFY_CLEARTEXT_NETWORK_MSG, uid, 0, firstPacket).sendToTarget();
index 0beef53..26108a3 100644 (file)
@@ -148,6 +148,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TASKS;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBLE_BEHIND;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityManagerService.ANIMATE;
 import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
 import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
@@ -2321,36 +2322,44 @@ public final class ActivityStackSupervisor implements DisplayListener {
             return false;
         }
 
-        moveActivityToStackLocked(r, PINNED_STACK_ID, "moveTopActivityToPinnedStack", null);
-        mWindowManager.animateResizePinnedStack(bounds);
+        moveActivityToPinnedStackLocked(r, "moveTopActivityToPinnedStack", bounds);
         return true;
     }
 
-    void moveActivityToStackLocked(ActivityRecord r, int stackId, String reason, Rect bounds) {
-        final TaskRecord task = r.task;
-        if (task.mActivities.size() == 1) {
-            // There is only one activity in the task. So, we can just move the task over to the
-            // stack without re-parenting the activity in a different task.
-            moveTaskToStackLocked(
-                    task.taskId, stackId, ON_TOP, FORCE_FOCUS, reason, true /* animate */);
-        } else {
-            final ActivityStack stack = getStack(stackId, CREATE_IF_NEEDED, ON_TOP);
-            stack.moveActivityToStack(r);
-        }
-
-        if (bounds != null) {
-            resizeStackLocked(stackId, bounds, null /* tempTaskBounds */,
-                    null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS, true);
+    void moveActivityToPinnedStackLocked(ActivityRecord r, String reason, Rect bounds) {
+        mWindowManager.deferSurfaceLayout();
+        try {
+            final TaskRecord task = r.task;
+
+            // Need to make sure the pinned stack exist so we can resize it below...
+            final ActivityStack stack = getStack(PINNED_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
+
+            // Resize the pinned stack to match the current size of the task the activity we are
+            // going to be moving is currently contained in. We do this to have the right starting
+            // animation bounds for the pinned stack to the desired bounds the caller wants.
+            resizeStackLocked(PINNED_STACK_ID, task.mBounds, null /* tempTaskBounds */,
+                    null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
+                    true /* allowResizeInDockedMode */);
+
+            if (task.mActivities.size() == 1) {
+                // There is only one activity in the task. So, we can just move the task over to
+                // the stack without re-parenting the activity in a different task.
+                moveTaskToStackLocked(
+                        task.taskId, PINNED_STACK_ID, ON_TOP, FORCE_FOCUS, reason, !ANIMATE);
+            } else {
+                stack.moveActivityToStack(r);
+            }
+        } finally {
+            mWindowManager.continueSurfaceLayout();
         }
 
-        // The task might have already been running and its visibility needs to be synchronized with
-        // the visibility of the stack / windows.
+        // The task might have already been running and its visibility needs to be synchronized
+        // with the visibility of the stack / windows.
         ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
         resumeFocusedStackTopActivityLocked();
 
-        if (stackId == PINNED_STACK_ID) {
-            mService.notifyActivityPinnedLocked();
-        }
+        mWindowManager.animateResizePinnedStack(bounds);
+        mService.notifyActivityPinnedLocked();
     }
 
     void positionTaskInStackLocked(int taskId, int stackId, int position) {
index b360b89..28be456 100644 (file)
@@ -1001,7 +1001,7 @@ class ActivityStarter {
                 // If the activity is not focusable, we can't resume it, but still would like to
                 // make sure it becomes visible as it starts (this will also trigger entry
                 // animation). An example of this are PIP activities.
-                mTargetStack.ensureActivitiesVisibleLocked(mStartActivity, 0, !PRESERVE_WINDOWS);
+                mTargetStack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
             }
         } else {
             mTargetStack.addRecentActivityLocked(mStartActivity);
index a8b7289..7244676 100644 (file)
@@ -38,6 +38,7 @@ import java.util.ArrayList;
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.view.WindowManager.DOCKED_BOTTOM;
 import static android.view.WindowManager.DOCKED_INVALID;
@@ -945,6 +946,13 @@ public class TaskStack implements DimLayer.DimLayerUser,
             mDragResizing = false;
             mService.requestTraversal();
         }
+        if (mStackId == PINNED_STACK_ID) {
+            try {
+                mService.mActivityManager.notifyPinnedStackAnimationEnded();
+            } catch (RemoteException e) {
+                // I don't believe you...
+            }
+        }
     }
 
     @Override