Merge "Remove App pairs related codes and files" into tm-qpr-dev

This commit is contained in:
Tony Huang
2022-05-12 07:03:37 +00:00
committed by Android (Google) Code Review
26 changed files with 1 additions and 2113 deletions

View File

@@ -18,7 +18,6 @@ package com.android.wm.shell;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import com.android.wm.shell.apppairs.AppPairsController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController;
import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer;
@@ -42,7 +41,6 @@ public final class ShellCommandHandlerImpl {
private final Optional<Pip> mPipOptional;
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 KidsModeTaskOrganizer mKidsModeTaskOrganizer;
@@ -56,7 +54,6 @@ public final class ShellCommandHandlerImpl {
Optional<Pip> pipOptional,
Optional<OneHandedController> oneHandedOptional,
Optional<HideDisplayCutoutController> hideDisplayCutout,
Optional<AppPairsController> appPairsOptional,
Optional<RecentTasksController> recentTasks,
ShellExecutor mainExecutor) {
mShellTaskOrganizer = shellTaskOrganizer;
@@ -66,7 +63,6 @@ public final class ShellCommandHandlerImpl {
mPipOptional = pipOptional;
mOneHandedOptional = oneHandedOptional;
mHideDisplayCutout = hideDisplayCutout;
mAppPairsOptional = appPairsOptional;
mMainExecutor = mainExecutor;
}
@@ -84,9 +80,6 @@ public final class ShellCommandHandlerImpl {
mHideDisplayCutout.ifPresent(hideDisplayCutout -> hideDisplayCutout.dump(pw));
pw.println();
pw.println();
mAppPairsOptional.ifPresent(appPairs -> appPairs.dump(pw, ""));
pw.println();
pw.println();
mSplitScreenOptional.ifPresent(splitScreen -> splitScreen.dump(pw, ""));
pw.println();
pw.println();
@@ -104,10 +97,6 @@ public final class ShellCommandHandlerImpl {
return false;
}
switch (args[1]) {
case "pair":
return runPair(args, pw);
case "unpair":
return runUnpair(args, pw);
case "moveToSideStage":
return runMoveToSideStage(args, pw);
case "removeFromSideStage":
@@ -121,29 +110,6 @@ public final class ShellCommandHandlerImpl {
}
}
private boolean runPair(String[] args, PrintWriter pw) {
if (args.length < 4) {
// First two arguments are "WMShell" and command name.
pw.println("Error: two task ids should be provided as arguments");
return false;
}
final int taskId1 = new Integer(args[2]);
final int taskId2 = new Integer(args[3]);
mAppPairsOptional.ifPresent(appPairs -> appPairs.pair(taskId1, taskId2));
return true;
}
private boolean runUnpair(String[] args, PrintWriter pw) {
if (args.length < 3) {
// First two arguments are "WMShell" and command name.
pw.println("Error: task id should be provided as an argument");
return false;
}
final int taskId = new Integer(args[2]);
mAppPairsOptional.ifPresent(appPairs -> appPairs.unpair(taskId));
return true;
}
private boolean runMoveToSideStage(String[] args, PrintWriter pw) {
if (args.length < 3) {
// First arguments are "WMShell" and command name.

View File

@@ -18,7 +18,6 @@ package com.android.wm.shell;
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN;
import com.android.wm.shell.apppairs.AppPairsController;
import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
@@ -54,7 +53,6 @@ public class ShellInitImpl {
private final KidsModeTaskOrganizer mKidsModeTaskOrganizer;
private final Optional<BubbleController> mBubblesOptional;
private final Optional<SplitScreenController> mSplitScreenOptional;
private final Optional<AppPairsController> mAppPairsOptional;
private final Optional<PipTouchHandler> mPipTouchHandlerOptional;
private final FullscreenTaskListener mFullscreenTaskListener;
private final Optional<FullscreenUnfoldController> mFullscreenUnfoldController;
@@ -76,7 +74,6 @@ public class ShellInitImpl {
KidsModeTaskOrganizer kidsModeTaskOrganizer,
Optional<BubbleController> bubblesOptional,
Optional<SplitScreenController> splitScreenOptional,
Optional<AppPairsController> appPairsOptional,
Optional<PipTouchHandler> pipTouchHandlerOptional,
FullscreenTaskListener fullscreenTaskListener,
Optional<FullscreenUnfoldController> fullscreenUnfoldTransitionController,
@@ -94,7 +91,6 @@ public class ShellInitImpl {
mKidsModeTaskOrganizer = kidsModeTaskOrganizer;
mBubblesOptional = bubblesOptional;
mSplitScreenOptional = splitScreenOptional;
mAppPairsOptional = appPairsOptional;
mFullscreenTaskListener = fullscreenTaskListener;
mPipTouchHandlerOptional = pipTouchHandlerOptional;
mFullscreenUnfoldController = fullscreenUnfoldTransitionController;
@@ -122,7 +118,6 @@ public class ShellInitImpl {
mShellTaskOrganizer.initStartingWindow(mStartingWindow);
mShellTaskOrganizer.registerOrganizer();
mAppPairsOptional.ifPresent(AppPairsController::onOrganizerRegistered);
mSplitScreenOptional.ifPresent(SplitScreenController::onOrganizerRegistered);
mBubblesOptional.ifPresent(BubbleController::initialize);

View File

@@ -1,357 +0,0 @@
/*
* 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.apppairs;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
import android.app.ActivityManager;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.SurfaceUtils;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.split.SplitLayout;
import com.android.wm.shell.common.split.SplitWindowManager;
import java.io.PrintWriter;
/**
* An app-pairs consisting of {@link #mRootTaskInfo} that acts as the hierarchy parent of
* {@link #mTaskInfo1} and {@link #mTaskInfo2} in the pair.
* Also includes all UI for managing the pair like the divider.
*/
class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayoutHandler {
private static final String TAG = AppPair.class.getSimpleName();
private ActivityManager.RunningTaskInfo mRootTaskInfo;
private SurfaceControl mRootTaskLeash;
private ActivityManager.RunningTaskInfo mTaskInfo1;
private SurfaceControl mTaskLeash1;
private ActivityManager.RunningTaskInfo mTaskInfo2;
private SurfaceControl mTaskLeash2;
private SurfaceControl mDimLayer1;
private SurfaceControl mDimLayer2;
private final SurfaceSession mSurfaceSession = new SurfaceSession();
private final AppPairsController mController;
private final SyncTransactionQueue mSyncQueue;
private final DisplayController mDisplayController;
private final DisplayImeController mDisplayImeController;
private final DisplayInsetsController mDisplayInsetsController;
private SplitLayout mSplitLayout;
private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks =
new SplitWindowManager.ParentContainerCallbacks() {
@Override
public void attachToParentSurface(SurfaceControl.Builder b) {
b.setParent(mRootTaskLeash);
}
@Override
public void onLeashReady(SurfaceControl leash) {
mSyncQueue.runInSync(t -> t
.show(leash)
.setLayer(leash, Integer.MAX_VALUE)
.setPosition(leash,
mSplitLayout.getDividerBounds().left,
mSplitLayout.getDividerBounds().top));
}
};
AppPair(AppPairsController controller) {
mController = controller;
mSyncQueue = controller.getSyncTransactionQueue();
mDisplayController = controller.getDisplayController();
mDisplayImeController = controller.getDisplayImeController();
mDisplayInsetsController = controller.getDisplayInsetsController();
}
int getRootTaskId() {
return mRootTaskInfo != null ? mRootTaskInfo.taskId : INVALID_TASK_ID;
}
private int getTaskId1() {
return mTaskInfo1 != null ? mTaskInfo1.taskId : INVALID_TASK_ID;
}
private int getTaskId2() {
return mTaskInfo2 != null ? mTaskInfo2.taskId : INVALID_TASK_ID;
}
boolean contains(int taskId) {
return taskId == getRootTaskId() || taskId == getTaskId1() || taskId == getTaskId2();
}
boolean pair(ActivityManager.RunningTaskInfo task1, ActivityManager.RunningTaskInfo task2) {
ProtoLog.v(WM_SHELL_TASK_ORG, "pair task1=%d task2=%d in AppPair=%s",
task1.taskId, task2.taskId, this);
if (!task1.supportsMultiWindow || !task2.supportsMultiWindow) {
ProtoLog.e(WM_SHELL_TASK_ORG,
"Can't pair tasks that doesn't support multi window, "
+ "task1.supportsMultiWindow=%b, task2.supportsMultiWindow=%b",
task1.supportsMultiWindow, task2.supportsMultiWindow);
return false;
}
mTaskInfo1 = task1;
mTaskInfo2 = task2;
mSplitLayout = new SplitLayout(TAG + "SplitDivider",
mDisplayController.getDisplayContext(mRootTaskInfo.displayId),
mRootTaskInfo.configuration, this /* layoutChangeListener */,
mParentContainerCallbacks, mDisplayImeController, mController.getTaskOrganizer(),
SplitLayout.PARALLAX_DISMISSING);
mDisplayInsetsController.addInsetsChangedListener(mRootTaskInfo.displayId, mSplitLayout);
final WindowContainerToken token1 = task1.token;
final WindowContainerToken token2 = task2.token;
final WindowContainerTransaction wct = new WindowContainerTransaction();
wct.setHidden(mRootTaskInfo.token, false)
.reparent(token1, mRootTaskInfo.token, true /* onTop */)
.reparent(token2, mRootTaskInfo.token, true /* onTop */)
.setWindowingMode(token1, WINDOWING_MODE_MULTI_WINDOW)
.setWindowingMode(token2, WINDOWING_MODE_MULTI_WINDOW)
.setBounds(token1, mSplitLayout.getBounds1())
.setBounds(token2, mSplitLayout.getBounds2())
// Moving the root task to top after the child tasks were repareted , or the root
// task cannot be visible and focused.
.reorder(mRootTaskInfo.token, true);
mController.getTaskOrganizer().applyTransaction(wct);
return true;
}
void unpair() {
unpair(null /* toTopToken */);
}
private void unpair(@Nullable WindowContainerToken toTopToken) {
final WindowContainerToken token1 = mTaskInfo1.token;
final WindowContainerToken token2 = mTaskInfo2.token;
final WindowContainerTransaction wct = new WindowContainerTransaction();
// Reparent out of this container and reset windowing mode.
wct.setHidden(mRootTaskInfo.token, true)
.reorder(mRootTaskInfo.token, false)
.reparent(token1, null, token1 == toTopToken /* onTop */)
.reparent(token2, null, token2 == toTopToken /* onTop */)
.setWindowingMode(token1, WINDOWING_MODE_UNDEFINED)
.setWindowingMode(token2, WINDOWING_MODE_UNDEFINED);
mController.getTaskOrganizer().applyTransaction(wct);
mTaskInfo1 = null;
mTaskInfo2 = null;
mSplitLayout.release();
mSplitLayout = null;
}
@Override
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
if (mRootTaskInfo == null || taskInfo.taskId == mRootTaskInfo.taskId) {
mRootTaskInfo = taskInfo;
mRootTaskLeash = leash;
} else if (taskInfo.taskId == getTaskId1()) {
mTaskInfo1 = taskInfo;
mTaskLeash1 = leash;
mSyncQueue.runInSync(t -> mDimLayer1 =
SurfaceUtils.makeDimLayer(t, mTaskLeash1, "Dim layer", mSurfaceSession));
} else if (taskInfo.taskId == getTaskId2()) {
mTaskInfo2 = taskInfo;
mTaskLeash2 = leash;
mSyncQueue.runInSync(t -> mDimLayer2 =
SurfaceUtils.makeDimLayer(t, mTaskLeash2, "Dim layer", mSurfaceSession));
} else {
throw new IllegalStateException("Unknown task=" + taskInfo.taskId);
}
if (mTaskLeash1 == null || mTaskLeash2 == null) return;
mSplitLayout.init();
mSyncQueue.runInSync(t -> t
.show(mRootTaskLeash)
.show(mTaskLeash1)
.show(mTaskLeash2)
.setPosition(mTaskLeash1,
mTaskInfo1.positionInParent.x,
mTaskInfo1.positionInParent.y)
.setPosition(mTaskLeash2,
mTaskInfo2.positionInParent.x,
mTaskInfo2.positionInParent.y));
}
@Override
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
if (!taskInfo.supportsMultiWindow) {
// Dismiss AppPair if the task no longer supports multi window.
mController.unpair(mRootTaskInfo.taskId);
return;
}
if (taskInfo.taskId == getRootTaskId()) {
if (mRootTaskInfo.isVisible != taskInfo.isVisible) {
mSyncQueue.runInSync(t -> {
if (taskInfo.isVisible) {
t.show(mRootTaskLeash);
} else {
t.hide(mRootTaskLeash);
}
});
}
mRootTaskInfo = taskInfo;
if (mSplitLayout != null
&& mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)) {
onLayoutSizeChanged(mSplitLayout);
}
} else if (taskInfo.taskId == getTaskId1()) {
mTaskInfo1 = taskInfo;
} else if (taskInfo.taskId == getTaskId2()) {
mTaskInfo2 = taskInfo;
} else {
throw new IllegalStateException("Unknown task=" + taskInfo.taskId);
}
}
@Override
public int getSplitItemPosition(WindowContainerToken token) {
if (token == null) {
return SPLIT_POSITION_UNDEFINED;
}
if (token.equals(mTaskInfo1.getToken())) {
return SPLIT_POSITION_TOP_OR_LEFT;
} else if (token.equals(mTaskInfo2.getToken())) {
return SPLIT_POSITION_BOTTOM_OR_RIGHT;
}
return SPLIT_POSITION_UNDEFINED;
}
@Override
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
if (taskInfo.taskId == getRootTaskId()) {
// We don't want to release this object back to the pool since the root task went away.
mController.unpair(mRootTaskInfo.taskId, false /* releaseToPool */);
} else if (taskInfo.taskId == getTaskId1()) {
mController.unpair(mRootTaskInfo.taskId);
mSyncQueue.runInSync(t -> t.remove(mDimLayer1));
} else if (taskInfo.taskId == getTaskId2()) {
mController.unpair(mRootTaskInfo.taskId);
mSyncQueue.runInSync(t -> t.remove(mDimLayer2));
}
}
@Override
public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
b.setParent(findTaskSurface(taskId));
}
@Override
public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc,
SurfaceControl.Transaction t) {
t.reparent(sc, findTaskSurface(taskId));
}
private SurfaceControl findTaskSurface(int taskId) {
if (getRootTaskId() == taskId) {
return mRootTaskLeash;
} else if (getTaskId1() == taskId) {
return mTaskLeash1;
} else if (getTaskId2() == taskId) {
return mTaskLeash2;
} else {
throw new IllegalArgumentException("There is no surface for taskId=" + taskId);
}
}
@Override
public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
final String childPrefix = innerPrefix + " ";
pw.println(prefix + this);
if (mRootTaskInfo != null) {
pw.println(innerPrefix + "Root taskId=" + mRootTaskInfo.taskId
+ " winMode=" + mRootTaskInfo.getWindowingMode());
}
if (mTaskInfo1 != null) {
pw.println(innerPrefix + "1 taskId=" + mTaskInfo1.taskId
+ " winMode=" + mTaskInfo1.getWindowingMode());
}
if (mTaskInfo2 != null) {
pw.println(innerPrefix + "2 taskId=" + mTaskInfo2.taskId
+ " winMode=" + mTaskInfo2.getWindowingMode());
}
}
@Override
public String toString() {
return TAG + "#" + getRootTaskId();
}
@Override
public void onSnappedToDismiss(boolean snappedToEnd) {
unpair(snappedToEnd ? mTaskInfo1.token : mTaskInfo2.token /* toTopToken */);
}
@Override
public void onLayoutPositionChanging(SplitLayout layout) {
mSyncQueue.runInSync(t ->
layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2,
true /* applyResizingOffset */));
}
@Override
public void onLayoutSizeChanging(SplitLayout layout) {
mSyncQueue.runInSync(t ->
layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2,
true /* applyResizingOffset */));
}
@Override
public void onLayoutSizeChanged(SplitLayout layout) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
layout.applyTaskChanges(wct, mTaskInfo1, mTaskInfo2);
mSyncQueue.queue(wct);
mSyncQueue.runInSync(t ->
layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2,
false /* applyResizingOffset */));
}
@Override
public void setLayoutOffsetTarget(int offsetX, int offsetY, SplitLayout layout) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
layout.applyLayoutOffsetTarget(wct, offsetX, offsetY, mTaskInfo1, mTaskInfo2);
mController.getTaskOrganizer().applyTransaction(wct);
}
}

View File

@@ -1,38 +0,0 @@
/*
* 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.apppairs;
import android.app.ActivityManager;
import androidx.annotation.NonNull;
import com.android.wm.shell.common.annotations.ExternalThread;
import java.io.PrintWriter;
/**
* Interface to engage app pairs feature.
*/
@ExternalThread
public interface AppPairs {
/** Pairs indicated tasks. */
boolean pair(int task1, int task2);
/** Pairs indicated tasks. */
boolean pair(ActivityManager.RunningTaskInfo task1, ActivityManager.RunningTaskInfo task2);
/** Unpairs any app-pair containing this task id. */
void unpair(int taskId);
}

View File

@@ -1,213 +0,0 @@
/*
* 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.apppairs;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
import android.app.ActivityManager;
import android.util.Slog;
import android.util.SparseArray;
import androidx.annotation.NonNull;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import java.io.PrintWriter;
/**
* Class manages app-pairs multitasking mode and implements the main interface {@link AppPairs}.
*/
public class AppPairsController {
private static final String TAG = AppPairsController.class.getSimpleName();
private final ShellTaskOrganizer mTaskOrganizer;
private final SyncTransactionQueue mSyncQueue;
private final ShellExecutor mMainExecutor;
private final AppPairsImpl mImpl = new AppPairsImpl();
private AppPairsPool mPairsPool;
// Active app-pairs mapped by root task id key.
private final SparseArray<AppPair> mActiveAppPairs = new SparseArray<>();
private final DisplayController mDisplayController;
private final DisplayImeController mDisplayImeController;
private final DisplayInsetsController mDisplayInsetsController;
public AppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue,
DisplayController displayController, ShellExecutor mainExecutor,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController) {
mTaskOrganizer = organizer;
mSyncQueue = syncQueue;
mDisplayController = displayController;
mDisplayImeController = displayImeController;
mDisplayInsetsController = displayInsetsController;
mMainExecutor = mainExecutor;
}
public AppPairs asAppPairs() {
return mImpl;
}
public void onOrganizerRegistered() {
if (mPairsPool == null) {
setPairsPool(new AppPairsPool(this));
}
}
@VisibleForTesting
public void setPairsPool(AppPairsPool pool) {
mPairsPool = pool;
}
public boolean pair(int taskId1, int taskId2) {
final ActivityManager.RunningTaskInfo task1 = mTaskOrganizer.getRunningTaskInfo(taskId1);
final ActivityManager.RunningTaskInfo task2 = mTaskOrganizer.getRunningTaskInfo(taskId2);
if (task1 == null || task2 == null) {
return false;
}
return pair(task1, task2);
}
public boolean pair(ActivityManager.RunningTaskInfo task1,
ActivityManager.RunningTaskInfo task2) {
return pairInner(task1, task2) != null;
}
@VisibleForTesting
public AppPair pairInner(
@NonNull ActivityManager.RunningTaskInfo task1,
@NonNull ActivityManager.RunningTaskInfo task2) {
final AppPair pair = mPairsPool.acquire();
if (!pair.pair(task1, task2)) {
mPairsPool.release(pair);
return null;
}
mActiveAppPairs.put(pair.getRootTaskId(), pair);
return pair;
}
public void unpair(int taskId) {
unpair(taskId, true /* releaseToPool */);
}
public void unpair(int taskId, boolean releaseToPool) {
AppPair pair = mActiveAppPairs.get(taskId);
if (pair == null) {
for (int i = mActiveAppPairs.size() - 1; i >= 0; --i) {
final AppPair candidate = mActiveAppPairs.valueAt(i);
if (candidate.contains(taskId)) {
pair = candidate;
break;
}
}
}
if (pair == null) {
ProtoLog.v(WM_SHELL_TASK_ORG, "taskId %d isn't isn't in an app-pair.", taskId);
return;
}
ProtoLog.v(WM_SHELL_TASK_ORG, "unpair taskId=%d pair=%s", taskId, pair);
mActiveAppPairs.remove(pair.getRootTaskId());
pair.unpair();
if (releaseToPool) {
mPairsPool.release(pair);
}
}
ShellTaskOrganizer getTaskOrganizer() {
return mTaskOrganizer;
}
SyncTransactionQueue getSyncTransactionQueue() {
return mSyncQueue;
}
DisplayController getDisplayController() {
return mDisplayController;
}
DisplayImeController getDisplayImeController() {
return mDisplayImeController;
}
DisplayInsetsController getDisplayInsetsController() {
return mDisplayInsetsController;
}
public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
final String childPrefix = innerPrefix + " ";
pw.println(prefix + this);
for (int i = mActiveAppPairs.size() - 1; i >= 0; --i) {
mActiveAppPairs.valueAt(i).dump(pw, childPrefix);
}
if (mPairsPool != null) {
mPairsPool.dump(pw, prefix);
}
}
@Override
public String toString() {
return TAG + "#" + mActiveAppPairs.size();
}
private class AppPairsImpl implements AppPairs {
@Override
public boolean pair(int task1, int task2) {
boolean[] result = new boolean[1];
try {
mMainExecutor.executeBlocking(() -> {
result[0] = AppPairsController.this.pair(task1, task2);
});
} catch (InterruptedException e) {
Slog.e(TAG, "Failed to pair tasks: " + task1 + ", " + task2);
}
return result[0];
}
@Override
public boolean pair(ActivityManager.RunningTaskInfo task1,
ActivityManager.RunningTaskInfo task2) {
boolean[] result = new boolean[1];
try {
mMainExecutor.executeBlocking(() -> {
result[0] = AppPairsController.this.pair(task1, task2);
});
} catch (InterruptedException e) {
Slog.e(TAG, "Failed to pair tasks: " + task1 + ", " + task2);
}
return result[0];
}
@Override
public void unpair(int taskId) {
mMainExecutor.execute(() -> {
AppPairsController.this.unpair(taskId);
});
}
}
}

View File

@@ -1,93 +0,0 @@
/*
* 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.apppairs;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
import androidx.annotation.NonNull;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import java.io.PrintWriter;
import java.util.ArrayList;
/**
* Class that manager pool of {@link AppPair} objects. Helps reduce the need to call system_server
* to create a root task for the app-pair when needed since we always have one ready to go.
*/
class AppPairsPool {
private static final String TAG = AppPairsPool.class.getSimpleName();
@VisibleForTesting
final AppPairsController mController;
// The pool
private final ArrayList<AppPair> mPool = new ArrayList();
AppPairsPool(AppPairsController controller) {
mController = controller;
incrementPool();
}
AppPair acquire() {
final AppPair entry = mPool.remove(mPool.size() - 1);
ProtoLog.v(WM_SHELL_TASK_ORG, "acquire entry.taskId=%s listener=%s size=%d",
entry.getRootTaskId(), entry, mPool.size());
if (mPool.size() == 0) {
incrementPool();
}
return entry;
}
void release(AppPair entry) {
mPool.add(entry);
ProtoLog.v(WM_SHELL_TASK_ORG, "release entry.taskId=%s listener=%s size=%d",
entry.getRootTaskId(), entry, mPool.size());
}
@VisibleForTesting
void incrementPool() {
ProtoLog.v(WM_SHELL_TASK_ORG, "incrementPool size=%d", mPool.size());
final AppPair entry = new AppPair(mController);
// TODO: multi-display...
mController.getTaskOrganizer().createRootTask(
DEFAULT_DISPLAY, WINDOWING_MODE_FULLSCREEN, entry);
mPool.add(entry);
}
@VisibleForTesting
int poolSize() {
return mPool.size();
}
public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
final String childPrefix = innerPrefix + " ";
pw.println(prefix + this);
for (int i = mPool.size() - 1; i >= 0; --i) {
mPool.get(i).dump(pw, childPrefix);
}
}
@Override
public String toString() {
return TAG + "#" + mPool.size();
}
}

View File

@@ -1,2 +0,0 @@
# WM shell sub-modules apppair owner
chenghsiuchang@google.com

View File

@@ -38,8 +38,6 @@ import com.android.wm.shell.TaskViewFactory;
import com.android.wm.shell.TaskViewFactoryController;
import com.android.wm.shell.TaskViewTransitions;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.apppairs.AppPairsController;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.back.BackAnimationController;
import com.android.wm.shell.bubbles.BubbleController;
@@ -560,17 +558,6 @@ public abstract class WMShellBaseModule {
return Optional.empty();
}
// App Pairs (optional feature)
@WMSingleton
@Provides
static Optional<AppPairs> provideAppPairs(Optional<AppPairsController> appPairsController) {
return appPairsController.map((controller) -> controller.asAppPairs());
}
@BindsOptionalOf
abstract AppPairsController optionalAppPairs();
//
// Starting window
//
@@ -651,7 +638,6 @@ public abstract class WMShellBaseModule {
KidsModeTaskOrganizer kidsModeTaskOrganizer,
Optional<BubbleController> bubblesOptional,
Optional<SplitScreenController> splitScreenOptional,
Optional<AppPairsController> appPairsOptional,
Optional<PipTouchHandler> pipTouchHandlerOptional,
FullscreenTaskListener fullscreenTaskListener,
Optional<FullscreenUnfoldController> appUnfoldTransitionController,
@@ -669,7 +655,6 @@ public abstract class WMShellBaseModule {
kidsModeTaskOrganizer,
bubblesOptional,
splitScreenOptional,
appPairsOptional,
pipTouchHandlerOptional,
fullscreenTaskListener,
appUnfoldTransitionController,
@@ -700,12 +685,11 @@ public abstract class WMShellBaseModule {
Optional<Pip> pipOptional,
Optional<OneHandedController> oneHandedOptional,
Optional<HideDisplayCutoutController> hideDisplayCutout,
Optional<AppPairsController> appPairsOptional,
Optional<RecentTasksController> recentTasksOptional,
@ShellMainThread ShellExecutor mainExecutor) {
return new ShellCommandHandlerImpl(shellTaskOrganizer, kidsModeTaskOrganizer,
splitScreenOptional, pipOptional, oneHandedOptional, hideDisplayCutout,
appPairsOptional, recentTasksOptional, mainExecutor);
recentTasksOptional, mainExecutor);
}
@WMSingleton

View File

@@ -29,7 +29,6 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.TaskViewTransitions;
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.apppairs.AppPairsController;
import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
@@ -176,17 +175,6 @@ public class WMShellModule {
recentTasks, stageTaskUnfoldControllerProvider);
}
@WMSingleton
@Provides
static AppPairsController provideAppPairs(ShellTaskOrganizer shellTaskOrganizer,
SyncTransactionQueue syncQueue, DisplayController displayController,
@ShellMainThread ShellExecutor mainExecutor,
DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController) {
return new AppPairsController(shellTaskOrganizer, syncQueue, displayController,
mainExecutor, displayImeController, displayInsetsController);
}
//
// Pip
//

View File

@@ -1,115 +0,0 @@
/*
* 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.flicker.apppairs
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.wm.shell.flicker.appPairsDividerIsInvisibleAtEnd
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import org.junit.After
import org.junit.Before
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
* Test cold launch app from launcher. When the device doesn't support non-resizable in multi window
* {@link Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW}, app pairs should not pair
* non-resizable apps.
*
* To run this test: `atest WMShellFlickerTests:AppPairsTestCannotPairNonResizeableApps`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group1
class AppPairsTestCannotPairNonResizeableApps(
testSpec: FlickerTestParameter
) : AppPairsTransition(testSpec) {
override val transition: FlickerBuilder.() -> Unit
get() = {
super.transition(this)
transitions {
nonResizeableApp?.launchViaIntent(wmHelper)
// TODO pair apps through normal UX flow
executeShellCommand(
composePairsCommand(primaryTaskId, nonResizeableTaskId, pair = true))
nonResizeableApp?.run { wmHelper.waitForFullScreenApp(nonResizeableApp.component) }
}
}
@Before
override fun setup() {
super.setup()
setSupportsNonResizableMultiWindow(instrumentation, -1)
}
@After
override fun teardown() {
super.teardown()
resetMultiWindowConfig(instrumentation)
}
@Ignore
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
@Ignore
@Test
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
@Ignore
@Test
override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
@Ignore
@Test
fun appPairsDividerIsInvisibleAtEnd() = testSpec.appPairsDividerIsInvisibleAtEnd()
@Ignore
@Test
fun onlyResizeableAppWindowVisible() {
val nonResizeableApp = nonResizeableApp
require(nonResizeableApp != null) {
"Non resizeable app not initialized"
}
testSpec.assertWmEnd {
isAppWindowVisible(nonResizeableApp.component)
isAppWindowInvisible(primaryApp.component)
}
}
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
repetitions = AppPairsHelper.TEST_REPETITIONS)
}
}
}

View File

@@ -1,104 +0,0 @@
/*
* 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.flicker.apppairs
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
* Test cold launch app from launcher.
* To run this test: `atest WMShellFlickerTests:AppPairsTestPairPrimaryAndSecondaryApps`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group1
class AppPairsTestPairPrimaryAndSecondaryApps(
testSpec: FlickerTestParameter
) : AppPairsTransition(testSpec) {
override val transition: FlickerBuilder.() -> Unit
get() = {
super.transition(this)
transitions {
// TODO pair apps through normal UX flow
executeShellCommand(
composePairsCommand(primaryTaskId, secondaryTaskId, pair = true))
waitAppsShown(primaryApp, secondaryApp)
}
}
@Ignore
@Test
override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
@Ignore
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
@Ignore
@Test
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
@Ignore
@Test
fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd()
@Ignore
@Test
fun bothAppWindowsVisible() {
testSpec.assertWmEnd {
isAppWindowVisible(primaryApp.component)
isAppWindowVisible(secondaryApp.component)
}
}
@Ignore
@Test
fun appsEndingBounds() {
testSpec.assertLayersEnd {
val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region
visibleRegion(primaryApp.component)
.coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion))
visibleRegion(secondaryApp.component)
.coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion))
}
}
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
repetitions = AppPairsHelper.TEST_REPETITIONS)
}
}
}

View File

@@ -1,128 +0,0 @@
/*
* 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.flicker.apppairs
import android.view.Display
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.traces.common.WindowManagerConditionsFactory
import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow
import org.junit.After
import org.junit.Before
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
* Test cold launch app from launcher. When the device supports non-resizable in multi window
* {@link Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW}, app pairs can pair
* non-resizable apps.
*
* To run this test: `atest WMShellFlickerTests:AppPairsTestSupportPairNonResizeableApps`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group1
class AppPairsTestSupportPairNonResizeableApps(
testSpec: FlickerTestParameter
) : AppPairsTransition(testSpec) {
override val transition: FlickerBuilder.() -> Unit
get() = {
super.transition(this)
transitions {
nonResizeableApp?.launchViaIntent(wmHelper)
// TODO pair apps through normal UX flow
executeShellCommand(
composePairsCommand(primaryTaskId, nonResizeableTaskId, pair = true))
val waitConditions = mutableListOf(
WindowManagerConditionsFactory.isWindowVisible(primaryApp.component),
WindowManagerConditionsFactory.isLayerVisible(primaryApp.component),
WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY))
nonResizeableApp?.let {
waitConditions.add(
WindowManagerConditionsFactory.isWindowVisible(nonResizeableApp.component))
waitConditions.add(
WindowManagerConditionsFactory.isLayerVisible(nonResizeableApp.component))
}
wmHelper.waitFor(*waitConditions.toTypedArray())
}
}
@Before
override fun setup() {
super.setup()
setSupportsNonResizableMultiWindow(instrumentation, 1)
}
@After
override fun teardown() {
super.teardown()
resetMultiWindowConfig(instrumentation)
}
@Ignore
@Test
override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
@Ignore
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
@Ignore
@Test
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
@Ignore
@Test
fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd()
@Ignore
@Test
fun bothAppWindowVisible() {
val nonResizeableApp = nonResizeableApp
require(nonResizeableApp != null) {
"Non resizeable app not initialized"
}
testSpec.assertWmEnd {
isAppWindowVisible(nonResizeableApp.component)
isAppWindowVisible(primaryApp.component)
}
}
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
repetitions = AppPairsHelper.TEST_REPETITIONS)
}
}
}

View File

@@ -1,121 +0,0 @@
/*
* 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.flicker.apppairs
import android.os.SystemClock
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.appPairsDividerIsInvisibleAtEnd
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
* Test cold launch app from launcher.
* To run this test: `atest WMShellFlickerTests:AppPairsTestUnpairPrimaryAndSecondaryApps`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group1
class AppPairsTestUnpairPrimaryAndSecondaryApps(
testSpec: FlickerTestParameter
) : AppPairsTransition(testSpec) {
override val transition: FlickerBuilder.() -> Unit
get() = {
super.transition(this)
setup {
eachRun {
executeShellCommand(
composePairsCommand(primaryTaskId, secondaryTaskId, pair = true))
waitAppsShown(primaryApp, secondaryApp)
}
}
transitions {
// TODO pair apps through normal UX flow
executeShellCommand(
composePairsCommand(primaryTaskId, secondaryTaskId, pair = false))
SystemClock.sleep(AppPairsHelper.TIMEOUT_MS)
}
}
@Ignore
@Test
override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales()
@Ignore
@Test
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
@Ignore
@Test
fun appPairsDividerIsInvisibleAtEnd() = testSpec.appPairsDividerIsInvisibleAtEnd()
@Ignore
@Test
fun bothAppWindowsInvisible() {
testSpec.assertWmEnd {
isAppWindowInvisible(primaryApp.component)
isAppWindowInvisible(secondaryApp.component)
}
}
@Ignore
@Test
fun appsStartingBounds() {
testSpec.assertLayersStart {
val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region
visibleRegion(primaryApp.component)
.coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion))
visibleRegion(secondaryApp.component)
.coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion))
}
}
@Ignore
@Test
fun appsEndingBounds() {
testSpec.assertLayersEnd {
notContains(primaryApp.component)
notContains(secondaryApp.component)
}
}
@Ignore
@Test
override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
repetitions = AppPairsHelper.TEST_REPETITIONS)
}
}
}

View File

@@ -1,178 +0,0 @@
/*
* 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.flicker.apppairs
import android.app.Instrumentation
import android.content.Context
import android.system.helpers.ActivityHelper
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.wm.flicker.FlickerBuilderProvider
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsVisible
import com.android.server.wm.flicker.statusBarLayerIsVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsVisible
import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.wm.shell.flicker.helpers.AppPairsHelper
import com.android.wm.shell.flicker.helpers.BaseAppHelper
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.getDevEnableNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setDevEnableNonResizableMultiWindow
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import com.android.wm.shell.flicker.testapp.Components
import org.junit.After
import org.junit.Before
import org.junit.Ignore
import org.junit.Test
abstract class AppPairsTransition(protected val testSpec: FlickerTestParameter) {
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
protected val context: Context = instrumentation.context
protected val activityHelper = ActivityHelper.getInstance()
protected val appPairsHelper = AppPairsHelper(instrumentation,
Components.SplitScreenActivity.LABEL,
Components.SplitScreenActivity.COMPONENT.toFlickerComponent())
protected val primaryApp = SplitScreenHelper.getPrimary(instrumentation)
protected val secondaryApp = SplitScreenHelper.getSecondary(instrumentation)
protected open val nonResizeableApp: SplitScreenHelper? =
SplitScreenHelper.getNonResizeable(instrumentation)
protected var primaryTaskId = ""
protected var secondaryTaskId = ""
protected var nonResizeableTaskId = ""
private var prevDevEnableNonResizableMultiWindow = 0
@Before
open fun setup() {
prevDevEnableNonResizableMultiWindow = getDevEnableNonResizableMultiWindow(context)
if (prevDevEnableNonResizableMultiWindow != 0) {
// Turn off the development option
setDevEnableNonResizableMultiWindow(context, 0)
}
}
@After
open fun teardown() {
setDevEnableNonResizableMultiWindow(context, prevDevEnableNonResizableMultiWindow)
}
@FlickerBuilderProvider
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
transition(this)
}
}
internal open val transition: FlickerBuilder.() -> Unit
get() = {
setup {
test {
device.wakeUpAndGoToHomeScreen()
}
eachRun {
this.setRotation(testSpec.startRotation)
primaryApp.launchViaIntent(wmHelper)
secondaryApp.launchViaIntent(wmHelper)
nonResizeableApp?.launchViaIntent(wmHelper)
updateTasksId()
}
}
teardown {
eachRun {
executeShellCommand(composePairsCommand(
primaryTaskId, secondaryTaskId, pair = false))
executeShellCommand(composePairsCommand(
primaryTaskId, nonResizeableTaskId, pair = false))
primaryApp.exit(wmHelper)
secondaryApp.exit(wmHelper)
nonResizeableApp?.exit(wmHelper)
}
}
}
protected fun updateTasksId() {
primaryTaskId = getTaskIdForActivity(
primaryApp.component.packageName, primaryApp.component.className).toString()
secondaryTaskId = getTaskIdForActivity(
secondaryApp.component.packageName, secondaryApp.component.className).toString()
val nonResizeableApp = nonResizeableApp
if (nonResizeableApp != null) {
nonResizeableTaskId = getTaskIdForActivity(
nonResizeableApp.component.packageName,
nonResizeableApp.component.className).toString()
}
}
private fun getTaskIdForActivity(pkgName: String, activityName: String): Int {
return activityHelper.getTaskIdForActivity(pkgName, activityName)
}
internal fun executeShellCommand(cmd: String) {
BaseAppHelper.executeShellCommand(instrumentation, cmd)
}
internal fun composePairsCommand(
primaryApp: String,
secondaryApp: String,
pair: Boolean
): String = buildString {
// dumpsys activity service SystemUIService WMShell {pair|unpair} ${TASK_ID_1} ${TASK_ID_2}
append("dumpsys activity service SystemUIService WMShell ")
if (pair) {
append("pair ")
} else {
append("unpair ")
}
append("$primaryApp $secondaryApp")
}
@Ignore
@Test
open fun navBarLayerIsVisible() {
testSpec.navBarLayerIsVisible()
}
@Ignore
@Test
open fun statusBarLayerIsVisible() {
testSpec.statusBarLayerIsVisible()
}
@Ignore
@Test
open fun navBarWindowIsVisible() {
testSpec.navBarWindowIsVisible()
}
@Ignore
@Test
open fun statusBarWindowIsVisible() {
testSpec.statusBarWindowIsVisible()
}
@Ignore
@Test
open fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
@Ignore
@Test
open fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
}

View File

@@ -1,2 +0,0 @@
# window manager > wm shell > Split Screen
# Bug component: 928697

View File

@@ -1,109 +0,0 @@
/*
* 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.flicker.apppairs
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.setRotation
import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
* Test open apps to app pairs and rotate.
* To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppsInAppPairsMode`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group1
class RotateTwoLaunchedAppsInAppPairsMode(
testSpec: FlickerTestParameter
) : RotateTwoLaunchedAppsTransition(testSpec) {
override val transition: FlickerBuilder.() -> Unit
get() = {
super.transition(this)
transitions {
executeShellCommand(composePairsCommand(
primaryTaskId, secondaryTaskId, true /* pair */))
waitAppsShown(primaryApp, secondaryApp)
setRotation(testSpec.endRotation)
}
}
@Ignore
@Test
override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
@Ignore
@Test
override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
@Ignore
@Test
fun bothAppWindowsVisible() {
testSpec.assertWmEnd {
isAppWindowVisible(primaryApp.component)
isAppWindowVisible(secondaryApp.component)
}
}
@Ignore
@Test
fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd()
@Ignore
@Test
fun appPairsPrimaryBoundsIsVisibleAtEnd() =
testSpec.appPairsPrimaryBoundsIsVisibleAtEnd(testSpec.endRotation,
primaryApp.component)
@Ignore
@Test
fun appPairsSecondaryBoundsIsVisibleAtEnd() =
testSpec.appPairsSecondaryBoundsIsVisibleAtEnd(testSpec.endRotation,
secondaryApp.component)
@Ignore
@Test
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
repetitions = SplitScreenHelper.TEST_REPETITIONS,
supportedRotations = listOf(Surface.ROTATION_90, Surface.ROTATION_270)
)
}
}
}

View File

@@ -1,117 +0,0 @@
/*
* 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.flicker.apppairs
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group1
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.setRotation
import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd
import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
/**
* Test open apps to app pairs and rotate.
* To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppsRotateAndEnterAppPairsMode`
*/
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group1
class RotateTwoLaunchedAppsRotateAndEnterAppPairsMode(
testSpec: FlickerTestParameter
) : RotateTwoLaunchedAppsTransition(testSpec) {
override val transition: FlickerBuilder.() -> Unit
get() = {
super.transition(this)
transitions {
this.setRotation(testSpec.endRotation)
executeShellCommand(
composePairsCommand(primaryTaskId, secondaryTaskId, pair = true))
waitAppsShown(primaryApp, secondaryApp)
}
}
@Ignore
@Test
fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd()
@Ignore
@Test
override fun navBarWindowIsVisible() = super.navBarWindowIsVisible()
@Ignore
@Test
override fun navBarLayerIsVisible() = super.navBarLayerIsVisible()
@Ignore
@Test
override fun statusBarWindowIsVisible() = super.statusBarWindowIsVisible()
@Ignore
@Test
override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible()
@Ignore
@Test
override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales()
@Ignore
@Test
fun bothAppWindowsVisible() {
testSpec.assertWmEnd {
isAppWindowVisible(primaryApp.component)
isAppWindowVisible(secondaryApp.component)
}
}
@Ignore
@Test
fun appPairsPrimaryBoundsIsVisibleAtEnd() =
testSpec.appPairsPrimaryBoundsIsVisibleAtEnd(testSpec.endRotation,
primaryApp.component)
@Ignore
@Test
fun appPairsSecondaryBoundsIsVisibleAtEnd() =
testSpec.appPairsSecondaryBoundsIsVisibleAtEnd(testSpec.endRotation,
secondaryApp.component)
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): Collection<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
repetitions = SplitScreenHelper.TEST_REPETITIONS,
supportedRotations = listOf(Surface.ROTATION_90, Surface.ROTATION_270)
)
}
}
}

View File

@@ -1,76 +0,0 @@
/*
* 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.flicker.apppairs
import android.view.Surface
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.wm.shell.flicker.helpers.BaseAppHelper.Companion.isShellTransitionsEnabled
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.Assume.assumeFalse
import org.junit.Before
import org.junit.Ignore
import org.junit.Test
abstract class RotateTwoLaunchedAppsTransition(
testSpec: FlickerTestParameter
) : AppPairsTransition(testSpec) {
override val nonResizeableApp: SplitScreenHelper?
get() = null
override val transition: FlickerBuilder.() -> Unit
get() = {
setup {
test {
device.wakeUpAndGoToHomeScreen()
this.setRotation(Surface.ROTATION_0)
primaryApp.launchViaIntent(wmHelper)
secondaryApp.launchViaIntent(wmHelper)
updateTasksId()
}
}
teardown {
eachRun {
executeShellCommand(composePairsCommand(
primaryTaskId, secondaryTaskId, pair = false))
primaryApp.exit(wmHelper)
secondaryApp.exit(wmHelper)
}
}
}
@Before
override fun setup() {
// AppPairs hasn't been updated to Shell Transition. There will be conflict on rotation.
assumeFalse(isShellTransitionsEnabled())
super.setup()
}
@Ignore
@Test
override fun navBarLayerIsVisible() {
super.navBarLayerIsVisible()
}
@Ignore
@Test
override fun navBarLayerRotatesAndScales() {
super.navBarLayerRotatesAndScales()
}
}

View File

@@ -1,123 +0,0 @@
/*
* 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.apppairs;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.hardware.display.DisplayManager;
import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.SyncTransactionQueue;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/**
* Tests for {@link AppPair}
* Build/Install/Run:
* atest WMShellUnitTests:AppPairTests
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
public class AppPairTests extends ShellTestCase {
private AppPairsController mController;
@Mock private SyncTransactionQueue mSyncQueue;
@Mock private ShellTaskOrganizer mTaskOrganizer;
@Mock private DisplayController mDisplayController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext);
when(mDisplayController.getDisplay(anyInt())).thenReturn(
mContext.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY));
mController = new TestAppPairsController(
mTaskOrganizer,
mSyncQueue,
mDisplayController);
spyOn(mController);
}
@After
public void tearDown() {}
@Test
@UiThreadTest
public void testContains() {
final ActivityManager.RunningTaskInfo task1 = new TestRunningTaskInfoBuilder().build();
final ActivityManager.RunningTaskInfo task2 = new TestRunningTaskInfoBuilder().build();
final AppPair pair = mController.pairInner(task1, task2);
assertThat(pair.contains(task1.taskId)).isTrue();
assertThat(pair.contains(task2.taskId)).isTrue();
pair.unpair();
assertThat(pair.contains(task1.taskId)).isFalse();
assertThat(pair.contains(task2.taskId)).isFalse();
}
@Test
@UiThreadTest
public void testVanishUnpairs() {
final ActivityManager.RunningTaskInfo task1 = new TestRunningTaskInfoBuilder().build();
final ActivityManager.RunningTaskInfo task2 = new TestRunningTaskInfoBuilder().build();
final AppPair pair = mController.pairInner(task1, task2);
assertThat(pair.contains(task1.taskId)).isTrue();
assertThat(pair.contains(task2.taskId)).isTrue();
pair.onTaskVanished(task1);
assertThat(pair.contains(task1.taskId)).isFalse();
assertThat(pair.contains(task2.taskId)).isFalse();
}
@Test
@UiThreadTest
public void testOnTaskInfoChanged_notSupportsMultiWindow() {
final ActivityManager.RunningTaskInfo task1 = new TestRunningTaskInfoBuilder().build();
final ActivityManager.RunningTaskInfo task2 = new TestRunningTaskInfoBuilder().build();
final AppPair pair = mController.pairInner(task1, task2);
assertThat(pair.contains(task1.taskId)).isTrue();
assertThat(pair.contains(task2.taskId)).isTrue();
task1.supportsMultiWindow = false;
pair.onTaskInfoChanged(task1);
verify(mController).unpair(pair.getRootTaskId());
}
}

View File

@@ -1,104 +0,0 @@
/*
* 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.apppairs;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.hardware.display.DisplayManager;
import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.SyncTransactionQueue;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/** Tests for {@link AppPairsController} */
@SmallTest
@RunWith(AndroidJUnit4.class)
public class AppPairsControllerTests extends ShellTestCase {
private TestAppPairsController mController;
private TestAppPairsPool mPool;
@Mock private SyncTransactionQueue mSyncQueue;
@Mock private ShellTaskOrganizer mTaskOrganizer;
@Mock private DisplayController mDisplayController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext);
when(mDisplayController.getDisplay(anyInt())).thenReturn(
mContext.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY));
mController = new TestAppPairsController(
mTaskOrganizer,
mSyncQueue,
mDisplayController);
mPool = mController.getPool();
}
@After
public void tearDown() {}
@Test
@UiThreadTest
public void testPairUnpair() {
final ActivityManager.RunningTaskInfo task1 = new TestRunningTaskInfoBuilder().build();
final ActivityManager.RunningTaskInfo task2 = new TestRunningTaskInfoBuilder().build();
final AppPair pair = mController.pairInner(task1, task2);
assertThat(pair.contains(task1.taskId)).isTrue();
assertThat(pair.contains(task2.taskId)).isTrue();
assertThat(mPool.poolSize()).isGreaterThan(0);
mController.unpair(task2.taskId);
assertThat(pair.contains(task1.taskId)).isFalse();
assertThat(pair.contains(task2.taskId)).isFalse();
assertThat(mPool.poolSize()).isGreaterThan(1);
}
@Test
@UiThreadTest
public void testUnpair_DontReleaseToPool() {
final ActivityManager.RunningTaskInfo task1 = new TestRunningTaskInfoBuilder().build();
final ActivityManager.RunningTaskInfo task2 = new TestRunningTaskInfoBuilder().build();
final AppPair pair = mController.pairInner(task1, task2);
assertThat(pair.contains(task1.taskId)).isTrue();
assertThat(pair.contains(task2.taskId)).isTrue();
mController.unpair(task2.taskId, false /* releaseToPool */);
assertThat(pair.contains(task1.taskId)).isFalse();
assertThat(pair.contains(task2.taskId)).isFalse();
assertThat(mPool.poolSize()).isEqualTo(1);
}
}

View File

@@ -1,77 +0,0 @@
/*
* 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.apppairs;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.when;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.SyncTransactionQueue;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/** Tests for {@link AppPairsPool} */
@SmallTest
@RunWith(AndroidJUnit4.class)
public class AppPairsPoolTests extends ShellTestCase {
private TestAppPairsController mController;
private TestAppPairsPool mPool;
@Mock private SyncTransactionQueue mSyncQueue;
@Mock private ShellTaskOrganizer mTaskOrganizer;
@Mock private DisplayController mDisplayController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext);
mController = new TestAppPairsController(
mTaskOrganizer,
mSyncQueue,
mDisplayController);
mPool = mController.getPool();
}
@After
public void tearDown() {}
@Test
public void testInitialState() {
// Pool should always start off with at least 1 entry.
assertThat(mPool.poolSize()).isGreaterThan(0);
}
@Test
public void testAcquireRelease() {
assertThat(mPool.poolSize()).isGreaterThan(0);
final AppPair appPair = mPool.acquire();
assertThat(mPool.poolSize()).isGreaterThan(0);
mPool.release(appPair);
assertThat(mPool.poolSize()).isGreaterThan(1);
}
}

View File

@@ -1,42 +0,0 @@
/*
* 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.apppairs;
import static org.mockito.Mockito.mock;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
public class TestAppPairsController extends AppPairsController {
private TestAppPairsPool mPool;
public TestAppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue,
DisplayController displayController) {
super(organizer, syncQueue, displayController, mock(ShellExecutor.class),
mock(DisplayImeController.class), mock(DisplayInsetsController.class));
mPool = new TestAppPairsPool(this);
setPairsPool(mPool);
}
TestAppPairsPool getPool() {
return mPool;
}
}

View File

@@ -1,36 +0,0 @@
/*
* 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.apppairs;
import android.app.ActivityManager;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
public class TestAppPairsPool extends AppPairsPool{
TestAppPairsPool(AppPairsController controller) {
super(controller);
}
@Override
void incrementPool() {
final AppPair entry = new AppPair(mController);
final ActivityManager.RunningTaskInfo info =
new TestRunningTaskInfoBuilder().build();
entry.onTaskAppeared(info, null /* leash */);
release(entry);
}
}

View File

@@ -119,7 +119,6 @@ public class SystemUIFactory {
.setBubbles(mWMComponent.getBubbles())
.setHideDisplayCutout(mWMComponent.getHideDisplayCutout())
.setShellCommandHandler(mWMComponent.getShellCommandHandler())
.setAppPairs(mWMComponent.getAppPairs())
.setTaskViewFactory(mWMComponent.getTaskViewFactory())
.setTransitions(mWMComponent.getTransitions())
.setStartingSurface(mWMComponent.getStartingSurface())
@@ -139,7 +138,6 @@ public class SystemUIFactory {
.setBubbles(Optional.ofNullable(null))
.setHideDisplayCutout(Optional.ofNullable(null))
.setShellCommandHandler(Optional.ofNullable(null))
.setAppPairs(Optional.ofNullable(null))
.setTaskViewFactory(Optional.ofNullable(null))
.setTransitions(new ShellTransitions() {})
.setDisplayAreaHelper(Optional.ofNullable(null))

View File

@@ -39,7 +39,6 @@ import com.android.systemui.unfold.UnfoldLatencyTracker;
import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider;
import com.android.wm.shell.ShellCommandHandler;
import com.android.wm.shell.TaskViewFactory;
import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.compatui.CompatUI;
@@ -87,9 +86,6 @@ public interface SysUIComponent {
@BindsInstance
Builder setSplitScreen(Optional<SplitScreen> s);
@BindsInstance
Builder setAppPairs(Optional<AppPairs> s);
@BindsInstance
Builder setOneHanded(Optional<OneHanded> o);

View File

@@ -26,7 +26,6 @@ import com.android.systemui.tv.TvWMComponent;
import com.android.wm.shell.ShellCommandHandler;
import com.android.wm.shell.ShellInit;
import com.android.wm.shell.TaskViewFactory;
import com.android.wm.shell.apppairs.AppPairs;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.bubbles.Bubbles;
import com.android.wm.shell.common.annotations.ShellMainThread;
@@ -97,9 +96,6 @@ public interface WMComponent {
@WMSingleton
Optional<SplitScreen> getSplitScreen();
@WMSingleton
Optional<AppPairs> getAppPairs();
@WMSingleton
Optional<Bubbles> getBubbles();