Merge "Add shell recents interface" into sc-v2-dev am: df857527f5
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/16069114 Change-Id: Iaa15cb5de5dd86457bc74a0e26e51dad51961930
This commit is contained in:
@@ -38,6 +38,7 @@ filegroup {
|
||||
path: "src",
|
||||
}
|
||||
|
||||
// Sources that have no dependencies that can be used directly downstream of this library
|
||||
filegroup {
|
||||
name: "wm_shell_util-sources",
|
||||
srcs: [
|
||||
@@ -46,6 +47,7 @@ filegroup {
|
||||
path: "src",
|
||||
}
|
||||
|
||||
// Aidls which can be used directly downstream of this library
|
||||
filegroup {
|
||||
name: "wm_shell-aidls",
|
||||
srcs: [
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController;
|
||||
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
|
||||
import com.android.wm.shell.onehanded.OneHandedController;
|
||||
import com.android.wm.shell.pip.Pip;
|
||||
import com.android.wm.shell.recents.RecentTasksController;
|
||||
import com.android.wm.shell.splitscreen.SplitScreenController;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
@@ -43,6 +44,7 @@ public final class ShellCommandHandlerImpl {
|
||||
private final Optional<OneHandedController> mOneHandedOptional;
|
||||
private final Optional<HideDisplayCutoutController> mHideDisplayCutout;
|
||||
private final Optional<AppPairsController> mAppPairsOptional;
|
||||
private final Optional<RecentTasksController> mRecentTasks;
|
||||
private final ShellTaskOrganizer mShellTaskOrganizer;
|
||||
private final ShellExecutor mMainExecutor;
|
||||
private final HandlerImpl mImpl = new HandlerImpl();
|
||||
@@ -55,8 +57,10 @@ public final class ShellCommandHandlerImpl {
|
||||
Optional<OneHandedController> oneHandedOptional,
|
||||
Optional<HideDisplayCutoutController> hideDisplayCutout,
|
||||
Optional<AppPairsController> appPairsOptional,
|
||||
Optional<RecentTasksController> recentTasks,
|
||||
ShellExecutor mainExecutor) {
|
||||
mShellTaskOrganizer = shellTaskOrganizer;
|
||||
mRecentTasks = recentTasks;
|
||||
mLegacySplitScreenOptional = legacySplitScreenOptional;
|
||||
mSplitScreenOptional = splitScreenOptional;
|
||||
mPipOptional = pipOptional;
|
||||
@@ -85,6 +89,9 @@ public final class ShellCommandHandlerImpl {
|
||||
pw.println();
|
||||
pw.println();
|
||||
mSplitScreenOptional.ifPresent(splitScreen -> splitScreen.dump(pw, ""));
|
||||
pw.println();
|
||||
pw.println();
|
||||
mRecentTasks.ifPresent(recentTasks -> recentTasks.dump(pw, ""));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -29,8 +29,8 @@ import com.android.wm.shell.draganddrop.DragAndDropController;
|
||||
import com.android.wm.shell.freeform.FreeformTaskListener;
|
||||
import com.android.wm.shell.fullscreen.FullscreenTaskListener;
|
||||
import com.android.wm.shell.fullscreen.FullscreenUnfoldController;
|
||||
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
|
||||
import com.android.wm.shell.pip.phone.PipTouchHandler;
|
||||
import com.android.wm.shell.recents.RecentTasksController;
|
||||
import com.android.wm.shell.splitscreen.SplitScreenController;
|
||||
import com.android.wm.shell.startingsurface.StartingWindowController;
|
||||
import com.android.wm.shell.transition.Transitions;
|
||||
@@ -49,7 +49,6 @@ public class ShellInitImpl {
|
||||
private final DragAndDropController mDragAndDropController;
|
||||
private final ShellTaskOrganizer mShellTaskOrganizer;
|
||||
private final Optional<BubbleController> mBubblesOptional;
|
||||
private final Optional<LegacySplitScreenController> mLegacySplitScreenOptional;
|
||||
private final Optional<SplitScreenController> mSplitScreenOptional;
|
||||
private final Optional<AppPairsController> mAppPairsOptional;
|
||||
private final Optional<PipTouchHandler> mPipTouchHandlerOptional;
|
||||
@@ -59,6 +58,7 @@ public class ShellInitImpl {
|
||||
private final ShellExecutor mMainExecutor;
|
||||
private final Transitions mTransitions;
|
||||
private final StartingWindowController mStartingWindow;
|
||||
private final Optional<RecentTasksController> mRecentTasks;
|
||||
|
||||
private final InitImpl mImpl = new InitImpl();
|
||||
|
||||
@@ -69,13 +69,13 @@ public class ShellInitImpl {
|
||||
DragAndDropController dragAndDropController,
|
||||
ShellTaskOrganizer shellTaskOrganizer,
|
||||
Optional<BubbleController> bubblesOptional,
|
||||
Optional<LegacySplitScreenController> legacySplitScreenOptional,
|
||||
Optional<SplitScreenController> splitScreenOptional,
|
||||
Optional<AppPairsController> appPairsOptional,
|
||||
Optional<PipTouchHandler> pipTouchHandlerOptional,
|
||||
FullscreenTaskListener fullscreenTaskListener,
|
||||
Optional<FullscreenUnfoldController> fullscreenUnfoldTransitionController,
|
||||
Optional<Optional<FreeformTaskListener>> freeformTaskListenerOptional,
|
||||
Optional<RecentTasksController> recentTasks,
|
||||
Transitions transitions,
|
||||
StartingWindowController startingWindow,
|
||||
ShellExecutor mainExecutor) {
|
||||
@@ -85,13 +85,13 @@ public class ShellInitImpl {
|
||||
mDragAndDropController = dragAndDropController;
|
||||
mShellTaskOrganizer = shellTaskOrganizer;
|
||||
mBubblesOptional = bubblesOptional;
|
||||
mLegacySplitScreenOptional = legacySplitScreenOptional;
|
||||
mSplitScreenOptional = splitScreenOptional;
|
||||
mAppPairsOptional = appPairsOptional;
|
||||
mFullscreenTaskListener = fullscreenTaskListener;
|
||||
mPipTouchHandlerOptional = pipTouchHandlerOptional;
|
||||
mFullscreenUnfoldController = fullscreenUnfoldTransitionController;
|
||||
mFreeformTaskListenerOptional = freeformTaskListenerOptional.flatMap(f -> f);
|
||||
mRecentTasks = recentTasks;
|
||||
mTransitions = transitions;
|
||||
mMainExecutor = mainExecutor;
|
||||
mStartingWindow = startingWindow;
|
||||
@@ -135,6 +135,7 @@ public class ShellInitImpl {
|
||||
f, ShellTaskOrganizer.TASK_LISTENER_TYPE_FREEFORM));
|
||||
|
||||
mFullscreenUnfoldController.ifPresent(FullscreenUnfoldController::init);
|
||||
mRecentTasks.ifPresent(RecentTasksController::init);
|
||||
}
|
||||
|
||||
@ExternalThread
|
||||
|
||||
@@ -51,6 +51,7 @@ import com.android.internal.protolog.common.ProtoLog;
|
||||
import com.android.internal.util.FrameworkStatsLog;
|
||||
import com.android.wm.shell.common.ScreenshotUtils;
|
||||
import com.android.wm.shell.common.ShellExecutor;
|
||||
import com.android.wm.shell.recents.RecentTasksController;
|
||||
import com.android.wm.shell.sizecompatui.SizeCompatUIController;
|
||||
import com.android.wm.shell.startingsurface.StartingWindowController;
|
||||
|
||||
@@ -59,6 +60,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
@@ -150,20 +152,34 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
|
||||
@Nullable
|
||||
private final SizeCompatUIController mSizeCompatUI;
|
||||
|
||||
@Nullable
|
||||
private final Optional<RecentTasksController> mRecentTasks;
|
||||
|
||||
public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context) {
|
||||
this(null /* taskOrganizerController */, mainExecutor, context, null /* sizeCompatUI */);
|
||||
this(null /* taskOrganizerController */, mainExecutor, context, null /* sizeCompatUI */,
|
||||
Optional.empty() /* recentTasksController */);
|
||||
}
|
||||
|
||||
public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable
|
||||
SizeCompatUIController sizeCompatUI) {
|
||||
this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI);
|
||||
this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI,
|
||||
Optional.empty() /* recentTasksController */);
|
||||
}
|
||||
|
||||
public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable
|
||||
SizeCompatUIController sizeCompatUI,
|
||||
Optional<RecentTasksController> recentTasks) {
|
||||
this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI,
|
||||
recentTasks);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController, ShellExecutor mainExecutor,
|
||||
Context context, @Nullable SizeCompatUIController sizeCompatUI) {
|
||||
Context context, @Nullable SizeCompatUIController sizeCompatUI,
|
||||
Optional<RecentTasksController> recentTasks) {
|
||||
super(taskOrganizerController, mainExecutor);
|
||||
mSizeCompatUI = sizeCompatUI;
|
||||
mRecentTasks = recentTasks;
|
||||
if (sizeCompatUI != null) {
|
||||
sizeCompatUI.setSizeCompatUICallback(this);
|
||||
}
|
||||
@@ -401,6 +417,11 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
|
||||
// Notify the size compat UI if the listener or task info changed.
|
||||
notifySizeCompatUI(taskInfo, newListener);
|
||||
}
|
||||
if (data.getTaskInfo().getWindowingMode() != taskInfo.getWindowingMode()) {
|
||||
// Notify the recent tasks when a task changes windowing modes
|
||||
mRecentTasks.ifPresent(recentTasks ->
|
||||
recentTasks.onTaskWindowingModeChanged(taskInfo));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -428,6 +449,8 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
|
||||
notifyLocusVisibilityIfNeeded(taskInfo);
|
||||
// Pass null for listener to remove the size compat UI on this task if there is any.
|
||||
notifySizeCompatUI(taskInfo, null /* taskListener */);
|
||||
// Notify the recent tasks that a task has been removed
|
||||
mRecentTasks.ifPresent(recentTasks -> recentTasks.onTaskRemoved(taskInfo));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.wm.shell.recents;
|
||||
|
||||
import com.android.wm.shell.recents.IRecentTasksListener;
|
||||
import com.android.wm.shell.util.GroupedRecentTaskInfo;
|
||||
|
||||
/**
|
||||
* Interface that is exposed to remote callers to fetch recent tasks.
|
||||
*/
|
||||
interface IRecentTasks {
|
||||
|
||||
/**
|
||||
* Registers a recent tasks listener.
|
||||
*/
|
||||
oneway void registerRecentTasksListener(in IRecentTasksListener listener) = 1;
|
||||
|
||||
/**
|
||||
* Unregisters a recent tasks listener.
|
||||
*/
|
||||
oneway void unregisterRecentTasksListener(in IRecentTasksListener listener) = 2;
|
||||
|
||||
/**
|
||||
* Gets the set of recent tasks.
|
||||
*/
|
||||
GroupedRecentTaskInfo[] getRecentTasks(int maxNum, int flags, int userId) = 3;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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 distshellributed 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.wm.shell.recents;
|
||||
|
||||
/**
|
||||
* Listener interface that Launcher attaches to SystemUI to get split-screen callbacks.
|
||||
*/
|
||||
oneway interface IRecentTasksListener {
|
||||
|
||||
/**
|
||||
* Called when the set of recent tasks change.
|
||||
*/
|
||||
void onRecentTasksChanged();
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.wm.shell.recents;
|
||||
|
||||
import com.android.wm.shell.common.annotations.ExternalThread;
|
||||
|
||||
/**
|
||||
* Interface for interacting with the recent tasks.
|
||||
*/
|
||||
@ExternalThread
|
||||
public interface RecentTasks {
|
||||
/**
|
||||
* Returns a binder that can be passed to an external process to fetch recent tasks.
|
||||
*/
|
||||
default IRecentTasks createExternalInterface() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.wm.shell.recents;
|
||||
|
||||
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
|
||||
|
||||
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityTaskManager;
|
||||
import android.app.TaskInfo;
|
||||
import android.content.Context;
|
||||
import android.os.RemoteException;
|
||||
import android.util.SparseArray;
|
||||
import android.util.SparseIntArray;
|
||||
|
||||
import androidx.annotation.BinderThread;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.wm.shell.common.RemoteCallable;
|
||||
import com.android.wm.shell.common.ShellExecutor;
|
||||
import com.android.wm.shell.common.SingleInstanceRemoteListener;
|
||||
import com.android.wm.shell.common.TaskStackListenerCallback;
|
||||
import com.android.wm.shell.common.TaskStackListenerImpl;
|
||||
import com.android.wm.shell.common.annotations.ExternalThread;
|
||||
import com.android.wm.shell.common.annotations.ShellMainThread;
|
||||
import com.android.wm.shell.util.GroupedRecentTaskInfo;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Manages the recent task list from the system, caching it as necessary.
|
||||
*/
|
||||
public class RecentTasksController implements TaskStackListenerCallback,
|
||||
RemoteCallable<RecentTasksController> {
|
||||
private static final String TAG = RecentTasksController.class.getSimpleName();
|
||||
|
||||
private final Context mContext;
|
||||
private final ShellExecutor mMainExecutor;
|
||||
private final TaskStackListenerImpl mTaskStackListener;
|
||||
private final RecentTasks mImpl = new RecentTasksImpl();
|
||||
|
||||
private final ArrayList<Runnable> mCallbacks = new ArrayList<>();
|
||||
// Mapping of split task ids, mappings are symmetrical (ie. if t1 is the taskid of a task in a
|
||||
// pair, then mSplitTasks[t1] = t2, and mSplitTasks[t2] = t1)
|
||||
private final SparseIntArray mSplitTasks = new SparseIntArray();
|
||||
|
||||
/**
|
||||
* Creates {@link RecentTasksController}, returns {@code null} if the feature is not
|
||||
* supported.
|
||||
*/
|
||||
@Nullable
|
||||
public static RecentTasksController create(
|
||||
Context context,
|
||||
TaskStackListenerImpl taskStackListener,
|
||||
@ShellMainThread ShellExecutor mainExecutor
|
||||
) {
|
||||
if (!context.getResources().getBoolean(com.android.internal.R.bool.config_hasRecents)) {
|
||||
return null;
|
||||
}
|
||||
return new RecentTasksController(context, taskStackListener, mainExecutor);
|
||||
}
|
||||
|
||||
RecentTasksController(Context context, TaskStackListenerImpl taskStackListener,
|
||||
ShellExecutor mainExecutor) {
|
||||
mContext = context;
|
||||
mTaskStackListener = taskStackListener;
|
||||
mMainExecutor = mainExecutor;
|
||||
}
|
||||
|
||||
public RecentTasks asRecentTasks() {
|
||||
return mImpl;
|
||||
}
|
||||
|
||||
public void init() {
|
||||
mTaskStackListener.addListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a split pair. This call does not validate the taskIds, only that they are not the same.
|
||||
*/
|
||||
public void addSplitPair(int taskId1, int taskId2) {
|
||||
if (taskId1 == taskId2) {
|
||||
return;
|
||||
}
|
||||
// Remove any previous pairs
|
||||
removeSplitPair(taskId1);
|
||||
removeSplitPair(taskId2);
|
||||
mSplitTasks.put(taskId1, taskId2);
|
||||
mSplitTasks.put(taskId2, taskId1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a split pair.
|
||||
*/
|
||||
public void removeSplitPair(int taskId) {
|
||||
int pairedTaskId = mSplitTasks.get(taskId, INVALID_TASK_ID);
|
||||
if (pairedTaskId != INVALID_TASK_ID) {
|
||||
mSplitTasks.delete(taskId);
|
||||
mSplitTasks.delete(pairedTaskId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context getContext() {
|
||||
return mContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ShellExecutor getRemoteCallExecutor() {
|
||||
return mMainExecutor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskStackChanged() {
|
||||
notifyRecentTasksChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRecentTaskListUpdated() {
|
||||
// In some cases immediately after booting, the tasks in the system recent task list may be
|
||||
// loaded, but not in the active task hierarchy in the system. These tasks are displayed in
|
||||
// overview, but removing them don't result in a onTaskStackChanged() nor a onTaskRemoved()
|
||||
// callback (those are for changes to the active tasks), but the task list is still updated,
|
||||
// so we should also invalidate the change id to ensure we load a new list instead of
|
||||
// reusing a stale list.
|
||||
notifyRecentTasksChanged();
|
||||
}
|
||||
|
||||
public void onTaskRemoved(TaskInfo taskInfo) {
|
||||
// Remove any split pairs associated with this task
|
||||
removeSplitPair(taskInfo.taskId);
|
||||
notifyRecentTasksChanged();
|
||||
}
|
||||
|
||||
public void onTaskWindowingModeChanged(TaskInfo taskInfo) {
|
||||
notifyRecentTasksChanged();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void notifyRecentTasksChanged() {
|
||||
for (int i = 0; i < mCallbacks.size(); i++) {
|
||||
mCallbacks.get(i).run();
|
||||
}
|
||||
}
|
||||
|
||||
private void registerRecentTasksListener(Runnable listener) {
|
||||
if (!mCallbacks.contains(listener)) {
|
||||
mCallbacks.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
private void unregisterRecentTasksListener(Runnable listener) {
|
||||
mCallbacks.remove(listener);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
List<ActivityManager.RecentTaskInfo> getRawRecentTasks(int maxNum, int flags, int userId) {
|
||||
return ActivityTaskManager.getInstance().getRecentTasks(maxNum, flags, userId);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
ArrayList<GroupedRecentTaskInfo> getRecentTasks(int maxNum, int flags, int userId) {
|
||||
// Note: the returned task list is from the most-recent to least-recent order
|
||||
final List<ActivityManager.RecentTaskInfo> rawList = getRawRecentTasks(maxNum, flags,
|
||||
userId);
|
||||
|
||||
// Make a mapping of task id -> task info
|
||||
final SparseArray<ActivityManager.RecentTaskInfo> rawMapping = new SparseArray<>();
|
||||
for (int i = 0; i < rawList.size(); i++) {
|
||||
final ActivityManager.RecentTaskInfo taskInfo = rawList.get(i);
|
||||
rawMapping.put(taskInfo.taskId, taskInfo);
|
||||
}
|
||||
|
||||
// Pull out the pairs as we iterate back in the list
|
||||
ArrayList<GroupedRecentTaskInfo> recentTasks = new ArrayList<>();
|
||||
for (int i = 0; i < rawList.size(); i++) {
|
||||
final ActivityManager.RecentTaskInfo taskInfo = rawList.get(i);
|
||||
if (!rawMapping.contains(taskInfo.taskId)) {
|
||||
// If it's not in the mapping, then it was already paired with another task
|
||||
continue;
|
||||
}
|
||||
|
||||
final int pairedTaskId = mSplitTasks.get(taskInfo.taskId);
|
||||
if (pairedTaskId != INVALID_TASK_ID) {
|
||||
final ActivityManager.RecentTaskInfo pairedTaskInfo = rawMapping.get(pairedTaskId);
|
||||
rawMapping.remove(pairedTaskId);
|
||||
recentTasks.add(new GroupedRecentTaskInfo(taskInfo, pairedTaskInfo));
|
||||
} else {
|
||||
recentTasks.add(new GroupedRecentTaskInfo(taskInfo));
|
||||
}
|
||||
}
|
||||
return recentTasks;
|
||||
}
|
||||
|
||||
public void dump(@NonNull PrintWriter pw, String prefix) {
|
||||
final String innerPrefix = prefix + " ";
|
||||
pw.println(prefix + TAG);
|
||||
ArrayList<GroupedRecentTaskInfo> recentTasks = getRecentTasks(Integer.MAX_VALUE,
|
||||
ActivityManager.RECENT_IGNORE_UNAVAILABLE, ActivityManager.getCurrentUser());
|
||||
for (int i = 0; i < recentTasks.size(); i++) {
|
||||
pw.println(innerPrefix + recentTasks.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The interface for calls from outside the Shell, within the host process.
|
||||
*/
|
||||
@ExternalThread
|
||||
private class RecentTasksImpl implements RecentTasks {
|
||||
private IRecentTasksImpl mIRecentTasks;
|
||||
|
||||
@Override
|
||||
public IRecentTasks createExternalInterface() {
|
||||
if (mIRecentTasks != null) {
|
||||
mIRecentTasks.invalidate();
|
||||
}
|
||||
mIRecentTasks = new IRecentTasksImpl(RecentTasksController.this);
|
||||
return mIRecentTasks;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The interface for calls from outside the host process.
|
||||
*/
|
||||
@BinderThread
|
||||
private static class IRecentTasksImpl extends IRecentTasks.Stub {
|
||||
private RecentTasksController mController;
|
||||
private final SingleInstanceRemoteListener<RecentTasksController,
|
||||
IRecentTasksListener> mListener;
|
||||
private final Runnable mRecentTasksListener =
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mListener.call(l -> l.onRecentTasksChanged());
|
||||
}
|
||||
};
|
||||
|
||||
public IRecentTasksImpl(RecentTasksController controller) {
|
||||
mController = controller;
|
||||
mListener = new SingleInstanceRemoteListener<>(controller,
|
||||
c -> c.registerRecentTasksListener(mRecentTasksListener),
|
||||
c -> c.unregisterRecentTasksListener(mRecentTasksListener));
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates this instance, preventing future calls from updating the controller.
|
||||
*/
|
||||
void invalidate() {
|
||||
mController = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerRecentTasksListener(IRecentTasksListener listener)
|
||||
throws RemoteException {
|
||||
executeRemoteCallWithTaskPermission(mController, "registerRecentTasksListener",
|
||||
(controller) -> mListener.register(listener));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterRecentTasksListener(IRecentTasksListener listener)
|
||||
throws RemoteException {
|
||||
executeRemoteCallWithTaskPermission(mController, "unregisterRecentTasksListener",
|
||||
(controller) -> mListener.unregister());
|
||||
}
|
||||
|
||||
@Override
|
||||
public GroupedRecentTaskInfo[] getRecentTasks(int maxNum, int flags, int userId)
|
||||
throws RemoteException {
|
||||
final GroupedRecentTaskInfo[][] out = new GroupedRecentTaskInfo[][]{null};
|
||||
executeRemoteCallWithTaskPermission(mController, "getRecentTasks",
|
||||
(controller) -> out[0] = controller.getRecentTasks(maxNum, flags, userId)
|
||||
.toArray(new GroupedRecentTaskInfo[0]),
|
||||
true /* blocking */);
|
||||
return out[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.wm.shell.util;
|
||||
|
||||
parcelable GroupedRecentTaskInfo;
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.wm.shell.util;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.WindowConfiguration;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Simple container for recent tasks. May contain either a single or pair of tasks.
|
||||
*/
|
||||
public class GroupedRecentTaskInfo implements Parcelable {
|
||||
public @NonNull ActivityManager.RecentTaskInfo mTaskInfo1;
|
||||
public @Nullable ActivityManager.RecentTaskInfo mTaskInfo2;
|
||||
|
||||
public GroupedRecentTaskInfo(@NonNull ActivityManager.RecentTaskInfo task1) {
|
||||
this(task1, null);
|
||||
}
|
||||
|
||||
public GroupedRecentTaskInfo(@NonNull ActivityManager.RecentTaskInfo task1,
|
||||
@Nullable ActivityManager.RecentTaskInfo task2) {
|
||||
mTaskInfo1 = task1;
|
||||
mTaskInfo2 = task2;
|
||||
}
|
||||
|
||||
GroupedRecentTaskInfo(Parcel parcel) {
|
||||
mTaskInfo1 = parcel.readTypedObject(ActivityManager.RecentTaskInfo.CREATOR);
|
||||
mTaskInfo2 = parcel.readTypedObject(ActivityManager.RecentTaskInfo.CREATOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Task1: " + getTaskInfo(mTaskInfo1) + ", Task2: " + getTaskInfo(mTaskInfo2);
|
||||
}
|
||||
|
||||
private String getTaskInfo(ActivityManager.RecentTaskInfo taskInfo) {
|
||||
if (taskInfo == null) {
|
||||
return null;
|
||||
}
|
||||
return "id=" + taskInfo.taskId
|
||||
+ " baseIntent=" + (taskInfo.baseIntent != null
|
||||
? taskInfo.baseIntent.getComponent()
|
||||
: "null")
|
||||
+ " winMode=" + WindowConfiguration.windowingModeToString(
|
||||
taskInfo.getWindowingMode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel parcel, int flags) {
|
||||
parcel.writeTypedObject(mTaskInfo1, flags);
|
||||
parcel.writeTypedObject(mTaskInfo2, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static final @android.annotation.NonNull Creator<GroupedRecentTaskInfo> CREATOR =
|
||||
new Creator<GroupedRecentTaskInfo>() {
|
||||
public GroupedRecentTaskInfo createFromParcel(Parcel source) {
|
||||
return new GroupedRecentTaskInfo(source);
|
||||
}
|
||||
public GroupedRecentTaskInfo[] newArray(int size) {
|
||||
return new GroupedRecentTaskInfo[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -65,6 +65,7 @@ import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Tests for the shell task organizer.
|
||||
@@ -131,7 +132,7 @@ public class ShellTaskOrganizerTests {
|
||||
.when(mTaskOrganizerController).registerTaskOrganizer(any());
|
||||
} catch (RemoteException e) {}
|
||||
mOrganizer = spy(new ShellTaskOrganizer(mTaskOrganizerController, mTestExecutor, mContext,
|
||||
mSizeCompatUI));
|
||||
mSizeCompatUI, Optional.empty()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -26,6 +26,7 @@ import androidx.test.InstrumentationRegistry;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
/**
|
||||
* Base class that does shell test case setup.
|
||||
@@ -36,6 +37,7 @@ public abstract class ShellTestCase {
|
||||
|
||||
@Before
|
||||
public void shellSetup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
final Context context =
|
||||
InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
final DisplayManager dm = context.getSystemService(DisplayManager.class);
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.wm.shell.recents;
|
||||
|
||||
import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import static java.lang.Integer.MAX_VALUE;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.WindowConfiguration;
|
||||
import android.content.Context;
|
||||
import android.view.SurfaceControl;
|
||||
import android.window.TaskAppearedInfo;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.wm.shell.ShellTaskOrganizer;
|
||||
import com.android.wm.shell.ShellTestCase;
|
||||
import com.android.wm.shell.TestShellExecutor;
|
||||
import com.android.wm.shell.common.ShellExecutor;
|
||||
import com.android.wm.shell.common.TaskStackListenerImpl;
|
||||
import com.android.wm.shell.util.GroupedRecentTaskInfo;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Tests for {@link RecentTasksController}.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class RecentTasksControllerTest extends ShellTestCase {
|
||||
|
||||
@Mock
|
||||
private Context mContext;
|
||||
@Mock
|
||||
private TaskStackListenerImpl mTaskStackListener;
|
||||
|
||||
private ShellTaskOrganizer mShellTaskOrganizer;
|
||||
private RecentTasksController mRecentTasksController;
|
||||
private ShellExecutor mMainExecutor;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mMainExecutor = new TestShellExecutor();
|
||||
mRecentTasksController = spy(new RecentTasksController(mContext, mTaskStackListener,
|
||||
mMainExecutor));
|
||||
mShellTaskOrganizer = new ShellTaskOrganizer(mMainExecutor, mContext,
|
||||
null /* sizeCompatUI */, Optional.of(mRecentTasksController));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRecentTasks() {
|
||||
ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
|
||||
ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
|
||||
ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
|
||||
setRawList(t1, t2, t3);
|
||||
|
||||
ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks(
|
||||
MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
|
||||
assertGroupedTasksListEquals(recentTasks,
|
||||
t1.taskId, -1,
|
||||
t2.taskId, -1,
|
||||
t3.taskId, -1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRecentTasks_withPairs() {
|
||||
ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
|
||||
ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
|
||||
ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
|
||||
ActivityManager.RecentTaskInfo t4 = makeTaskInfo(4);
|
||||
ActivityManager.RecentTaskInfo t5 = makeTaskInfo(5);
|
||||
ActivityManager.RecentTaskInfo t6 = makeTaskInfo(6);
|
||||
setRawList(t1, t2, t3, t4, t5, t6);
|
||||
|
||||
// Mark a couple pairs [t2, t4], [t3, t5]
|
||||
mRecentTasksController.addSplitPair(t2.taskId, t4.taskId);
|
||||
mRecentTasksController.addSplitPair(t3.taskId, t5.taskId);
|
||||
|
||||
ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks(
|
||||
MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
|
||||
assertGroupedTasksListEquals(recentTasks,
|
||||
t1.taskId, -1,
|
||||
t2.taskId, t4.taskId,
|
||||
t3.taskId, t5.taskId,
|
||||
t6.taskId, -1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemovedTaskRemovesSplit() {
|
||||
ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
|
||||
ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
|
||||
ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
|
||||
setRawList(t1, t2, t3);
|
||||
|
||||
// Add a pair
|
||||
mRecentTasksController.addSplitPair(t2.taskId, t3.taskId);
|
||||
reset(mRecentTasksController);
|
||||
|
||||
// Remove one of the tasks and ensure the pair is removed
|
||||
SurfaceControl mockLeash = mock(SurfaceControl.class);
|
||||
ActivityManager.RunningTaskInfo rt2 = makeRunningTaskInfo(2);
|
||||
mShellTaskOrganizer.onTaskAppeared(rt2, mockLeash);
|
||||
mShellTaskOrganizer.onTaskVanished(rt2);
|
||||
|
||||
verify(mRecentTasksController).removeSplitPair(t2.taskId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTaskWindowingModeChangedNotifiesChange() {
|
||||
ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
|
||||
setRawList(t1);
|
||||
|
||||
// Remove one of the tasks and ensure the pair is removed
|
||||
SurfaceControl mockLeash = mock(SurfaceControl.class);
|
||||
ActivityManager.RunningTaskInfo rt2Fullscreen = makeRunningTaskInfo(2);
|
||||
rt2Fullscreen.configuration.windowConfiguration.setWindowingMode(
|
||||
WINDOWING_MODE_FULLSCREEN);
|
||||
mShellTaskOrganizer.onTaskAppeared(rt2Fullscreen, mockLeash);
|
||||
|
||||
// Change the windowing mode and ensure the recent tasks change is notified
|
||||
ActivityManager.RunningTaskInfo rt2MultiWIndow = makeRunningTaskInfo(2);
|
||||
rt2MultiWIndow.configuration.windowConfiguration.setWindowingMode(
|
||||
WINDOWING_MODE_MULTI_WINDOW);
|
||||
mShellTaskOrganizer.onTaskInfoChanged(rt2MultiWIndow);
|
||||
|
||||
verify(mRecentTasksController).notifyRecentTasksChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to create a task with a given task id.
|
||||
*/
|
||||
private ActivityManager.RecentTaskInfo makeTaskInfo(int taskId) {
|
||||
ActivityManager.RecentTaskInfo info = new ActivityManager.RecentTaskInfo();
|
||||
info.taskId = taskId;
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to create a running task with a given task id.
|
||||
*/
|
||||
private ActivityManager.RunningTaskInfo makeRunningTaskInfo(int taskId) {
|
||||
ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
|
||||
info.taskId = taskId;
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to set the raw task list on the controller.
|
||||
*/
|
||||
private ArrayList<ActivityManager.RecentTaskInfo> setRawList(
|
||||
ActivityManager.RecentTaskInfo... tasks) {
|
||||
ArrayList<ActivityManager.RecentTaskInfo> rawList = new ArrayList<>();
|
||||
for (ActivityManager.RecentTaskInfo task : tasks) {
|
||||
rawList.add(task);
|
||||
}
|
||||
doReturn(rawList).when(mRecentTasksController).getRawRecentTasks(anyInt(), anyInt(),
|
||||
anyInt());
|
||||
return rawList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the recent tasks matches the given task ids.
|
||||
* @param expectedTaskIds list of task ids that map to the flattened task ids of the tasks in
|
||||
* the grouped task list
|
||||
*/
|
||||
private void assertGroupedTasksListEquals(ArrayList<GroupedRecentTaskInfo> recentTasks,
|
||||
int... expectedTaskIds) {
|
||||
int[] flattenedTaskIds = new int[recentTasks.size() * 2];
|
||||
for (int i = 0; i < recentTasks.size(); i++) {
|
||||
GroupedRecentTaskInfo pair = recentTasks.get(i);
|
||||
flattenedTaskIds[2 * i] = pair.mTaskInfo1.taskId;
|
||||
flattenedTaskIds[2 * i + 1] = pair.mTaskInfo2 != null
|
||||
? pair.mTaskInfo2.taskId
|
||||
: -1;
|
||||
}
|
||||
assertTrue("Expected: " + Arrays.toString(expectedTaskIds)
|
||||
+ " Received: " + Arrays.toString(flattenedTaskIds),
|
||||
Arrays.equals(flattenedTaskIds, expectedTaskIds));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.shared.recents.model;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* A group task in the recent tasks list.
|
||||
* TODO: Move this into Launcher
|
||||
*/
|
||||
public class GroupTask {
|
||||
public @NonNull Task task1;
|
||||
public @Nullable Task task2;
|
||||
|
||||
public GroupTask(@NonNull Task t1, @Nullable Task t2) {
|
||||
task1 = t1;
|
||||
task2 = t2;
|
||||
}
|
||||
|
||||
public GroupTask(@NonNull GroupTask group) {
|
||||
task1 = new Task(group.task1);
|
||||
task2 = group.task2 != null
|
||||
? new Task(group.task2)
|
||||
: null;
|
||||
}
|
||||
|
||||
public boolean containsTask(int taskId) {
|
||||
return task1.key.id == taskId || (task2 != null && task2.key.id == taskId);
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,7 @@ import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A task in the recent tasks list.
|
||||
* TODO: Move this into Launcher or see if we can remove now
|
||||
*/
|
||||
public class Task {
|
||||
|
||||
|
||||
@@ -57,6 +57,8 @@ public class QuickStepContract {
|
||||
"extra_shell_starting_window";
|
||||
// See ISmartspaceTransitionController.aidl
|
||||
public static final String KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER = "smartspace_transition";
|
||||
// See IRecentTasks.aidl
|
||||
public static final String KEY_EXTRA_RECENT_TASKS = "recent_tasks";
|
||||
|
||||
public static final String NAV_BAR_MODE_2BUTTON_OVERLAY =
|
||||
WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY;
|
||||
|
||||
@@ -32,6 +32,7 @@ import com.android.systemui.navigationbar.gestural.BackGestureTfClassifierProvid
|
||||
import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider;
|
||||
import com.android.wm.shell.transition.ShellTransitions;
|
||||
import com.android.wm.shell.transition.Transitions;
|
||||
import com.android.wm.shell.recents.RecentTasks;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
@@ -120,7 +121,8 @@ public class SystemUIFactory {
|
||||
.setTransitions(mWMComponent.getTransitions())
|
||||
.setStartingSurface(mWMComponent.getStartingSurface())
|
||||
.setDisplayAreaHelper(mWMComponent.getDisplayAreaHelper())
|
||||
.setTaskSurfaceHelper(mWMComponent.getTaskSurfaceHelper());
|
||||
.setTaskSurfaceHelper(mWMComponent.getTaskSurfaceHelper())
|
||||
.setRecentTasks(mWMComponent.getRecentTasks());
|
||||
} else {
|
||||
// TODO: Call on prepareSysUIComponentBuilder but not with real components. Other option
|
||||
// is separating this logic into newly creating SystemUITestsFactory.
|
||||
@@ -137,7 +139,8 @@ public class SystemUIFactory {
|
||||
.setTransitions(new ShellTransitions() {})
|
||||
.setDisplayAreaHelper(Optional.ofNullable(null))
|
||||
.setStartingSurface(Optional.ofNullable(null))
|
||||
.setTaskSurfaceHelper(Optional.ofNullable(null));
|
||||
.setTaskSurfaceHelper(Optional.ofNullable(null))
|
||||
.setRecentTasks(Optional.ofNullable(null));
|
||||
}
|
||||
mSysUIComponent = builder.build();
|
||||
if (mInitializeComponents) {
|
||||
|
||||
@@ -34,6 +34,7 @@ import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
|
||||
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
|
||||
import com.android.wm.shell.onehanded.OneHanded;
|
||||
import com.android.wm.shell.pip.Pip;
|
||||
import com.android.wm.shell.recents.RecentTasks;
|
||||
import com.android.wm.shell.splitscreen.SplitScreen;
|
||||
import com.android.wm.shell.startingsurface.StartingSurface;
|
||||
import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelper;
|
||||
@@ -101,6 +102,9 @@ public interface SysUIComponent {
|
||||
@BindsInstance
|
||||
Builder setTaskSurfaceHelper(Optional<TaskSurfaceHelper> t);
|
||||
|
||||
@BindsInstance
|
||||
Builder setRecentTasks(Optional<RecentTasks> r);
|
||||
|
||||
SysUIComponent build();
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout;
|
||||
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
|
||||
import com.android.wm.shell.onehanded.OneHanded;
|
||||
import com.android.wm.shell.pip.Pip;
|
||||
import com.android.wm.shell.recents.RecentTasks;
|
||||
import com.android.wm.shell.splitscreen.SplitScreen;
|
||||
import com.android.wm.shell.startingsurface.StartingSurface;
|
||||
import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelper;
|
||||
@@ -110,4 +111,7 @@ public interface WMComponent {
|
||||
|
||||
@WMSingleton
|
||||
Optional<TaskSurfaceHelper> getTaskSurfaceHelper();
|
||||
|
||||
@WMSingleton
|
||||
Optional<RecentTasks> getRecentTasks();
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_OVERVIEW;
|
||||
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
|
||||
|
||||
import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_RECENT_TASKS;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_ONE_HANDED;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_PIP;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
|
||||
@@ -105,6 +106,7 @@ import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
|
||||
import com.android.wm.shell.onehanded.OneHanded;
|
||||
import com.android.wm.shell.pip.Pip;
|
||||
import com.android.wm.shell.pip.PipAnimationController;
|
||||
import com.android.wm.shell.recents.RecentTasks;
|
||||
import com.android.wm.shell.splitscreen.SplitScreen;
|
||||
import com.android.wm.shell.startingsurface.StartingSurface;
|
||||
import com.android.wm.shell.transition.ShellTransitions;
|
||||
@@ -158,6 +160,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
|
||||
private final ShellTransitions mShellTransitions;
|
||||
private final Optional<StartingSurface> mStartingSurface;
|
||||
private final SmartspaceTransitionController mSmartspaceTransitionController;
|
||||
private final Optional<RecentTasks> mRecentTasks;
|
||||
|
||||
private Region mActiveNavBarRegion;
|
||||
|
||||
@@ -491,6 +494,9 @@ public class OverviewProxyService extends CurrentUserTracker implements
|
||||
params.putBinder(
|
||||
KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER,
|
||||
mSmartspaceTransitionController.createExternalInterface().asBinder());
|
||||
mRecentTasks.ifPresent(recentTasks -> params.putBinder(
|
||||
KEY_EXTRA_RECENT_TASKS,
|
||||
recentTasks.createExternalInterface().asBinder()));
|
||||
|
||||
try {
|
||||
mOverviewProxy.onInitialize(params);
|
||||
@@ -541,17 +547,18 @@ public class OverviewProxyService extends CurrentUserTracker implements
|
||||
@Inject
|
||||
public OverviewProxyService(Context context, CommandQueue commandQueue,
|
||||
Lazy<NavigationBarController> navBarControllerLazy,
|
||||
Lazy<Optional<StatusBar>> statusBarOptionalLazy,
|
||||
NavigationModeController navModeController,
|
||||
NotificationShadeWindowController statusBarWinController, SysUiState sysUiState,
|
||||
Optional<Pip> pipOptional,
|
||||
Optional<LegacySplitScreen> legacySplitScreenOptional,
|
||||
Optional<SplitScreen> splitScreenOptional,
|
||||
Lazy<Optional<StatusBar>> statusBarOptionalLazy,
|
||||
Optional<OneHanded> oneHandedOptional,
|
||||
Optional<RecentTasks> recentTasks,
|
||||
Optional<StartingSurface> startingSurface,
|
||||
BroadcastDispatcher broadcastDispatcher,
|
||||
ShellTransitions shellTransitions,
|
||||
ScreenLifecycle screenLifecycle,
|
||||
Optional<StartingSurface> startingSurface,
|
||||
SmartspaceTransitionController smartspaceTransitionController,
|
||||
DumpManager dumpManager) {
|
||||
super(broadcastDispatcher);
|
||||
@@ -573,6 +580,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
|
||||
mSysUiState.addCallback(this::notifySystemUiStateFlags);
|
||||
mOneHandedOptional = oneHandedOptional;
|
||||
mShellTransitions = shellTransitions;
|
||||
mRecentTasks = recentTasks;
|
||||
|
||||
// Assumes device always starts with back button until launcher tells it that it does not
|
||||
mNavBarButtonAlpha = 1.0f;
|
||||
|
||||
@@ -74,6 +74,8 @@ import com.android.wm.shell.pip.PipSurfaceTransactionHelper;
|
||||
import com.android.wm.shell.pip.PipUiEventLogger;
|
||||
import com.android.wm.shell.pip.phone.PipAppOpsListener;
|
||||
import com.android.wm.shell.pip.phone.PipTouchHandler;
|
||||
import com.android.wm.shell.recents.RecentTasks;
|
||||
import com.android.wm.shell.recents.RecentTasksController;
|
||||
import com.android.wm.shell.sizecompatui.SizeCompatUIController;
|
||||
import com.android.wm.shell.splitscreen.SplitScreen;
|
||||
import com.android.wm.shell.splitscreen.SplitScreenController;
|
||||
@@ -144,8 +146,11 @@ public abstract class WMShellBaseModule {
|
||||
@WMSingleton
|
||||
@Provides
|
||||
static ShellTaskOrganizer provideShellTaskOrganizer(@ShellMainThread ShellExecutor mainExecutor,
|
||||
Context context, SizeCompatUIController sizeCompatUI) {
|
||||
return new ShellTaskOrganizer(mainExecutor, context, sizeCompatUI);
|
||||
Context context,
|
||||
SizeCompatUIController sizeCompatUI,
|
||||
Optional<RecentTasksController> recentTasksOptional
|
||||
) {
|
||||
return new ShellTaskOrganizer(mainExecutor, context, sizeCompatUI, recentTasksOptional);
|
||||
}
|
||||
|
||||
@WMSingleton
|
||||
@@ -409,6 +414,28 @@ public abstract class WMShellBaseModule {
|
||||
@BindsOptionalOf
|
||||
abstract PipTouchHandler optionalPipTouchHandler();
|
||||
|
||||
//
|
||||
// Recent tasks
|
||||
//
|
||||
|
||||
@WMSingleton
|
||||
@Provides
|
||||
static Optional<RecentTasks> provideRecentTasks(
|
||||
Optional<RecentTasksController> recentTasksController) {
|
||||
return recentTasksController.map((controller) -> controller.asRecentTasks());
|
||||
}
|
||||
|
||||
@WMSingleton
|
||||
@Provides
|
||||
static Optional<RecentTasksController> provideRecentTasksController(
|
||||
Context context,
|
||||
TaskStackListenerImpl taskStackListener,
|
||||
@ShellMainThread ShellExecutor mainExecutor
|
||||
) {
|
||||
return Optional.ofNullable(
|
||||
RecentTasksController.create(context, taskStackListener, mainExecutor));
|
||||
}
|
||||
|
||||
//
|
||||
// Shell transitions
|
||||
//
|
||||
@@ -555,13 +582,13 @@ public abstract class WMShellBaseModule {
|
||||
DragAndDropController dragAndDropController,
|
||||
ShellTaskOrganizer shellTaskOrganizer,
|
||||
Optional<BubbleController> bubblesOptional,
|
||||
Optional<LegacySplitScreenController> legacySplitScreenOptional,
|
||||
Optional<SplitScreenController> splitScreenOptional,
|
||||
Optional<AppPairsController> appPairsOptional,
|
||||
Optional<PipTouchHandler> pipTouchHandlerOptional,
|
||||
FullscreenTaskListener fullscreenTaskListener,
|
||||
Optional<FullscreenUnfoldController> appUnfoldTransitionController,
|
||||
Optional<Optional<FreeformTaskListener>> freeformTaskListener,
|
||||
Optional<RecentTasksController> recentTasksOptional,
|
||||
Transitions transitions,
|
||||
StartingWindowController startingWindow,
|
||||
@ShellMainThread ShellExecutor mainExecutor) {
|
||||
@@ -571,13 +598,13 @@ public abstract class WMShellBaseModule {
|
||||
dragAndDropController,
|
||||
shellTaskOrganizer,
|
||||
bubblesOptional,
|
||||
legacySplitScreenOptional,
|
||||
splitScreenOptional,
|
||||
appPairsOptional,
|
||||
pipTouchHandlerOptional,
|
||||
fullscreenTaskListener,
|
||||
appUnfoldTransitionController,
|
||||
freeformTaskListener,
|
||||
recentTasksOptional,
|
||||
transitions,
|
||||
startingWindow,
|
||||
mainExecutor);
|
||||
@@ -603,9 +630,10 @@ public abstract class WMShellBaseModule {
|
||||
Optional<OneHandedController> oneHandedOptional,
|
||||
Optional<HideDisplayCutoutController> hideDisplayCutout,
|
||||
Optional<AppPairsController> appPairsOptional,
|
||||
Optional<RecentTasksController> recentTasksOptional,
|
||||
@ShellMainThread ShellExecutor mainExecutor) {
|
||||
return new ShellCommandHandlerImpl(shellTaskOrganizer,
|
||||
legacySplitScreenOptional, splitScreenOptional, pipOptional, oneHandedOptional,
|
||||
hideDisplayCutout, appPairsOptional, mainExecutor);
|
||||
hideDisplayCutout, appPairsOptional, recentTasksOptional, mainExecutor);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user