From b502690e2d0a120993279a6fe800ad07dccc8872 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Wed, 3 May 2017 12:45:13 -0700 Subject: [PATCH] Updating Overview to work with PiP - Ensure that an activity that is auto-entering PiP when hitting Overview does not show up in Overview. This is done by listening for the onActivityPinned() callback from the system, and remove the pinned task. - Ensure that we show the PiP task in Overview after it is dismissed, while Overview is open. This is done by listening for the onActivityUnpinned() callback from the system and refreshing the task list similar to when the multi-window mode changes. - When launching from a PiP activity, or launching back into Overview where the next task should be PiP, then ensure that we scroll the stack to the front so that the first task is fully visible. - Fix two lingering Overview issues, when there are no handlers (ie. with dynamically registered handlers), ensure that we call pre/post dispatch callbacks (otherwise it could cause animated events not to work correctly). Also, ensure that we don't update the dummy stack view TaskStack without clearing the stack first, since we may be modifying the same stack that the activity consumed when starting. Bug: 34185886 Bug: 38207296 Test: Launch PIP activity from hitting Overview in the various ways described above Change-Id: I699e655106e6ed7206e163f9d3c15477bbfd52ef --- core/java/android/app/ITaskStackListener.aidl | 2 +- core/java/android/app/TaskStackListener.java | 2 +- .../systemui/pip/phone/PipManager.java | 27 ++++++- .../systemui/pip/phone/PipMenuActivity.java | 21 +++++ .../pip/phone/PipMenuActivityController.java | 19 +++++ .../android/systemui/pip/tv/PipManager.java | 2 +- .../systemui/recents/RecentsActivity.java | 64 ++++++++++------ .../recents/RecentsActivityLaunchState.java | 6 ++ .../android/systemui/recents/RecentsImpl.java | 57 ++++++++++++-- .../systemui/recents/events/EventBus.java | 5 ++ .../events/activity/ShowEmptyViewEvent.java | 25 ++++++ .../events/component/ActivityPinnedEvent.java | 31 ++++++++ .../component/ActivityUnpinnedEvent.java | 25 ++++++ .../events/component/ExpandPipEvent.java | 26 +++++++ .../events/component/HidePipMenuEvent.java | 26 +++++++ .../recents/misc/SystemServicesProxy.java | 21 +++-- .../systemui/recents/model/TaskStack.java | 66 +++++++++++++--- .../systemui/recents/views/RecentsView.java | 12 +++ .../views/TaskStackLayoutAlgorithm.java | 9 ++- .../systemui/recents/views/TaskStackView.java | 76 ++++++++++++++----- .../server/am/ActivityStackSupervisor.java | 3 +- .../am/TaskChangeNotificationController.java | 6 +- 22 files changed, 458 insertions(+), 73 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowEmptyViewEvent.java create mode 100644 packages/SystemUI/src/com/android/systemui/recents/events/component/ActivityPinnedEvent.java create mode 100644 packages/SystemUI/src/com/android/systemui/recents/events/component/ActivityUnpinnedEvent.java create mode 100644 packages/SystemUI/src/com/android/systemui/recents/events/component/ExpandPipEvent.java create mode 100644 packages/SystemUI/src/com/android/systemui/recents/events/component/HidePipMenuEvent.java diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl index 4994fbb0da3a0..b5b101773f153 100644 --- a/core/java/android/app/ITaskStackListener.aidl +++ b/core/java/android/app/ITaskStackListener.aidl @@ -30,7 +30,7 @@ oneway interface ITaskStackListener { void onTaskStackChanged(); /** Called whenever an Activity is moved to the pinned stack from another stack. */ - void onActivityPinned(String packageName); + void onActivityPinned(String packageName, int taskId); /** Called whenever an Activity is moved from the pinned stack to another stack. */ void onActivityUnpinned(); diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java index 2df011fb856ee..a52ca0a64cd2c 100644 --- a/core/java/android/app/TaskStackListener.java +++ b/core/java/android/app/TaskStackListener.java @@ -31,7 +31,7 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { } @Override - public void onActivityPinned(String packageName) throws RemoteException { + public void onActivityPinned(String packageName, int taskId) throws RemoteException { } @Override diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index da2d38f03caa6..0da468114890e 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -16,9 +16,11 @@ package com.android.systemui.pip.phone; +import static android.app.ActivityManager.StackId.PINNED_STACK_ID; import static android.view.Display.DEFAULT_DISPLAY; import android.app.ActivityManager; +import android.app.ActivityManager.StackInfo; import android.app.IActivityManager; import android.content.ComponentName; import android.content.Context; @@ -33,6 +35,8 @@ import android.view.IWindowManager; import android.view.WindowManagerGlobal; import com.android.systemui.pip.BasePipManager; +import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.events.component.ExpandPipEvent; import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener; import com.android.systemui.statusbar.CommandQueue; @@ -65,7 +69,7 @@ public class PipManager implements BasePipManager { */ TaskStackListener mTaskStackListener = new TaskStackListener() { @Override - public void onActivityPinned(String packageName) { + public void onActivityPinned(String packageName, int taskId) { if (!checkCurrentUserId(false /* debug */)) { return; } @@ -186,6 +190,7 @@ public class PipManager implements BasePipManager { mInputConsumerController); mNotificationController = new PipNotificationController(context, mActivityManager, mTouchHandler.getMotionHelper()); + EventBus.getDefault().register(this); } /** @@ -195,6 +200,26 @@ public class PipManager implements BasePipManager { mTouchHandler.onConfigurationChanged(); } + /** + * Expands the PIP. + */ + public final void onBusEvent(ExpandPipEvent event) { + if (event.clearThumbnailWindows) { + try { + StackInfo stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID); + if (stackInfo != null && stackInfo.taskIds != null) { + SystemServicesProxy ssp = SystemServicesProxy.getInstance(mContext); + for (int taskId : stackInfo.taskIds) { + ssp.cancelThumbnailTransition(taskId); + } + } + } catch (RemoteException e) { + // Do nothing + } + } + mTouchHandler.getMotionHelper().expandPip(false /* skipAnimation */); + } + /** * Sent from KEYCODE_WINDOW handler in PhoneWindowManager, to request the menu to be shown. */ diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java index 65f24cf5ba983..5f4e54774a116 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java @@ -63,6 +63,8 @@ import android.widget.LinearLayout; import com.android.systemui.Interpolators; import com.android.systemui.R; +import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.events.component.HidePipMenuEvent; import java.util.ArrayList; import java.util.Collections; @@ -231,6 +233,7 @@ public class PipMenuActivity extends Activity { super.onStop(); cancelDelayedFinish(); + EventBus.getDefault().unregister(this); } @Override @@ -290,6 +293,19 @@ public class PipMenuActivity extends Activity { // Do nothing } + public final void onBusEvent(HidePipMenuEvent event) { + if (mMenuState != MENU_STATE_NONE) { + // If the menu is visible in either the closed or full state, then hide the menu and + // trigger the animation trigger afterwards + event.getAnimationTrigger().increment(); + hideMenu(() -> { + mHandler.post(() -> { + event.getAnimationTrigger().decrement(); + }); + }, true /* notifyMenuVisibility */); + } + } + private void showMenu(int menuState, Rect stackBounds, Rect movementBounds, boolean allowMenuTimeout) { mAllowMenuTimeout = allowMenuTimeout; @@ -374,11 +390,16 @@ public class PipMenuActivity extends Activity { private void updateFromIntent(Intent intent) { mToControllerMessenger = intent.getParcelableExtra(EXTRA_CONTROLLER_MESSENGER); notifyActivityCallback(mMessenger); + + // Register for HidePipMenuEvents once we notify the controller of this activity + EventBus.getDefault().register(this); + ParceledListSlice actions = intent.getParcelableExtra(EXTRA_ACTIONS); if (actions != null) { mActions.clear(); mActions.addAll(actions.getList()); } + final int menuState = intent.getIntExtra(EXTRA_MENU_STATE, MENU_STATE_NONE); if (menuState != MENU_STATE_NONE) { Rect stackBounds = intent.getParcelableExtra(EXTRA_STACK_BOUNDS); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java index 5afa53f557ba2..1ccea6f684dfa 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java @@ -36,6 +36,9 @@ import android.util.Log; import android.view.IWindowManager; import com.android.systemui.pip.phone.PipMediaController.ActionListener; +import com.android.systemui.recents.events.EventBus; +import com.android.systemui.recents.events.component.HidePipMenuEvent; +import com.android.systemui.recents.misc.ReferenceCountedTrigger; import java.io.PrintWriter; import java.util.ArrayList; @@ -119,6 +122,7 @@ public class PipMenuActivityController { // The dismiss fraction update is sent frequently, so use a temporary bundle for the message private Bundle mTmpDismissFractionData = new Bundle(); + private ReferenceCountedTrigger mOnAttachDecrementTrigger; private boolean mStartActivityRequested; private Messenger mToActivityMessenger; private Messenger mMessenger = new Messenger(new Handler() { @@ -157,6 +161,10 @@ public class PipMenuActivityController { case MESSAGE_UPDATE_ACTIVITY_CALLBACK: { mToActivityMessenger = msg.replyTo; mStartActivityRequested = false; + if (mOnAttachDecrementTrigger != null) { + mOnAttachDecrementTrigger.decrement(); + mOnAttachDecrementTrigger = null; + } // Mark the menu as invisible once the activity finishes as well if (mToActivityMessenger == null) { onMenuStateChanged(MENU_STATE_NONE, true /* resize */); @@ -181,6 +189,8 @@ public class PipMenuActivityController { mActivityManager = activityManager; mMediaController = mediaController; mInputConsumerController = inputConsumerController; + + EventBus.getDefault().register(this); } public void onActivityPinned() { @@ -435,6 +445,15 @@ public class PipMenuActivityController { mMenuState = menuState; } + public final void onBusEvent(HidePipMenuEvent event) { + if (mStartActivityRequested) { + // If the menu has been start-requested, but not actually started, then we defer the + // trigger callback until the menu has started and called back to the controller + mOnAttachDecrementTrigger = event.getAnimationTrigger(); + mOnAttachDecrementTrigger.increment(); + } + } + public void dump(PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; pw.println(prefix + TAG); diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java index 6667b71efc0e9..92496aa287d27 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java @@ -603,7 +603,7 @@ public class PipManager implements BasePipManager { } @Override - public void onActivityPinned(String packageName) { + public void onActivityPinned(String packageName, int taskId) { if (DEBUG) Log.d(TAG, "onActivityPinned()"); if (!checkCurrentUserId(DEBUG)) { return; diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index 7bc591f4832a8..edbf01d46a851 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -62,6 +62,7 @@ import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent; import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent; import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent; import com.android.systemui.recents.events.activity.ToggleRecentsEvent; +import com.android.systemui.recents.events.component.ActivityUnpinnedEvent; import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent; import com.android.systemui.recents.events.component.ScreenPinningRequestEvent; import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent; @@ -205,6 +206,10 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD } Settings.Secure.putLongForUser(RecentsActivity.this.getContentResolver(), Secure.OVERVIEW_LAST_STACK_ACTIVE_TIME, currentTime, currentUser); + + // Clear the last PiP task time, it's an edge case and we'd rather it + // not relaunch the PiP task if the user double taps + RecentsImpl.clearLastPipTime(); } } } @@ -393,6 +398,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD * Reloads the stack views upon launching Recents. */ private void reloadStackView() { + // If the Recents component has preloaded a load plan, then use that to prevent // reconstructing the task stack RecentsTaskLoader loader = Recents.getTaskLoader(); @@ -499,28 +505,7 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD public void onMultiWindowModeChanged(boolean isInMultiWindowMode) { super.onMultiWindowModeChanged(isInMultiWindowMode); - // Reload the task stack completely - RecentsConfiguration config = Recents.getConfiguration(); - RecentsActivityLaunchState launchState = config.getLaunchState(); - RecentsTaskLoader loader = Recents.getTaskLoader(); - RecentsTaskLoadPlan loadPlan = loader.createLoadPlan(this); - loader.preloadTasks(loadPlan, -1 /* runningTaskId */, - false /* includeFrontMostExcludedTask */); - - RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options(); - loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks; - loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails; - loader.loadTasks(this, loadPlan, loadOpts); - - TaskStack stack = loadPlan.getTaskStack(); - int numStackTasks = stack.getStackTaskCount(); - boolean showDeferredAnimation = numStackTasks > 0; - - EventBus.getDefault().send(new ConfigurationChangedEvent(true /* fromMultiWindow */, - false /* fromDeviceOrientationChange */, false /* fromDisplayDensityChange */, - numStackTasks > 0)); - EventBus.getDefault().send(new MultiWindowStateChangedEvent(isInMultiWindowMode, - showDeferredAnimation, stack)); + reloadTaskStack(isInMultiWindowMode, true /* sendConfigChangedEvent */); } @Override @@ -819,6 +804,41 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD mRecentsView.invalidate(); } + public final void onBusEvent(final ActivityUnpinnedEvent event) { + if (mIsVisible) { + // Skip the configuration change event as the PiP activity does not actually affect the + // config of recents + reloadTaskStack(isInMultiWindowMode(), false /* sendConfigChangedEvent */); + } + } + + private void reloadTaskStack(boolean isInMultiWindowMode, boolean sendConfigChangedEvent) { + // Reload the task stack completely + RecentsConfiguration config = Recents.getConfiguration(); + RecentsActivityLaunchState launchState = config.getLaunchState(); + RecentsTaskLoader loader = Recents.getTaskLoader(); + RecentsTaskLoadPlan loadPlan = loader.createLoadPlan(this); + loader.preloadTasks(loadPlan, -1 /* runningTaskId */, + false /* includeFrontMostExcludedTask */); + + RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options(); + loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks; + loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails; + loader.loadTasks(this, loadPlan, loadOpts); + + TaskStack stack = loadPlan.getTaskStack(); + int numStackTasks = stack.getStackTaskCount(); + boolean showDeferredAnimation = numStackTasks > 0; + + if (sendConfigChangedEvent) { + EventBus.getDefault().send(new ConfigurationChangedEvent(true /* fromMultiWindow */, + false /* fromDeviceOrientationChange */, false /* fromDisplayDensityChange */, + numStackTasks > 0)); + } + EventBus.getDefault().send(new MultiWindowStateChangedEvent(isInMultiWindowMode, + showDeferredAnimation, stack)); + } + @Override public boolean onPreDraw() { mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this); diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java index a7f6b70d281e1..5b8ed94d5df09 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java @@ -29,6 +29,10 @@ public class RecentsActivityLaunchState { public boolean launchedWithAltTab; public boolean launchedFromApp; + // Set if the activity that we launched from entered PiP during the transition into Recents + public boolean launchedFromPipApp; + // Set if the next activity that quick-switch will launch is the PiP activity + public boolean launchedWithNextPipApp; public boolean launchedFromBlacklistedApp; public boolean launchedFromHome; public boolean launchedViaDragGesture; @@ -41,6 +45,8 @@ public class RecentsActivityLaunchState { launchedFromHome = false; launchedFromApp = false; launchedFromBlacklistedApp = false; + launchedFromPipApp = false; + launchedWithNextPipApp = false; launchedToTaskId = -1; launchedWithAltTab = false; launchedViaDragGesture = false; diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java index 2b812a5f7ac3f..b65cac8ec94ca 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java @@ -57,6 +57,9 @@ import com.android.systemui.recents.events.activity.LaunchMostRecentTaskRequestE import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent; import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent; import com.android.systemui.recents.events.activity.ToggleRecentsEvent; +import com.android.systemui.recents.events.component.ActivityPinnedEvent; +import com.android.systemui.recents.events.component.ActivityUnpinnedEvent; +import com.android.systemui.recents.events.component.HidePipMenuEvent; import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent; import com.android.systemui.recents.events.component.ScreenPinningRequestEvent; import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent; @@ -127,6 +130,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener // previous one. VisibilityReport visibilityReport; synchronized (mDummyStackView) { + mDummyStackView.getStack().removeAllTasks(false /* notifyStackChanges */); mDummyStackView.setTasks(plan.getTaskStack(), false /* allowNotify */); updateDummyStackViewLayout(plan.getTaskStack(), getWindowRect(null /* windowRectOverride */)); @@ -150,6 +154,23 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener } } + @Override + public void onActivityPinned(String packageName, int taskId) { + // This time needs to be fetched the same way the last active time is fetched in + // {@link TaskRecord#touchActiveTime} + Recents.getConfiguration().getLaunchState().launchedFromPipApp = true; + Recents.getConfiguration().getLaunchState().launchedWithNextPipApp = false; + EventBus.getDefault().send(new ActivityPinnedEvent(taskId)); + consumeInstanceLoadPlan(); + sLastPipTime = System.currentTimeMillis(); + } + + @Override + public void onActivityUnpinned() { + EventBus.getDefault().send(new ActivityUnpinnedEvent()); + sLastPipTime = -1; + } + @Override public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) { EventBus.getDefault().send(new TaskSnapshotChangedEvent(taskId, snapshot)); @@ -157,6 +178,8 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener } protected static RecentsTaskLoadPlan sInstanceLoadPlan; + // Stores the last pinned task time + protected static long sLastPipTime = -1; protected Context mContext; protected Handler mHandler; @@ -592,6 +615,20 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener return plan; } + /** + * @return the time at which a task last entered picture-in-picture. + */ + public static long getLastPipTime() { + return sLastPipTime; + } + + /** + * Clears the time at which a task last entered picture-in-picture. + */ + public static void clearLastPipTime() { + sLastPipTime = -1; + } + /** * Reloads all the resources for the current configuration. */ @@ -674,6 +711,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener updateDummyStackViewLayout(stack, windowRect); if (stack != null) { TaskStackLayoutAlgorithm stackLayout = mDummyStackView.getStackAlgorithm(); + mDummyStackView.getStack().removeAllTasks(false /* notifyStackChanges */); mDummyStackView.setTasks(stack, false /* allowNotifyStackChanges */); // Get the width of a task view so that we know how wide to draw the header bar. if (useGridLayout) { @@ -919,6 +957,9 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener launchState.launchedFromHome = !useThumbnailTransition && !mLaunchedWhileDocking; launchState.launchedFromApp = useThumbnailTransition || mLaunchedWhileDocking; launchState.launchedFromBlacklistedApp = launchState.launchedFromApp && isBlacklisted; + launchState.launchedFromPipApp = false; + launchState.launchedWithNextPipApp = + stack.isNextLaunchTargetPip(RecentsImpl.getLastPipTime()); launchState.launchedViaDockGesture = mLaunchedWhileDocking; launchState.launchedViaDragGesture = mDraggingInRecents; launchState.launchedToTaskId = runningTaskId; @@ -986,12 +1027,16 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | Intent.FLAG_ACTIVITY_TASK_ON_HOME); - if (opts != null) { - mContext.startActivityAsUser(intent, opts.toBundle(), UserHandle.CURRENT); - } else { - mContext.startActivityAsUser(intent, UserHandle.CURRENT); - } - EventBus.getDefault().send(new RecentsActivityStartingEvent()); + HidePipMenuEvent hideMenuEvent = new HidePipMenuEvent(); + hideMenuEvent.addPostAnimationCallback(() -> { + if (opts != null) { + mContext.startActivityAsUser(intent, opts.toBundle(), UserHandle.CURRENT); + } else { + mContext.startActivityAsUser(intent, UserHandle.CURRENT); + } + EventBus.getDefault().send(new RecentsActivityStartingEvent()); + }); + EventBus.getDefault().send(hideMenuEvent); } /**** OnAnimationFinishedListener Implementation ****/ diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java index a7375051e3385..d7abb387c278f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java +++ b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java @@ -810,6 +810,11 @@ public class EventBus extends BroadcastReceiver { private void queueEvent(final Event event) { ArrayList eventHandlers = mEventTypeMap.get(event.getClass()); if (eventHandlers == null) { + // This is just an optimization to return early if there are no handlers. However, we + // should still ensure that we call pre/post dispatch callbacks so that AnimatedEvents + // are still cleaned up correctly if a listener has not been registered to handle them + event.onPreDispatch(); + event.onPostDispatch(); return; } diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowEmptyViewEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowEmptyViewEvent.java new file mode 100644 index 0000000000000..75bfd7bde66c5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ShowEmptyViewEvent.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.recents.events.activity; + +import com.android.systemui.recents.events.EventBus; + +/** + * Sent when the stack should be hidden and the empty view shown. + */ +public class ShowEmptyViewEvent extends EventBus.Event { +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/component/ActivityPinnedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/component/ActivityPinnedEvent.java new file mode 100644 index 0000000000000..f4d2fcff96726 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/events/component/ActivityPinnedEvent.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.recents.events.component; + +import com.android.systemui.recents.events.EventBus; + +/** + * This is sent when an activity is pinned. + */ +public class ActivityPinnedEvent extends EventBus.Event { + + public final int taskId; + + public ActivityPinnedEvent(int taskId) { + this.taskId = taskId; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/component/ActivityUnpinnedEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/component/ActivityUnpinnedEvent.java new file mode 100644 index 0000000000000..48c5f0b60f515 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/events/component/ActivityUnpinnedEvent.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.recents.events.component; + +import com.android.systemui.recents.events.EventBus; + +/** + * This is sent when an activity is unpinned. + */ +public class ActivityUnpinnedEvent extends EventBus.Event { +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/component/ExpandPipEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/component/ExpandPipEvent.java new file mode 100644 index 0000000000000..8fe4975f1a2a2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/events/component/ExpandPipEvent.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.recents.events.component; + +import com.android.systemui.recents.events.EventBus; + +/** + * This is sent when the PiP should be expanded due to being relaunched. + */ +public class ExpandPipEvent extends EventBus.Event { + public final boolean clearThumbnailWindows = true; +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/component/HidePipMenuEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/component/HidePipMenuEvent.java new file mode 100644 index 0000000000000..ce4f207aa1d7d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/events/component/HidePipMenuEvent.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.recents.events.component; + +import com.android.systemui.recents.events.EventBus; + +/** + * This is sent when the PiP menu should be hidden. + */ +public class HidePipMenuEvent extends EventBus.AnimatedEvent { + // Simple event +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index f431517cfe77d..2452242aad4d6 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -156,7 +156,7 @@ public class SystemServicesProxy { public void onTaskStackChangedBackground() { } public void onTaskStackChanged() { } public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) { } - public void onActivityPinned(String packageName) { } + public void onActivityPinned(String packageName, int taskId) { } public void onActivityUnpinned() { } public void onPinnedActivityRestartAttempt(boolean clearedTask) { } public void onPinnedStackAnimationStarted() { } @@ -211,9 +211,9 @@ public class SystemServicesProxy { } @Override - public void onActivityPinned(String packageName) throws RemoteException { + public void onActivityPinned(String packageName, int taskId) throws RemoteException { mHandler.removeMessages(H.ON_ACTIVITY_PINNED); - mHandler.obtainMessage(H.ON_ACTIVITY_PINNED, packageName).sendToTarget(); + mHandler.obtainMessage(H.ON_ACTIVITY_PINNED, taskId, 0, packageName).sendToTarget(); } @Override @@ -442,9 +442,18 @@ public class SystemServicesProxy { * Returns the top running task. */ public ActivityManager.RunningTaskInfo getRunningTask() { - List tasks = mAm.getRunningTasks(1); + // Note: The set of running tasks from the system is ordered by recency + List tasks = mAm.getRunningTasks(10); if (tasks != null && !tasks.isEmpty()) { - return tasks.get(0); + // Find the first task in a valid stack, we ignore everything from the Recents and PiP + // stacks + for (int i = 0; i < tasks.size(); i++) { + ActivityManager.RunningTaskInfo task = tasks.get(i); + int stackId = task.stackId; + if (stackId != RECENTS_STACK_ID && stackId != PINNED_STACK_ID) { + return task; + } + } } return null; } @@ -1283,7 +1292,7 @@ public class SystemServicesProxy { } case ON_ACTIVITY_PINNED: { for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { - mTaskStackListeners.get(i).onActivityPinned((String) msg.obj); + mTaskStackListeners.get(i).onActivityPinned((String) msg.obj, msg.arg1); } break; } diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java index 9b25ef8faafd1..6e3be09b2ae9c 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java @@ -229,7 +229,8 @@ public class TaskStack { * Notifies when a task has been removed from the stack. */ void onStackTaskRemoved(TaskStack stack, Task removedTask, Task newFrontMostTask, - AnimationProps animation, boolean fromDockGesture); + AnimationProps animation, boolean fromDockGesture, + boolean dismissRecentsIfAllRemoved); /** * Notifies when all tasks have been removed from the stack. @@ -631,13 +632,22 @@ public class TaskStack { * how they should update themselves. */ public void removeTask(Task t, AnimationProps animation, boolean fromDockGesture) { + removeTask(t, animation, fromDockGesture, true /* dismissRecentsIfAllRemoved */); + } + + /** + * Removes a task from the stack, with an additional {@param animation} hint to the callbacks on + * how they should update themselves. + */ + public void removeTask(Task t, AnimationProps animation, boolean fromDockGesture, + boolean dismissRecentsIfAllRemoved) { if (mStackTaskList.contains(t)) { removeTaskImpl(mStackTaskList, t); Task newFrontMostTask = getStackFrontMostTask(false /* includeFreeform */); if (mCb != null) { // Notify that a task has been removed mCb.onStackTaskRemoved(this, t, newFrontMostTask, animation, - fromDockGesture); + fromDockGesture, dismissRecentsIfAllRemoved); } } mRawTaskList.remove(t); @@ -646,19 +656,27 @@ public class TaskStack { /** * Removes all tasks from the stack. */ - public void removeAllTasks() { + public void removeAllTasks(boolean notifyStackChanges) { ArrayList tasks = mStackTaskList.getTasks(); for (int i = tasks.size() - 1; i >= 0; i--) { Task t = tasks.get(i); removeTaskImpl(mStackTaskList, t); mRawTaskList.remove(t); } - if (mCb != null) { + if (mCb != null && notifyStackChanges) { // Notify that all tasks have been removed mCb.onStackTasksRemoved(this); } } + + /** + * @see #setTasks(Context, List, boolean, boolean) + */ + public void setTasks(Context context, TaskStack stack, boolean notifyStackChanges) { + setTasks(context, stack.mRawTaskList, notifyStackChanges); + } + /** * Sets a few tasks in one go, without calling any callbacks. * @@ -723,7 +741,8 @@ public class TaskStack { Task newFrontMostTask = getStackFrontMostTask(false); for (int i = 0; i < removedTaskCount; i++) { mCb.onStackTaskRemoved(this, removedTasks.get(i), newFrontMostTask, - AnimationProps.IMMEDIATE, false /* fromDockGesture */); + AnimationProps.IMMEDIATE, false /* fromDockGesture */, + true /* dismissRecentsIfAllRemoved */); } // Only callback for the newly added tasks after this stack has been updated @@ -853,22 +872,47 @@ public class TaskStack { return null; } + /** + * Returns whether the next launch target should actually be the PiP task. + */ + public boolean isNextLaunchTargetPip(long lastPipTime) { + Task launchTarget = getLaunchTarget(); + Task nextLaunchTarget = getNextLaunchTargetRaw(); + if (nextLaunchTarget != null && lastPipTime > 0) { + // If the PiP time is more recent than the next launch target, then launch the PiP task + return lastPipTime > nextLaunchTarget.key.lastActiveTime; + } else if (launchTarget != null && lastPipTime > 0 && getTaskCount() == 1) { + // Otherwise, if there is no next launch target, but there is a PiP, then launch + // the PiP task + return true; + } + return false; + } + /** * Returns the task in stack tasks which should be launched next if Recents are toggled - * again, or null if there is no task to be launched. + * again, or null if there is no task to be launched. Callers should check + * {@link #isNextLaunchTargetPip(long)} before fetching the next raw launch target from the + * stack. */ public Task getNextLaunchTarget() { + Task nextLaunchTarget = getNextLaunchTargetRaw(); + if (nextLaunchTarget != null) { + return nextLaunchTarget; + } + return getStackTasks().get(getTaskCount() - 1); + } + + private Task getNextLaunchTargetRaw() { int taskCount = getTaskCount(); if (taskCount == 0) { return null; } int launchTaskIndex = indexOfStackTask(getLaunchTarget()); - if (launchTaskIndex != -1) { - launchTaskIndex = Math.max(0, launchTaskIndex - 1); - } else { - launchTaskIndex = getTaskCount() - 1; + if (launchTaskIndex != -1 && launchTaskIndex > 0) { + return getStackTasks().get(launchTaskIndex - 1); } - return getStackTasks().get(launchTaskIndex); + return null; } /** Returns the index of this task in this current task stack */ diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index 8f9c457c255ba..fcca622e43769 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -58,7 +58,9 @@ import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationC import com.android.systemui.recents.events.activity.HideStackActionButtonEvent; import com.android.systemui.recents.events.activity.LaunchTaskEvent; import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent; +import com.android.systemui.recents.events.activity.ShowEmptyViewEvent; import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent; +import com.android.systemui.recents.events.component.ExpandPipEvent; import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent; import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent; import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent; @@ -253,6 +255,12 @@ public class RecentsView extends FrameLayout { /** Launches the task that recents was launched from if possible */ public boolean launchPreviousTask() { + if (Recents.getConfiguration().getLaunchState().launchedFromPipApp) { + // If the app auto-entered PiP on the way to Recents, then just re-expand it + EventBus.getDefault().send(new ExpandPipEvent()); + return true; + } + if (mTaskStackView != null) { Task task = getStack().getLaunchTarget(); if (task != null) { @@ -639,6 +647,10 @@ public class RecentsView extends FrameLayout { updateStack(event.stack, false /* setStackViewTasks */); } + public final void onBusEvent(ShowEmptyViewEvent event) { + showEmptyView(R.string.recents_empty_message); + } + /** * Shows the stack action button. */ diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java index 7ba705e47cf6d..8135034319624 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java @@ -562,7 +562,8 @@ public class TaskStackLayoutAlgorithm { mMinScrollP = 0; mMaxScrollP = Math.max(mMinScrollP, (mNumStackTasks - 1) - Math.max(0, mFocusedRange.getAbsoluteX(maxBottomNormX))); - if (launchState.launchedFromHome) { + if (launchState.launchedFromHome || launchState.launchedFromPipApp + || launchState.launchedWithNextPipApp) { mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP); } else { mInitialScrollP = Utilities.clamp(launchTaskIndex - 1, mMinScrollP, mMaxScrollP); @@ -581,8 +582,8 @@ public class TaskStackLayoutAlgorithm { mMinScrollP = 0; mMaxScrollP = Math.max(mMinScrollP, (mNumStackTasks - 1) - Math.max(0, mUnfocusedRange.getAbsoluteX(maxBottomNormX))); - boolean scrollToFront = launchState.launchedFromHome || - launchState.launchedViaDockGesture; + boolean scrollToFront = launchState.launchedFromHome || launchState.launchedFromPipApp + || launchState.launchedWithNextPipApp || launchState.launchedViaDockGesture; if (launchState.launchedFromBlacklistedApp) { mInitialScrollP = mMaxScrollP; } else if (launchState.launchedWithAltTab) { @@ -608,6 +609,8 @@ public class TaskStackLayoutAlgorithm { mTaskIndexOverrideMap.clear(); boolean scrollToFront = launchState.launchedFromHome || + launchState.launchedFromPipApp || + launchState.launchedWithNextPipApp || launchState.launchedFromBlacklistedApp || launchState.launchedViaDockGesture; if (getInitialFocusState() == STATE_UNFOCUSED && mNumStackTasks > 1) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index 18a9bab740844..5f9a8f5cdd7c3 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -57,6 +57,7 @@ import com.android.systemui.recents.RecentsActivity; import com.android.systemui.recents.RecentsActivityLaunchState; import com.android.systemui.recents.RecentsConfiguration; import com.android.systemui.recents.RecentsDebugFlags; +import com.android.systemui.recents.RecentsImpl; import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent; import com.android.systemui.recents.events.activity.ConfigurationChangedEvent; @@ -72,7 +73,11 @@ import com.android.systemui.recents.events.activity.LaunchTaskEvent; import com.android.systemui.recents.events.activity.LaunchTaskStartedEvent; import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent; import com.android.systemui.recents.events.activity.PackagesChangedEvent; +import com.android.systemui.recents.events.activity.ShowEmptyViewEvent; import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent; +import com.android.systemui.recents.events.component.ActivityPinnedEvent; +import com.android.systemui.recents.events.component.ExpandPipEvent; +import com.android.systemui.recents.events.component.HidePipMenuEvent; import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent; import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent; import com.android.systemui.recents.events.ui.DeleteTaskDataEvent; @@ -379,8 +384,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // Only notify if we are already initialized, otherwise, everything will pick up all the // new and old tasks when we next layout - mStack.setTasks(getContext(), stack.computeAllTasksList(), - allowNotifyStackChanges && isInitialized); + mStack.setTasks(getContext(), stack, allowNotifyStackChanges && isInitialized); } /** Returns the task stack. */ @@ -1496,7 +1500,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal */ @Override public void onStackTaskRemoved(TaskStack stack, Task removedTask, Task newFrontMostTask, - AnimationProps animation, boolean fromDockGesture) { + AnimationProps animation, boolean fromDockGesture, boolean dismissRecentsIfAllRemoved) { if (mFocusedTask == removedTask) { resetFocusedTask(removedTask); } @@ -1527,9 +1531,13 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // If there are no remaining tasks, then just close recents if (mStack.getTaskCount() == 0) { - EventBus.getDefault().send(new AllTaskViewsDismissedEvent(fromDockGesture - ? R.string.recents_empty_message - : R.string.recents_empty_message_dismissed_all)); + if (dismissRecentsIfAllRemoved) { + EventBus.getDefault().send(new AllTaskViewsDismissedEvent(fromDockGesture + ? R.string.recents_empty_message + : R.string.recents_empty_message_dismissed_all)); + } else { + EventBus.getDefault().send(new ShowEmptyViewEvent()); + } } } @@ -1802,14 +1810,36 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal return; } - final Task launchTask = mStack.getNextLaunchTarget(); - if (launchTask != null) { - launchTask(launchTask); - MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_LAUNCH_PREVIOUS_TASK, - launchTask.key.getComponent().toString()); - } else if (mStack.getTaskCount() == 0) { - // If there are no tasks, then just hide recents back to home. - EventBus.getDefault().send(new HideRecentsEvent(false, true)); + if (mStack.getTaskCount() == 0) { + if (RecentsImpl.getLastPipTime() != -1) { + EventBus.getDefault().send(new ExpandPipEvent()); + MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_LAUNCH_PREVIOUS_TASK, + "pip"); + } else { + // If there are no tasks, then just hide recents back to home. + EventBus.getDefault().send(new HideRecentsEvent(false, true)); + } + return; + } + + if (!Recents.getConfiguration().getLaunchState().launchedFromPipApp + && mStack.isNextLaunchTargetPip(RecentsImpl.getLastPipTime())) { + // If the launch task is in the pinned stack, then expand the PiP now + EventBus.getDefault().send(new ExpandPipEvent()); + MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_LAUNCH_PREVIOUS_TASK, "pip"); + } else { + final Task launchTask = mStack.getNextLaunchTarget(); + if (launchTask != null) { + // Defer launching the task until the PiP menu has been dismissed (if it is + // showing at all) + HidePipMenuEvent hideMenuEvent = new HidePipMenuEvent(); + hideMenuEvent.addPostAnimationCallback(() -> { + launchTask(launchTask); + }); + EventBus.getDefault().send(hideMenuEvent); + MetricsLogger.action(getContext(), MetricsEvent.OVERVIEW_LAUNCH_PREVIOUS_TASK, + launchTask.key.getComponent().toString()); + } } } @@ -1871,7 +1901,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal R.string.accessibility_recents_all_items_dismissed)); // Remove all tasks and delete the task data for all tasks - mStack.removeAllTasks(); + mStack.removeAllTasks(true /* notifyStackChanges */); for (int i = tasks.size() - 1; i >= 0; i--) { EventBus.getDefault().send(new DeleteTaskDataEvent(tasks.get(i))); } @@ -2217,11 +2247,23 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } } + public final void onBusEvent(ActivityPinnedEvent event) { + // If an activity enters PiP while Recents is open, remove the stack task associated with + // the new PiP task + Task removeTask = mStack.findTaskWithId(event.taskId); + if (removeTask != null) { + // In this case, we remove the task, but if the last task is removed, don't dismiss + // Recents to home + mStack.removeTask(removeTask, AnimationProps.IMMEDIATE, false /* fromDockGesture */, + false /* dismissRecentsIfAllRemoved */); + } + updateLayoutAlgorithm(false /* boundScroll */); + updateToInitialState(); + } + public void reloadOnConfigurationChange() { mStableLayoutAlgorithm.reloadOnConfigurationChange(getContext()); mLayoutAlgorithm.reloadOnConfigurationChange(getContext()); - - boolean hasDockedTask = Recents.getSystemServices().hasDockedTask(); } /** diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 8210c07b7678b..79ea7baa14429 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -2998,7 +2998,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D stack.animateResizePinnedStack(sourceHintBounds, destBounds, -1 /* animationDuration */, true /* schedulePipModeChangedOnAnimationEnd */); - mService.mTaskChangeNotificationController.notifyActivityPinned(r.packageName); + mService.mTaskChangeNotificationController.notifyActivityPinned(r.packageName, + r.getTask().taskId); } /** Move activity with its stack to front and make the stack focused. */ diff --git a/services/core/java/com/android/server/am/TaskChangeNotificationController.java b/services/core/java/com/android/server/am/TaskChangeNotificationController.java index f5d7b6858b83d..ea9ff592d6e1d 100644 --- a/services/core/java/com/android/server/am/TaskChangeNotificationController.java +++ b/services/core/java/com/android/server/am/TaskChangeNotificationController.java @@ -96,7 +96,7 @@ class TaskChangeNotificationController { }; private final TaskStackConsumer mNotifyActivityPinned = (l, m) -> { - l.onActivityPinned((String) m.obj); + l.onActivityPinned((String) m.obj, m.arg1); }; private final TaskStackConsumer mNotifyActivityUnpinned = (l, m) -> { @@ -279,10 +279,10 @@ class TaskChangeNotificationController { } /** Notifies all listeners when an Activity is pinned. */ - void notifyActivityPinned(String packageName) { + void notifyActivityPinned(String packageName, int taskId) { mHandler.removeMessages(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG); final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG, - packageName); + taskId, 0, packageName); forAllLocalListeners(mNotifyActivityPinned, msg); msg.sendToTarget(); }