Merge "Updating Overview to work with PiP" into oc-dev

am: 0553cbcf36

Change-Id: I57d2f76567c0a92d9a282c6c8cdd0732be06873d
This commit is contained in:
Winson Chung
2017-05-16 23:50:04 +00:00
committed by android-build-merger
22 changed files with 458 additions and 73 deletions

View File

@@ -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();

View File

@@ -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

View File

@@ -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.
*/

View File

@@ -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;
@@ -373,11 +389,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);

View File

@@ -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);

View File

@@ -612,7 +612,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;

View File

@@ -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();
}
}
}
@@ -395,6 +400,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();
@@ -501,28 +507,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
@@ -821,6 +806,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);

View File

@@ -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;

View File

@@ -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;
@@ -594,6 +617,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.
*/
@@ -676,6 +713,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) {
@@ -921,6 +959,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;
@@ -988,12 +1029,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 ****/

View File

@@ -810,6 +810,11 @@ public class EventBus extends BroadcastReceiver {
private void queueEvent(final Event event) {
ArrayList<EventHandler> 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;
}

View File

@@ -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 {
}

View File

@@ -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;
}
}

View File

@@ -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 {
}

View File

@@ -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;
}

View File

@@ -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
}

View File

@@ -163,7 +163,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() { }
@@ -218,9 +218,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
@@ -449,9 +449,18 @@ public class SystemServicesProxy {
* Returns the top running task.
*/
public ActivityManager.RunningTaskInfo getRunningTask() {
List<ActivityManager.RunningTaskInfo> tasks = mAm.getRunningTasks(1);
// Note: The set of running tasks from the system is ordered by recency
List<ActivityManager.RunningTaskInfo> 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;
}
@@ -1314,7 +1323,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;
}

View File

@@ -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<Task> 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 */

View File

@@ -55,7 +55,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;
@@ -250,6 +252,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) {
@@ -635,6 +643,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.
*/

View File

@@ -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) {

View File

@@ -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();
}
/**

View File

@@ -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. */

View File

@@ -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();
}