diff options
Diffstat (limited to 'libs')
27 files changed, 645 insertions, 1323 deletions
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml index f0eae97b107e..807e5afae890 100644 --- a/libs/WindowManager/Shell/res/values/config.xml +++ b/libs/WindowManager/Shell/res/values/config.xml @@ -42,16 +42,4 @@ <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows, when the PIP menu is shown in center. --> <string translatable="false" name="pip_menu_bounds">"596 280 1324 690"</string> - - <!-- Gravity of letterboxed apps in portrait screen orientation. - Can be Gravity.TOP, Gravity.CENTER or Gravity.BOTTOM. - Any other value will result in runtime exception for a letterboxed activity. - Default is Gravity.TOP. --> - <integer name="config_letterboxPortraitGravity">0x00000030</integer> - - <!-- Gravity of letterboxed apps in landscape screen orientation. - Can be Gravity.LEFT, Gravity.CENTER or Gravity.RIGHT. - Any other value will result in runtime exception for a letterboxed activity. - Default is Gravity.CENTER. --> - <integer name="config_letterboxLandscapeGravity">0x00000011</integer> </resources> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java index 4f13b83bc29d..63d31182a748 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java @@ -20,8 +20,9 @@ import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCR import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString; import android.app.ActivityManager; -import android.util.ArraySet; +import android.graphics.Point; import android.util.Slog; +import android.util.SparseArray; import android.view.SurfaceControl; import androidx.annotation.NonNull; @@ -40,7 +41,7 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener { private final SyncTransactionQueue mSyncQueue; - private final ArraySet<Integer> mTasks = new ArraySet<>(); + private final SparseArray<SurfaceControl> mLeashByTaskId = new SparseArray<>(); public FullscreenTaskListener(SyncTransactionQueue syncQueue) { mSyncQueue = syncQueue; @@ -48,39 +49,44 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener { @Override public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { - synchronized (mTasks) { - if (mTasks.contains(taskInfo.taskId)) { - throw new RuntimeException("Task appeared more than once: #" + taskInfo.taskId); - } - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Appeared: #%d", - taskInfo.taskId); - mTasks.add(taskInfo.taskId); - mSyncQueue.runInSync(t -> { - // Reset several properties back to fullscreen (PiP, for example, leaves all these - // properties in a bad state). - t.setWindowCrop(leash, null); - t.setPosition(leash, 0, 0); - // TODO(shell-transitions): Eventually set everything in transition so there's no - // SF Transaction here. - if (!Transitions.ENABLE_SHELL_TRANSITIONS) { - t.setAlpha(leash, 1f); - t.setMatrix(leash, 1, 0, 0, 1); - t.show(leash); - } - }); + if (mLeashByTaskId.get(taskInfo.taskId) != null) { + throw new IllegalStateException("Task appeared more than once: #" + taskInfo.taskId); } + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Appeared: #%d", + taskInfo.taskId); + mLeashByTaskId.put(taskInfo.taskId, leash); + final Point positionInParent = taskInfo.positionInParent; + mSyncQueue.runInSync(t -> { + // Reset several properties back to fullscreen (PiP, for example, leaves all these + // properties in a bad state). + t.setWindowCrop(leash, null); + t.setPosition(leash, positionInParent.x, positionInParent.y); + // TODO(shell-transitions): Eventually set everything in transition so there's no + // SF Transaction here. + if (!Transitions.ENABLE_SHELL_TRANSITIONS) { + t.setAlpha(leash, 1f); + t.setMatrix(leash, 1, 0, 0, 1); + t.show(leash); + } + }); + } + + @Override + public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { + final SurfaceControl leash = mLeashByTaskId.get(taskInfo.taskId); + final Point positionInParent = taskInfo.positionInParent; + mSyncQueue.runInSync(t -> t.setPosition(leash, positionInParent.x, positionInParent.y)); } @Override public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { - synchronized (mTasks) { - if (!mTasks.remove(taskInfo.taskId)) { - Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId); - return; - } - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Vanished: #%d", - taskInfo.taskId); + if (mLeashByTaskId.get(taskInfo.taskId) == null) { + Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId); + return; } + mLeashByTaskId.remove(taskInfo.taskId); + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Vanished: #%d", + taskInfo.taskId); } @Override @@ -88,7 +94,7 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener { final String innerPrefix = prefix + " "; final String childPrefix = innerPrefix + " "; pw.println(prefix + this); - pw.println(innerPrefix + mTasks.size() + " Tasks"); + pw.println(innerPrefix + mLeashByTaskId.size() + " Tasks"); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java index 45948dd9e800..20850428d3bd 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandler.java @@ -16,12 +16,9 @@ package com.android.wm.shell; -import android.view.Gravity; - import com.android.wm.shell.apppairs.AppPairs; import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; -import com.android.wm.shell.letterbox.LetterboxConfigController; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.splitscreen.SplitScreen; @@ -42,7 +39,6 @@ public final class ShellCommandHandler { private final Optional<HideDisplayCutout> mHideDisplayCutout; private final ShellTaskOrganizer mShellTaskOrganizer; private final Optional<AppPairs> mAppPairsOptional; - private final LetterboxConfigController mLetterboxConfigController; public ShellCommandHandler( ShellTaskOrganizer shellTaskOrganizer, @@ -50,15 +46,13 @@ public final class ShellCommandHandler { Optional<Pip> pipOptional, Optional<OneHanded> oneHandedOptional, Optional<HideDisplayCutout> hideDisplayCutout, - Optional<AppPairs> appPairsOptional, - LetterboxConfigController letterboxConfigController) { + Optional<AppPairs> appPairsOptional) { mShellTaskOrganizer = shellTaskOrganizer; mSplitScreenOptional = splitScreenOptional; mPipOptional = pipOptional; mOneHandedOptional = oneHandedOptional; mHideDisplayCutout = hideDisplayCutout; mAppPairsOptional = appPairsOptional; - mLetterboxConfigController = letterboxConfigController; } /** Dumps WM Shell internal state. */ @@ -85,14 +79,6 @@ public final class ShellCommandHandler { return false; } switch (args[1]) { - case "set-letterbox-portrait-gravity": - return runSetLetterboxPortraitGravity(args, pw); - case "get-letterbox-portrait-gravity": - return runGetLetterboxPortraitGravity(pw); - case "set-letterbox-landscape-gravity": - return runSetLetterboxLandscapeGravity(args, pw); - case "get-letterbox-landscape-gravity": - return runGetLetterboxLandscapeGravity(pw); case "pair": return runPair(args, pw); case "unpair": @@ -104,92 +90,6 @@ public final class ShellCommandHandler { } } - private boolean runSetLetterboxPortraitGravity(String[] args, PrintWriter pw) { - if (args.length < 3) { - // First two arguments are "WMShell" and command name. - pw.println("Error: reset, TOP, CENTER or BOTTOM should be provided as an argument"); - return true; - } - switch (args[2]) { - case "reset": - mLetterboxConfigController.resetPortraitGravity(); - break; - case "TOP": - mLetterboxConfigController.setPortraitGravity(Gravity.TOP); - break; - case "CENTER": - mLetterboxConfigController.setPortraitGravity(Gravity.CENTER); - break; - case "BOTTOM": - mLetterboxConfigController.setPortraitGravity(Gravity.BOTTOM); - break; - default: - pw.println("Error: expected reset, TOP, CENTER or BOTTOM but got " + args[2]); - } - return true; - } - - private boolean runGetLetterboxPortraitGravity(PrintWriter pw) { - final int gravity = mLetterboxConfigController.getPortraitGravity(); - switch (gravity) { - case Gravity.TOP: - pw.println("TOP"); - break; - case Gravity.CENTER: - pw.println("CENTER"); - break; - case Gravity.BOTTOM: - pw.println("BOTTOM"); - break; - default: - throw new AssertionError("Unexpected gravity: " + gravity); - } - return true; - } - - private boolean runSetLetterboxLandscapeGravity(String[] args, PrintWriter pw) { - if (args.length < 3) { - // First two arguments are "WMShell" and command name. - pw.println("Error: reset, LEFT, CENTER or RIGHT should be provided as an argument"); - return false; - } - switch (args[2]) { - case "reset": - mLetterboxConfigController.resetLandscapeGravity(); - break; - case "LEFT": - mLetterboxConfigController.setLandscapeGravity(Gravity.LEFT); - break; - case "CENTER": - mLetterboxConfigController.setLandscapeGravity(Gravity.CENTER); - break; - case "RIGHT": - mLetterboxConfigController.setLandscapeGravity(Gravity.RIGHT); - break; - default: - pw.println( - "Error: expected reset, LEFT, CENTER or RIGHT but got " + args[2]); - } - return true; - } - - private boolean runGetLetterboxLandscapeGravity(PrintWriter pw) { - final int gravity = mLetterboxConfigController.getLandscapeGravity(); - switch (gravity) { - case Gravity.LEFT: - pw.println("LEFT"); - break; - case Gravity.CENTER: - pw.println("CENTER"); - break; - case Gravity.RIGHT: - pw.println("RIGHT"); - break; - default: - throw new AssertionError("Unexpected gravity: " + gravity); - } - return true; - } private boolean runPair(String[] args, PrintWriter pw) { if (args.length < 4) { @@ -220,12 +120,6 @@ public final class ShellCommandHandler { pw.println(" Print this help text."); pw.println(" <no arguments provided>"); pw.println(" Dump Window Manager Shell internal state"); - pw.println(" set-letterbox-portrait-gravity [reset|TOP|CENTER|BOTTOM]"); - pw.println(" get-letterbox-portrait-gravity"); - pw.println(" Set, reset or print letterbox gravity for portrait screen mode."); - pw.println(" set-letterbox-landscape-gravity [reset|LEFT|CENTER|RIGHT]"); - pw.println(" get-letterbox-landscape-gravity"); - pw.println(" Set, reset or print letterbox gravity for landscape screen mode."); pw.println(" pair <taskId1> <taskId2>"); pw.println(" unpair <taskId>"); pw.println(" Pairs/unpairs tasks with given ids."); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java index 6c08079e586e..f8956030936c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInit.java @@ -17,13 +17,11 @@ package com.android.wm.shell; import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN; -import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_LETTERBOX; import com.android.wm.shell.apppairs.AppPairs; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.draganddrop.DragAndDropController; -import com.android.wm.shell.letterbox.LetterboxTaskListener; import com.android.wm.shell.splitscreen.SplitScreen; import java.util.Optional; @@ -38,7 +36,6 @@ public class ShellInit { private final ShellTaskOrganizer mShellTaskOrganizer; private final Optional<SplitScreen> mSplitScreenOptional; private final Optional<AppPairs> mAppPairsOptional; - private final LetterboxTaskListener mLetterboxTaskListener; private final FullscreenTaskListener mFullscreenTaskListener; private final Transitions mTransitions; @@ -47,7 +44,6 @@ public class ShellInit { ShellTaskOrganizer shellTaskOrganizer, Optional<SplitScreen> splitScreenOptional, Optional<AppPairs> appPairsOptional, - LetterboxTaskListener letterboxTaskListener, FullscreenTaskListener fullscreenTaskListener, Transitions transitions) { mDisplayImeController = displayImeController; @@ -55,7 +51,6 @@ public class ShellInit { mShellTaskOrganizer = shellTaskOrganizer; mSplitScreenOptional = splitScreenOptional; mAppPairsOptional = appPairsOptional; - mLetterboxTaskListener = letterboxTaskListener; mFullscreenTaskListener = fullscreenTaskListener; mTransitions = transitions; } @@ -66,8 +61,6 @@ public class ShellInit { mDisplayImeController.startMonitorDisplays(); mShellTaskOrganizer.addListenerForType( - mLetterboxTaskListener, TASK_LISTENER_TYPE_LETTERBOX); - mShellTaskOrganizer.addListenerForType( mFullscreenTaskListener, TASK_LISTENER_TYPE_FULLSCREEN); // Register the shell organizer mShellTaskOrganizer.registerOrganizer(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java index 62d265aab38f..10cec6d59cbe 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -62,14 +62,12 @@ public class ShellTaskOrganizer extends TaskOrganizer { public static final int TASK_LISTENER_TYPE_FULLSCREEN = -2; public static final int TASK_LISTENER_TYPE_MULTI_WINDOW = -3; public static final int TASK_LISTENER_TYPE_PIP = -4; - public static final int TASK_LISTENER_TYPE_LETTERBOX = -5; @IntDef(prefix = {"TASK_LISTENER_TYPE_"}, value = { TASK_LISTENER_TYPE_UNDEFINED, TASK_LISTENER_TYPE_FULLSCREEN, TASK_LISTENER_TYPE_MULTI_WINDOW, TASK_LISTENER_TYPE_PIP, - TASK_LISTENER_TYPE_LETTERBOX, }) public @interface TaskListenerType {} @@ -371,9 +369,7 @@ public class ShellTaskOrganizer extends TaskOrganizer { static @TaskListenerType int taskInfoToTaskListenerType(RunningTaskInfo runningTaskInfo) { switch (runningTaskInfo.getWindowingMode()) { case WINDOWING_MODE_FULLSCREEN: - return runningTaskInfo.letterboxActivityBounds != null - ? TASK_LISTENER_TYPE_LETTERBOX - : TASK_LISTENER_TYPE_FULLSCREEN; + return TASK_LISTENER_TYPE_FULLSCREEN; case WINDOWING_MODE_MULTI_WINDOW: return TASK_LISTENER_TYPE_MULTI_WINDOW; case WINDOWING_MODE_PINNED: @@ -389,8 +385,6 @@ public class ShellTaskOrganizer extends TaskOrganizer { switch (type) { case TASK_LISTENER_TYPE_FULLSCREEN: return "TASK_LISTENER_TYPE_FULLSCREEN"; - case TASK_LISTENER_TYPE_LETTERBOX: - return "TASK_LISTENER_TYPE_LETTERBOX"; case TASK_LISTENER_TYPE_MULTI_WINDOW: return "TASK_LISTENER_TYPE_MULTI_WINDOW"; case TASK_LISTENER_TYPE_PIP: diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index 4c70b5d32108..e1a94b071bf4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -123,14 +123,16 @@ public class SplitLayout { mBounds1.set(mRootBounds); mBounds2.set(mRootBounds); if (isLandscape(mRootBounds)) { + position += mRootBounds.left; mDividerBounds.left = position - mDividerInsets; mDividerBounds.right = mDividerBounds.left + mDividerWindowWidth; - mBounds1.right = mBounds1.left + position; + mBounds1.right = position; mBounds2.left = mBounds1.right + mDividerSize; } else { + position += mRootBounds.top; mDividerBounds.top = position - mDividerInsets; mDividerBounds.bottom = mDividerBounds.top + mDividerWindowWidth; - mBounds1.bottom = mBounds1.top + position; + mBounds1.bottom = position; mBounds2.top = mBounds1.bottom + mDividerSize; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/letterbox/LetterboxConfigController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/letterbox/LetterboxConfigController.java deleted file mode 100644 index 0a549c6aa7d9..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/letterbox/LetterboxConfigController.java +++ /dev/null @@ -1,108 +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.letterbox; - -import android.content.Context; -import android.view.Gravity; - -import com.android.wm.shell.R; - -/** - * Controls access to and overrides of resource config values used by {@link - * LetterboxTaskOrganizer}. - */ -public final class LetterboxConfigController { - - private final Context mContext; - - /** {@link Gravity} of letterboxed apps in portrait screen orientation. */ - private int mLetterboxPortraitGravity; - - /** {@link Gravity} of letterboxed apps in landscape screen orientation. */ - private int mLetterboxLandscapeGravity; - - public LetterboxConfigController(Context context) { - mContext = context; - mLetterboxPortraitGravity = - mContext.getResources().getInteger(R.integer.config_letterboxPortraitGravity); - mLetterboxLandscapeGravity = - mContext.getResources().getInteger(R.integer.config_letterboxLandscapeGravity); - } - - /** - * Overrides {@link Gravity} of letterboxed apps in portrait screen orientation. - * - * @throws IllegalArgumentException if gravity isn't equal to {@link Gravity#TOP}, {@link - * Gravity#CENTER} or {@link Gravity#BOTTOM}. - */ - public void setPortraitGravity(int gravity) { - if (gravity != Gravity.TOP && gravity != Gravity.CENTER && gravity != Gravity.BOTTOM) { - throw new IllegalArgumentException( - "Expected Gravity#TOP, Gravity#CENTER or Gravity#BOTTOM but got" - + gravity); - } - mLetterboxPortraitGravity = gravity; - } - - /** - * Resets {@link Gravity} of letterboxed apps in portrait screen orientation to {@link - * R.integer.config_letterboxPortraitGravity}. - */ - public void resetPortraitGravity() { - mLetterboxPortraitGravity = - mContext.getResources().getInteger(R.integer.config_letterboxPortraitGravity); - } - - /** - * Gets {@link Gravity} of letterboxed apps in portrait screen orientation. - */ - public int getPortraitGravity() { - return mLetterboxPortraitGravity; - } - - /** - * Overrides {@link Gravity} of letterboxed apps in landscape screen orientation. - * - * @throws IllegalArgumentException if gravity isn't equal to {@link Gravity#RIGHT}, {@link - * Gravity#CENTER} or {@link Gravity#LEFT}. - */ - public void setLandscapeGravity(int gravity) { - if (gravity != Gravity.LEFT && gravity != Gravity.CENTER && gravity != Gravity.RIGHT) { - throw new IllegalArgumentException( - "Expected Gravity#LEFT, Gravity#CENTER or Gravity#RIGHT but got" - + gravity); - } - mLetterboxLandscapeGravity = gravity; - } - - /** - * Resets {@link Gravity} of letterboxed apps in landscape screen orientation to {@link - * R.integer.config_letterboxLandscapeGravity}. - */ - public void resetLandscapeGravity() { - mLetterboxLandscapeGravity = - mContext.getResources().getInteger(R.integer.config_letterboxLandscapeGravity); - } - - /** - * Gets {@link Gravity} of letterboxed apps in landscape screen orientation. - */ - public int getLandscapeGravity() { - return mLetterboxLandscapeGravity; - } - -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/letterbox/LetterboxTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/letterbox/LetterboxTaskListener.java deleted file mode 100644 index 490ef3296be6..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/letterbox/LetterboxTaskListener.java +++ /dev/null @@ -1,267 +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.letterbox; - -import android.app.ActivityManager; -import android.graphics.Insets; -import android.graphics.Point; -import android.graphics.Rect; -import android.util.Slog; -import android.util.SparseArray; -import android.view.Gravity; -import android.view.SurfaceControl; -import android.view.WindowInsets; -import android.view.WindowManager; - -import com.android.internal.protolog.common.ProtoLog; -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.Transitions; -import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.protolog.ShellProtoLogGroup; - -/** - * Organizes a task in {@link android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN} when - * it's presented in the letterbox mode either because orientations of a top activity and a device - * don't match or because a top activity is in a size compat mode. - */ -public class LetterboxTaskListener implements ShellTaskOrganizer.TaskListener { - private static final String TAG = "LetterboxTaskListener"; - - private final SyncTransactionQueue mSyncQueue; - private final LetterboxConfigController mLetterboxConfigController; - private final WindowManager mWindowManager; - private final SparseArray<SurfaceControl> mLeashByTaskId = new SparseArray<>(); - - public LetterboxTaskListener( - SyncTransactionQueue syncQueue, - LetterboxConfigController letterboxConfigController, - WindowManager windowManager) { - mSyncQueue = syncQueue; - mLetterboxConfigController = letterboxConfigController; - mWindowManager = windowManager; - } - - @Override - public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { - if (mLeashByTaskId.get(taskInfo.taskId) != null) { - throw new IllegalStateException("Task appeared more than once: #" + taskInfo.taskId); - } - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Letterbox Task Appeared: #%d", - taskInfo.taskId); - mLeashByTaskId.put(taskInfo.taskId, leash); - Point positionInParent = new Point(); - Rect crop = new Rect(); - resolveTaskPositionAndCrop(taskInfo, positionInParent, crop); - mSyncQueue.runInSync(t -> { - setPositionAndWindowCrop(t, leash, positionInParent, crop); - if (!Transitions.ENABLE_SHELL_TRANSITIONS) { - t.setAlpha(leash, 1f); - t.setMatrix(leash, 1, 0, 0, 1); - t.show(leash); - } - }); - } - - @Override - public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { - if (mLeashByTaskId.get(taskInfo.taskId) == null) { - Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId); - return; - } - mLeashByTaskId.remove(taskInfo.taskId); - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Letterbox Task Vanished: #%d", - taskInfo.taskId); - } - - @Override - public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Letterbox Task Changed: #%d", - taskInfo.taskId); - final SurfaceControl leash = mLeashByTaskId.get(taskInfo.taskId); - Point positionInParent = new Point(); - Rect crop = new Rect(); - resolveTaskPositionAndCrop(taskInfo, positionInParent, crop); - mSyncQueue.runInSync(t -> setPositionAndWindowCrop(t, leash, positionInParent, crop)); - } - - private static void setPositionAndWindowCrop( - SurfaceControl.Transaction transaction, - SurfaceControl leash, - final Point positionInParent, - final Rect crop) { - transaction.setPosition(leash, positionInParent.x, positionInParent.y); - transaction.setWindowCrop(leash, crop); - } - - // TODO(b/173440321): Correct presentation of letterboxed activities in One-handed mode. - private void resolveTaskPositionAndCrop( - ActivityManager.RunningTaskInfo taskInfo, - Point positionInParent, - Rect crop) { - // In screen coordinates - Rect parentBounds = new Rect(taskInfo.parentBounds); - // Intersect parent and max bounds. This is required for situations when parent bounds - // go beyond display bounds, for example, in One-handed mode. - final Rect maxBounds = taskInfo.getConfiguration().windowConfiguration.getMaxBounds(); - if (!parentBounds.intersect(maxBounds)) { - Slog.w(TAG, "Task parent and max bounds don't intersect: #" + taskInfo.taskId); - } - - // In screen coordinates - final Rect taskBounds = taskInfo.getConfiguration().windowConfiguration.getBounds(); - final Rect activityBounds = taskInfo.letterboxActivityBounds; - - Insets insets = getInsets(); - Rect displayBoundsWithInsets = - new Rect(mWindowManager.getMaximumWindowMetrics().getBounds()); - displayBoundsWithInsets.inset(insets); - - Rect taskBoundsWithInsets = new Rect(taskBounds); - taskBoundsWithInsets.intersect(displayBoundsWithInsets); - - Rect activityBoundsWithInsets = new Rect(activityBounds); - activityBoundsWithInsets.intersect(displayBoundsWithInsets); - - Rect parentBoundsWithInsets = new Rect(parentBounds); - parentBoundsWithInsets.intersect(displayBoundsWithInsets); - - // Crop need to be in the task coordinates. - crop.set(activityBoundsWithInsets); - crop.offset(-taskBounds.left, -taskBounds.top); - - // Account for insets since coordinates calculations below are done with them. - positionInParent.x = parentBoundsWithInsets.left - parentBounds.left - - (taskBoundsWithInsets.left - taskBounds.left); - positionInParent.y = parentBoundsWithInsets.top - parentBounds.top - - (taskBoundsWithInsets.top - taskBounds.top); - - // Calculating a position of task bounds (without insets) in parent coordinates (without - // insets) to align activity bounds (without insets) as requested in config. Activity - // accounts for insets that overlap with its bounds (this overlap can be partial) so - // ignoring overlap with insets when computing the position. Also, cropping unwanted insets - // while keeping the top one if the activity is aligned at the top of the window to show - // status bar decor view. - if (parentBounds.height() >= parentBounds.width()) { - final int gravity = mLetterboxConfigController.getPortraitGravity(); - // Center activity horizontally. - positionInParent.x += - (parentBoundsWithInsets.width() - activityBoundsWithInsets.width()) / 2 - + taskBoundsWithInsets.left - activityBoundsWithInsets.left; - switch (gravity) { - case Gravity.TOP: - positionInParent.y += taskBoundsWithInsets.top - activityBoundsWithInsets.top; - break; - case Gravity.CENTER: - positionInParent.y += - taskBoundsWithInsets.top - activityBoundsWithInsets.top - + (parentBoundsWithInsets.height() - - activityBoundsWithInsets.height()) / 2; - break; - case Gravity.BOTTOM: - positionInParent.y += - parentBoundsWithInsets.height() - activityBoundsWithInsets.bottom - + taskBoundsWithInsets.top; - break; - default: - throw new AssertionError( - "Unexpected portrait gravity " + gravity - + " for task: #" + taskInfo.taskId); - } - } else { - final int gravity = mLetterboxConfigController.getLandscapeGravity(); - // Align activity to the top. - positionInParent.y += taskBoundsWithInsets.top - activityBoundsWithInsets.top; - switch (gravity) { - case Gravity.LEFT: - positionInParent.x += taskBoundsWithInsets.left - activityBoundsWithInsets.left; - break; - case Gravity.CENTER: - positionInParent.x += - (parentBoundsWithInsets.width() - activityBoundsWithInsets.width()) / 2 - + taskBoundsWithInsets.left - activityBoundsWithInsets.left; - break; - case Gravity.RIGHT: - positionInParent.x += - parentBoundsWithInsets.width() - - activityBoundsWithInsets.right + taskBoundsWithInsets.left; - break; - default: - throw new AssertionError( - "Unexpected landscape gravity " + gravity - + " for task: #" + taskInfo.taskId); - } - } - - // New bounds of the activity after it's repositioned with required gravity. - Rect newActivityBounds = new Rect(activityBounds); - // Task's surfce will be repositioned to positionInParent together with the activity - // inside it so the new activity bounds are the original activity bounds offset by - // the task's offset. - newActivityBounds.offset( - positionInParent.x - taskBounds.left, positionInParent.y - taskBounds.top); - Rect newActivityBoundsWithInsets = new Rect(newActivityBounds); - newActivityBoundsWithInsets.intersect(displayBoundsWithInsets); - // Activity handles insets on its own (e.g. under status bar or navigation bar). - // crop that is calculated above crops all insets from an activity and below insets that - // can be shown are added back to the crop bounds (e.g. if activity is still shown at the - // top of the display then the top inset won't be cropped). - // After task's surface is repositioned, intersection between an activity and insets can - // change but if it doesn't, the activity should be shown under insets to maximize visible - // area. - // Also, an activity can use area under insets and insets shouldn't be cropped in this case - // regardless of a position on the screen. - final Rect activityInsetsFromCore = taskInfo.letterboxActivityInsets; - if (newActivityBounds.top - newActivityBoundsWithInsets.top - == activityBounds.top - activityBoundsWithInsets.top - // Check whether an activity is shown under inset. If it is, then the inset from - // WM Core and the inset computed here will be different because local insets - // doesn't take into account visibility of insets requested by the activity. - || activityBoundsWithInsets.top - activityBounds.top - != activityInsetsFromCore.top) { - crop.top -= activityBoundsWithInsets.top - activityBounds.top; - } - if (newActivityBounds.bottom - newActivityBoundsWithInsets.bottom - == activityBounds.bottom - activityBoundsWithInsets.bottom - || activityBounds.bottom - activityBoundsWithInsets.bottom - != activityInsetsFromCore.bottom) { - crop.bottom += activityBounds.bottom - activityBoundsWithInsets.bottom; - } - if (newActivityBounds.left - newActivityBoundsWithInsets.left - == activityBounds.left - activityBoundsWithInsets.left - || activityBoundsWithInsets.left - activityBounds.left - != activityInsetsFromCore.left) { - crop.left -= activityBoundsWithInsets.left - activityBounds.left; - } - if (newActivityBounds.right - newActivityBoundsWithInsets.right - == activityBounds.right - activityBoundsWithInsets.right - || activityBounds.right - activityBoundsWithInsets.right - != activityInsetsFromCore.right) { - crop.right += activityBounds.right - activityBoundsWithInsets.right; - } - } - - private Insets getInsets() { - return mWindowManager - .getMaximumWindowMetrics() - .getWindowInsets() - .getInsets( - WindowInsets.Type.navigationBars() - | WindowInsets.Type.statusBars() - | WindowInsets.Type.displayCutout()); - } - -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java index 3b1e6cbe5ccd..951a68884e11 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java @@ -75,6 +75,7 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback, @Nullable OneHandedGestureEventCallback mGestureEventCallback; private Rect mGestureRegion = new Rect(); + private boolean mIsStopGesture; /** * Constructor of OneHandedGestureHandler, we only handle the gesture of @@ -153,20 +154,20 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback, float distance = (float) Math.hypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y); if (distance > mDragDistThreshold) { - mGestureEventCallback.onStop(); + mIsStopGesture = true; } } break; case MotionEvent.ACTION_UP: if (mLastPos.y >= mDownPos.y && mPassedSlop) { mGestureEventCallback.onStart(); + } else if (mIsStopGesture) { + mGestureEventCallback.onStop(); } - mPassedSlop = false; - mAllowGesture = false; + clearState(); break; case MotionEvent.ACTION_CANCEL: - mPassedSlop = false; - mAllowGesture = false; + clearState(); break; default: break; @@ -174,6 +175,11 @@ public class OneHandedGestureHandler implements OneHandedTransitionCallback, } } + private void clearState() { + mPassedSlop = false; + mIsStopGesture = false; + } + private void disposeInputChannel() { if (mInputEventReceiver != null) { mInputEventReceiver.dispose(); diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt index 1638d72f9914..c5b54bc4abcb 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt @@ -16,8 +16,10 @@ package com.android.wm.shell.flicker +import android.graphics.Region import com.android.server.wm.flicker.dsl.EventLogAssertion import com.android.server.wm.flicker.dsl.LayersAssertion +import com.android.server.wm.flicker.helpers.WindowUtils @JvmOverloads fun LayersAssertion.appPairsDividerIsVisible( @@ -59,6 +61,50 @@ fun LayersAssertion.dockedStackDividerIsInvisible( } } +@JvmOverloads +fun LayersAssertion.dockedStackPrimaryBoundsIsVisible( + rotation: Int, + primaryLayerName: String, + bugId: Int = 0, + enabled: Boolean = bugId == 0 +) { + end("PrimaryAppBounds", bugId, enabled) { + val entry = this.trace.entries.firstOrNull() + ?: throw IllegalStateException("Trace is empty") + val dividerRegion = entry.getVisibleBounds(FlickerTestBase.DOCKED_STACK_DIVIDER) + this.hasVisibleRegion(primaryLayerName, getPrimaryRegion(dividerRegion, rotation)) + } +} + +@JvmOverloads +fun LayersAssertion.dockedStackSecondaryBoundsIsVisible( + rotation: Int, + secondaryLayerName: String, + bugId: Int = 0, + enabled: Boolean = bugId == 0 +) { + end("SecondaryAppBounds", bugId, enabled) { + val entry = this.trace.entries.firstOrNull() + ?: throw IllegalStateException("Trace is empty") + val dividerRegion = entry.getVisibleBounds(FlickerTestBase.DOCKED_STACK_DIVIDER) + this.hasVisibleRegion(secondaryLayerName, getSecondaryRegion(dividerRegion, rotation)) + } +} + +fun getPrimaryRegion(dividerRegion: Region, rotation: Int): Region { + val displayBounds = WindowUtils.getDisplayBounds(rotation) + return Region(0, 0, displayBounds.getBounds().right, + dividerRegion.getBounds().bottom - WindowUtils.dockedStackDividerInset) +} + +fun getSecondaryRegion(dividerRegion: Region, rotation: Int): Region { + val displayBounds = WindowUtils.getDisplayBounds(rotation) + return Region(0, + dividerRegion.getBounds().bottom - WindowUtils.dockedStackDividerInset, + displayBounds.getBounds().right, + displayBounds.getBounds().bottom - WindowUtils.navigationBarHeight) +} + fun EventLogAssertion.focusChanges( vararg windows: String, bugId: Int = 0, diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt index 96234fcc8570..5125a3972cf4 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt @@ -37,3 +37,4 @@ const val TEST_APP_FIXED_ACTIVITY_LABEL = "FixedApp" // Test App > SplitScreen Activity const val TEST_APP_SPLITSCREEN_PRIMARY_LABEL = "SplitScreenPrimaryApp" const val TEST_APP_SPLITSCREEN_SECONDARY_LABEL = "SplitScreenSecondaryApp" +const val TEST_APP_NONRESIZEABLE_LABEL = "NonResizeableApp" diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt index 0f8d30a94ec6..379ec95b6792 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTest.kt @@ -61,10 +61,9 @@ class AppPairsTest( setup { eachRun { uiDevice.wakeUpAndGoToHomeScreen() - primaryApp.open() - uiDevice.pressHome() - secondaryApp.open() - uiDevice.pressHome() + primaryApp.launchViaIntent() + secondaryApp.launchViaIntent() + nonResizeableApp.launchViaIntent() updateTaskId() } } @@ -90,7 +89,7 @@ class AppPairsTest( @Test fun testAppPairs_pairPrimaryAndSecondaryApps() { - val testTag = "testAppPaired_pairPrimaryAndSecondary" + val testTag = "testAppPairs_pairPrimaryAndSecondaryApps" runWithFlicker(appPairsSetup) { withTestName { testTag } repeat { @@ -176,6 +175,36 @@ class AppPairsTest( } } + @Test + fun testAppPairs_canNotPairNonResizeableApps() { + val testTag = "testAppPairs_canNotPairNonResizeableApps" + runWithFlicker(appPairsSetup) { + withTestName { testTag } + repeat { + TEST_REPETITIONS + } + transitions { + nonResizeableApp.launchViaIntent() + // TODO pair apps through normal UX flow + executeShellCommand(composePairsCommand( + primaryTaskId, nonResizeableTaskId, true /* pair */)) + SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) + } + assertions { + layersTrace { + appPairsDividerIsInvisible() + } + windowManagerTrace { + end { + showsAppWindow(nonResizeableApp.defaultWindowName) + .and() + .hidesAppWindow(primaryApp.defaultWindowName) + } + } + } + } + } + private fun composePairsCommand( primaryApp: String, secondaryApp: String, @@ -202,6 +231,7 @@ class AppPairsTest( private fun updateTaskId() { val primaryAppComponent = primaryApp.openAppIntent.component val secondaryAppComponent = secondaryApp.openAppIntent.component + val nonResizeableAppComponent = nonResizeableApp.openAppIntent.component if (primaryAppComponent != null) { primaryTaskId = appPairsHelper.getTaskIdForActivity( primaryAppComponent.packageName, primaryAppComponent.className).toString() @@ -210,11 +240,17 @@ class AppPairsTest( secondaryTaskId = appPairsHelper.getTaskIdForActivity( secondaryAppComponent.packageName, secondaryAppComponent.className).toString() } + if (nonResizeableAppComponent != null) { + nonResizeableTaskId = appPairsHelper.getTaskIdForActivity( + nonResizeableAppComponent.packageName, + nonResizeableAppComponent.className).toString() + } } companion object { var primaryTaskId = "" var secondaryTaskId = "" + var nonResizeableTaskId = "" @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<Array<Any>> { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestBase.kt index f32cd8842074..4d46f2856704 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestBase.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestBase.kt @@ -17,6 +17,7 @@ package com.android.wm.shell.flicker.apppairs import com.android.wm.shell.flicker.NonRotationTestBase +import com.android.wm.shell.flicker.TEST_APP_NONRESIZEABLE_LABEL import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_PRIMARY_LABEL import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_SECONDARY_LABEL import com.android.wm.shell.flicker.helpers.AppPairsHelper @@ -36,4 +37,7 @@ abstract class AppPairsTestBase( protected val secondaryApp = SplitScreenHelper(instrumentation, TEST_APP_SPLITSCREEN_SECONDARY_LABEL, Components.SplitScreenSecondaryActivity()) + protected val nonResizeableApp = SplitScreenHelper(instrumentation, + TEST_APP_NONRESIZEABLE_LABEL, + Components.NonResizeableActivity()) } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt index e67fc97dad2e..fb795c72b106 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt @@ -17,9 +17,7 @@ package com.android.wm.shell.flicker.helpers import android.app.Instrumentation -import android.graphics.Region import android.os.SystemClock -import com.android.server.wm.flicker.helpers.WindowUtils import com.android.wm.shell.flicker.testapp.Components class SplitScreenHelper( @@ -43,20 +41,6 @@ class SplitScreenHelper( SystemClock.sleep(TIMEOUT_MS) } - fun getPrimaryBounds(dividerBounds: Region): android.graphics.Region { - val primaryAppBounds = Region(0, 0, dividerBounds.bounds.right, - dividerBounds.bounds.bottom + WindowUtils.dockedStackDividerInset) - return primaryAppBounds - } - - fun getSecondaryBounds(dividerBounds: Region): android.graphics.Region { - val displayBounds = WindowUtils.displayBounds - val secondaryAppBounds = Region(0, - dividerBounds.bounds.bottom - WindowUtils.dockedStackDividerInset, - displayBounds.right, displayBounds.bottom - WindowUtils.navigationBarHeight) - return secondaryAppBounds - } - companion object { const val TEST_REPETITIONS = 1 const val TIMEOUT_MS = 3_000L diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt index 5570a562a515..348676189524 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenTest.kt @@ -21,14 +21,23 @@ import android.view.Surface import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.dsl.runWithFlicker +import com.android.server.wm.flicker.helpers.WindowUtils +import com.android.server.wm.flicker.helpers.canSplitScreen +import com.android.server.wm.flicker.helpers.exitSplitScreen +import com.android.server.wm.flicker.helpers.isInSplitScreen import com.android.server.wm.flicker.helpers.launchSplitScreen +import com.android.server.wm.flicker.helpers.openQuickstep import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen +import com.android.wm.shell.flicker.dockedStackDividerIsInvisible import com.android.wm.shell.flicker.dockedStackDividerIsVisible import com.android.wm.shell.flicker.helpers.SplitScreenHelper.Companion.TEST_REPETITIONS import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible +import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible +import org.junit.Assert import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -56,14 +65,16 @@ class EnterSplitScreenTest( setup { eachRun { uiDevice.wakeUpAndGoToHomeScreen() - splitScreenApp.open() - uiDevice.pressHome() } } teardown { eachRun { + if (uiDevice.isInSplitScreen()) { + uiDevice.exitSplitScreen() + } splitScreenApp.exit() secondaryApp.exit() + nonResizeableApp.exit() } } assertions { @@ -87,18 +98,14 @@ class EnterSplitScreenTest( TEST_REPETITIONS } transitions { + splitScreenApp.launchViaIntent() uiDevice.launchSplitScreen() } assertions { layersTrace { dockedStackDividerIsVisible() - end("appsEndingBounds", enabled = false) { - val entry = this.trace.entries.firstOrNull() - ?: throw IllegalStateException("Trace is empty") - this.hasVisibleRegion(splitScreenApp.defaultWindowName, - splitScreenApp.getPrimaryBounds( - entry.getVisibleBounds(DOCKED_STACK_DIVIDER))) - } + dockedStackPrimaryBoundsIsVisible( + rotation, splitScreenApp.defaultWindowName, 169271943) } windowManagerTrace { end { @@ -118,27 +125,18 @@ class EnterSplitScreenTest( TEST_REPETITIONS } transitions { - secondaryApp.open() - uiDevice.pressHome() - splitScreenApp.open() - uiDevice.pressHome() + secondaryApp.launchViaIntent() + splitScreenApp.launchViaIntent() uiDevice.launchSplitScreen() splitScreenApp.reopenAppFromOverview() } assertions { layersTrace { dockedStackDividerIsVisible() - end("appsEndingBounds", enabled = false) { - val entry = this.trace.entries.firstOrNull() - ?: throw IllegalStateException("Trace is empty") - this.hasVisibleRegion(splitScreenApp.defaultWindowName, - splitScreenApp.getPrimaryBounds( - entry.getVisibleBounds(DOCKED_STACK_DIVIDER))) - .and() - .hasVisibleRegion(secondaryApp.defaultWindowName, - splitScreenApp.getSecondaryBounds( - entry.getVisibleBounds(DOCKED_STACK_DIVIDER))) - } + dockedStackPrimaryBoundsIsVisible( + rotation, splitScreenApp.defaultWindowName, 169271943) + dockedStackSecondaryBoundsIsVisible( + rotation, secondaryApp.defaultWindowName, 169271943) } windowManagerTrace { end { @@ -150,6 +148,98 @@ class EnterSplitScreenTest( } } + @Test + fun testNonResizeableNotDocked() { + val testTag = "testNonResizeableNotDocked" + runWithFlicker(splitScreenSetup) { + withTestName { testTag } + repeat { + TEST_REPETITIONS + } + transitions { + nonResizeableApp.launchViaIntent() + uiDevice.openQuickstep() + if (uiDevice.canSplitScreen()) { + Assert.fail("Non-resizeable app should not enter split screen") + } + } + assertions { + layersTrace { + dockedStackDividerIsInvisible() + } + windowManagerTrace { + end { + hidesAppWindow(nonResizeableApp.defaultWindowName) + } + } + } + } + } + + @Test + fun testNonResizeableWhenAlreadyInSplitScreenPrimary() { + val testTag = "testNonResizeableWhenAlreadyInSplitScreenPrimary" + runWithFlicker(splitScreenSetup) { + withTestName { testTag } + repeat { + TEST_REPETITIONS + } + transitions { + nonResizeableApp.launchViaIntent() + splitScreenApp.launchViaIntent() + uiDevice.launchSplitScreen() + nonResizeableApp.reopenAppFromOverview() + } + assertions { + layersTrace { + dockedStackDividerIsInvisible() + end("appsEndingBounds", enabled = false) { + val displayBounds = WindowUtils.getDisplayBounds(rotation) + this.hasVisibleRegion(nonResizeableApp.defaultWindowName, displayBounds) + } + } + windowManagerTrace { + end { + showsAppWindow(nonResizeableApp.defaultWindowName) + hidesAppWindow(splitScreenApp.defaultWindowName) + } + } + } + } + } + + @Test + fun testNonResizeableWhenAlreadyInSplitScreenSecondary() { + val testTag = "testNonResizeableWhenAlreadyInSplitScreenSecondary" + runWithFlicker(splitScreenSetup) { + withTestName { testTag } + repeat { + TEST_REPETITIONS + } + transitions { + splitScreenApp.launchViaIntent() + uiDevice.launchSplitScreen() + uiDevice.pressBack() + nonResizeableApp.launchViaIntent() + } + assertions { + layersTrace { + dockedStackDividerIsInvisible() + end("appsEndingBounds", enabled = false) { + val displayBounds = WindowUtils.getDisplayBounds(rotation) + this.hasVisibleRegion(nonResizeableApp.defaultWindowName, displayBounds) + } + } + windowManagerTrace { + end { + showsAppWindow(nonResizeableApp.defaultWindowName) + hidesAppWindow(splitScreenApp.defaultWindowName) + } + } + } + } + } + companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/RotateOneLaunchedAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/RotateOneLaunchedAppTest.kt new file mode 100644 index 000000000000..be8f9bcdedb6 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/RotateOneLaunchedAppTest.kt @@ -0,0 +1,146 @@ +/* + * 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.splitscreen + +import android.view.Surface +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.dsl.runWithFlicker +import com.android.server.wm.flicker.helpers.exitSplitScreen +import com.android.server.wm.flicker.helpers.isInSplitScreen +import com.android.server.wm.flicker.helpers.launchSplitScreen +import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen +import com.android.server.wm.flicker.navBarLayerRotatesAndScales +import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.statusBarLayerRotatesScales +import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import com.android.wm.shell.flicker.dockedStackDividerIsVisible +import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible +import com.android.wm.shell.flicker.helpers.SplitScreenHelper +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test open app to split screen. + * To run this test: `atest WMShellFlickerTests:RotateOneLaunchedAppTest` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class RotateOneLaunchedAppTest( + rotationName: String, + rotation: Int +) : SplitScreenTestBase(rotationName, rotation) { + private val splitScreenRotationSetup: FlickerBuilder + get() = FlickerBuilder(instrumentation).apply { + val testSetupRotation = "testSetupRotation" + withTestName { + testSetupRotation + } + setup { + test { + device.wakeUpAndGoToHomeScreen() + } + } + teardown { + eachRun { + if (uiDevice.isInSplitScreen()) { + uiDevice.exitSplitScreen() + } + setRotation(Surface.ROTATION_0) + splitScreenApp.exit() + secondaryApp.exit() + } + } + } + + @Test + fun testRotateInSplitScreenMode() { + val testTag = "testEnterSplitScreen_launchToSide" + runWithFlicker(splitScreenRotationSetup) { + withTestName { testTag } + repeat { + SplitScreenHelper.TEST_REPETITIONS + } + transitions { + splitScreenApp.launchViaIntent() + device.launchSplitScreen() + setRotation(rotation) + } + assertions { + layersTrace { + navBarLayerRotatesAndScales(Surface.ROTATION_0, rotation, 169271943) + statusBarLayerRotatesScales(Surface.ROTATION_0, rotation, 169271943) + dockedStackDividerIsVisible() + dockedStackPrimaryBoundsIsVisible( + rotation, splitScreenApp.defaultWindowName, 169271943) + } + windowManagerTrace { + navBarWindowIsAlwaysVisible() + statusBarWindowIsAlwaysVisible() + end { + showsAppWindow(splitScreenApp.defaultWindowName) + } + } + } + } + } + + @Test + fun testRotateAndEnterSplitScreenMode() { + val testTag = "testRotateAndEnterSplitScreenMode" + runWithFlicker(splitScreenRotationSetup) { + withTestName { testTag } + repeat { + SplitScreenHelper.TEST_REPETITIONS + } + transitions { + splitScreenApp.launchViaIntent() + setRotation(rotation) + device.launchSplitScreen() + } + assertions { + layersTrace { + navBarLayerRotatesAndScales(Surface.ROTATION_0, rotation, 169271943) + statusBarLayerRotatesScales(Surface.ROTATION_0, rotation, 169271943) + dockedStackDividerIsVisible() + dockedStackPrimaryBoundsIsVisible( + rotation, splitScreenApp.defaultWindowName, 169271943) + } + windowManagerTrace { + navBarWindowIsAlwaysVisible() + statusBarWindowIsAlwaysVisible() + end { + showsAppWindow(splitScreenApp.defaultWindowName) + } + } + } + } + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<Array<Any>> { + val supportedRotations = intArrayOf(Surface.ROTATION_90, Surface.ROTATION_270) + return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) } + } + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/RotateTwoLaunchedAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/RotateTwoLaunchedAppTest.kt new file mode 100644 index 000000000000..2e05c2a99273 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/RotateTwoLaunchedAppTest.kt @@ -0,0 +1,157 @@ +/* + * 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.splitscreen + +import android.view.Surface +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.dsl.runWithFlicker +import com.android.server.wm.flicker.helpers.exitSplitScreen +import com.android.server.wm.flicker.helpers.isInSplitScreen +import com.android.server.wm.flicker.helpers.launchSplitScreen +import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen +import com.android.server.wm.flicker.navBarLayerRotatesAndScales +import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.statusBarLayerRotatesScales +import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import com.android.wm.shell.flicker.dockedStackDividerIsVisible +import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible +import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible +import com.android.wm.shell.flicker.helpers.SplitScreenHelper +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test open app to split screen. + * To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppTest` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class RotateTwoLaunchedAppTest( + rotationName: String, + rotation: Int +) : SplitScreenTestBase(rotationName, rotation) { + private val splitScreenRotationSetup: FlickerBuilder + get() = FlickerBuilder(instrumentation).apply { + val testSetupRotation = "testSetupRotation" + withTestName { + testSetupRotation + } + setup { + test { + device.wakeUpAndGoToHomeScreen() + } + } + teardown { + eachRun { + if (uiDevice.isInSplitScreen()) { + uiDevice.exitSplitScreen() + } + setRotation(Surface.ROTATION_0) + splitScreenApp.exit() + secondaryApp.exit() + } + } + } + + @Test + fun testRotateInSplitScreenMode() { + val testTag = "testRotateInSplitScreenMode" + runWithFlicker(splitScreenRotationSetup) { + withTestName { testTag } + repeat { + SplitScreenHelper.TEST_REPETITIONS + } + transitions { + secondaryApp.launchViaIntent() + splitScreenApp.launchViaIntent() + device.launchSplitScreen() + splitScreenApp.reopenAppFromOverview() + setRotation(rotation) + } + assertions { + layersTrace { + navBarLayerRotatesAndScales(Surface.ROTATION_0, rotation, 169271943) + statusBarLayerRotatesScales(Surface.ROTATION_0, rotation, 169271943) + dockedStackDividerIsVisible() + dockedStackPrimaryBoundsIsVisible( + rotation, splitScreenApp.defaultWindowName, 169271943) + dockedStackSecondaryBoundsIsVisible( + rotation, secondaryApp.defaultWindowName, 169271943) + } + windowManagerTrace { + navBarWindowIsAlwaysVisible() + statusBarWindowIsAlwaysVisible() + end { + showsAppWindow(splitScreenApp.defaultWindowName) + .and().showsAppWindow(secondaryApp.defaultWindowName) + } + } + } + } + } + + @Test + fun testRotateAndEnterSplitScreenMode() { + val testTag = "testRotateAndEnterSplitScreenMode" + runWithFlicker(splitScreenRotationSetup) { + withTestName { testTag } + repeat { + SplitScreenHelper.TEST_REPETITIONS + } + transitions { + secondaryApp.launchViaIntent() + splitScreenApp.launchViaIntent() + setRotation(rotation) + device.launchSplitScreen() + splitScreenApp.reopenAppFromOverview() + } + assertions { + layersTrace { + navBarLayerRotatesAndScales(Surface.ROTATION_0, rotation, 169271943) + statusBarLayerRotatesScales(Surface.ROTATION_0, rotation, 169271943) + dockedStackDividerIsVisible() + dockedStackPrimaryBoundsIsVisible( + rotation, splitScreenApp.defaultWindowName, 169271943) + dockedStackSecondaryBoundsIsVisible( + rotation, secondaryApp.defaultWindowName, 169271943) + } + windowManagerTrace { + navBarWindowIsAlwaysVisible() + statusBarWindowIsAlwaysVisible() + end { + showsAppWindow(splitScreenApp.defaultWindowName) + .and().showsAppWindow(secondaryApp.defaultWindowName) + } + } + } + } + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): Collection<Array<Any>> { + val supportedRotations = intArrayOf(Surface.ROTATION_90, Surface.ROTATION_270) + return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) } + } + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenRotateOneLaunchedAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenRotateOneLaunchedAppTest.kt deleted file mode 100644 index d2371bd766f5..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenRotateOneLaunchedAppTest.kt +++ /dev/null @@ -1,95 +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.splitscreen - -import androidx.test.filters.FlakyTest -import android.view.Surface -import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.Flicker -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory -import com.android.server.wm.flicker.helpers.StandardAppHelper -import com.android.server.wm.flicker.startRotation -import com.android.server.wm.flicker.endRotation -import com.android.server.wm.flicker.helpers.buildTestTag -import com.android.server.wm.flicker.helpers.exitSplitScreen -import com.android.server.wm.flicker.helpers.isInSplitScreen -import com.android.server.wm.flicker.helpers.launchSplitScreen -import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -import com.android.server.wm.flicker.repetitions -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test open app to split screen. - * To run this test: `atest WMShellFlickerTests:SplitScreenRotateOneLaunchedAppTest` - */ -@RequiresDevice -@RunWith(Parameterized::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@FlakyTest -class SplitScreenRotateOneLaunchedAppTest( - testName: String, - flickerSpec: Flicker -) : FlickerTestRunner(testName, flickerSpec) { - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - val testApp = StandardAppHelper(instrumentation, - "com.android.wm.shell.flicker.testapp", "SimpleApp") - - return FlickerTestRunnerFactory(instrumentation, repetitions = 3) - .buildTest { configuration -> - withTestName { - buildTestTag("splitScreenRotateOneApp", testApp, configuration) - } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - testApp.open() - device.launchSplitScreen() - device.waitForIdle() - } - eachRun { - this.setRotation(configuration.startRotation) - } - } - teardown { - eachRun { - setRotation(Surface.ROTATION_0) - } - test { - testApp.exit() - if (device.isInSplitScreen()) { - device.exitSplitScreen() - } - } - } - transitions { - this.setRotation(configuration.endRotation) - } - } - } - } -}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenRotateTwoLaunchedAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenRotateTwoLaunchedAppTest.kt deleted file mode 100644 index 67346424acd2..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenRotateTwoLaunchedAppTest.kt +++ /dev/null @@ -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.splitscreen - -import androidx.test.filters.FlakyTest -import android.view.Surface -import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.Flicker -import com.android.server.wm.flicker.FlickerTestRunner -import com.android.server.wm.flicker.FlickerTestRunnerFactory -import com.android.server.wm.flicker.helpers.StandardAppHelper -import com.android.server.wm.flicker.startRotation -import com.android.server.wm.flicker.endRotation -import com.android.server.wm.flicker.helpers.buildTestTag -import com.android.server.wm.flicker.helpers.exitSplitScreen -import com.android.server.wm.flicker.helpers.reopenAppFromOverview -import com.android.server.wm.flicker.helpers.isInSplitScreen -import com.android.server.wm.flicker.helpers.launchSplitScreen -import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -import com.android.server.wm.flicker.repetitions -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test open app to split screen. - * To run this test: `atest WMShellFlickerTests:SplitScreenRotateTwoLaunchedAppTest` - */ -@RequiresDevice -@RunWith(Parameterized::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@FlakyTest -class SplitScreenRotateTwoLaunchedAppTest( - testName: String, - flickerSpec: Flicker -) : FlickerTestRunner(testName, flickerSpec) { - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<Array<Any>> { - val instrumentation = InstrumentationRegistry.getInstrumentation() - val testApp = StandardAppHelper(instrumentation, - "com.android.wm.shell.flicker.testapp", "SimpleApp") - val secondaryApp = StandardAppHelper(instrumentation, - "com.android.wm.shell.flicker.testapp", - "SplitScreenSecondaryApp") - - return FlickerTestRunnerFactory(instrumentation, repetitions = 3) - .buildTest { configuration -> - withTestName { - buildTestTag("splitScreenRotateTwoApps", testApp, configuration) - } - repeat { configuration.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - testApp.open() - device.pressHome() - secondaryApp.open() - device.pressHome() - device.launchSplitScreen() - device.reopenAppFromOverview() - device.waitForIdle() - } - eachRun { - this.setRotation(configuration.startRotation) - } - } - teardown { - eachRun { - setRotation(Surface.ROTATION_0) - } - test { - testApp.exit() - secondaryApp.exit() - if (device.isInSplitScreen()) { - device.exitSplitScreen() - } - } - } - transitions { - this.setRotation(configuration.endRotation) - } - } - } - } -}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenTestBase.kt index a3440df9ddf8..42c509d6eba8 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenTestBase.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenTestBase.kt @@ -17,6 +17,7 @@ package com.android.wm.shell.flicker.splitscreen import com.android.wm.shell.flicker.NonRotationTestBase +import com.android.wm.shell.flicker.TEST_APP_NONRESIZEABLE_LABEL import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_PRIMARY_LABEL import com.android.wm.shell.flicker.TEST_APP_SPLITSCREEN_SECONDARY_LABEL import com.android.wm.shell.flicker.helpers.SplitScreenHelper @@ -32,4 +33,7 @@ abstract class SplitScreenTestBase( protected val secondaryApp = SplitScreenHelper(instrumentation, TEST_APP_SPLITSCREEN_SECONDARY_LABEL, Components.SplitScreenSecondaryActivity()) + protected val nonResizeableApp = SplitScreenHelper(instrumentation, + TEST_APP_NONRESIZEABLE_LABEL, + Components.NonResizeableActivity()) } diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml index a583b725899b..28ed3431db62 100644 --- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml +++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml @@ -86,6 +86,17 @@ </intent-filter> </activity> + <activity android:name=".NonResizeableActivity" + android:resizeableActivity="false" + android:taskAffinity="com.android.wm.shell.flicker.testapp.NonResizeableActivity" + android:label="NonResizeableApp" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + <activity android:name=".SimpleActivity" android:taskAffinity="com.android.wm.shell.flicker.testapp.SimpleActivity" android:label="SimpleApp" diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_non_resizeable.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_non_resizeable.xml new file mode 100644 index 000000000000..45d5917f86d6 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_non_resizeable.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 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. +--> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:background="@android:color/holo_orange_light"> + + <TextView + android:id="@+id/NonResizeableTest" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:gravity="center_vertical|center_horizontal" + android:text="NonResizeableActivity" + android:textAppearance="?android:attr/textAppearanceLarge"/> + +</LinearLayout> diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/Components.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/Components.java index 8e9b4cb2d53e..f729ea554072 100644 --- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/Components.java +++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/Components.java @@ -38,6 +38,13 @@ public class Components { } } + public static class NonResizeableActivity extends ComponentsInfo { + @Override + public String getActivityName() { + return NonResizeableActivity.class.getSimpleName(); + } + } + public static class PipActivity extends ComponentsInfo { // Intent action that this activity dynamically registers to enter picture-in-picture public static final String ACTION_ENTER_PIP = PACKAGE_NAME + ".PipActivity.ENTER_PIP"; diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/NonResizeableActivity.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/NonResizeableActivity.java new file mode 100644 index 000000000000..24275e002c7f --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/NonResizeableActivity.java @@ -0,0 +1,29 @@ +/* + * 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.testapp; + +import android.app.Activity; +import android.os.Bundle; + +public class NonResizeableActivity extends Activity { + + @Override + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + setContentView(R.layout.activity_non_resizeable); + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java index 3ff750af7ec9..862776ec7df2 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java @@ -16,18 +16,14 @@ package com.android.wm.shell; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; -import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN; -import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_LETTERBOX; import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_MULTI_WINDOW; import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -38,7 +34,6 @@ import static org.mockito.Mockito.verify; import android.app.ActivityManager.RunningTaskInfo; import android.content.Context; import android.content.pm.ParceledListSlice; -import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; @@ -47,7 +42,6 @@ import android.window.ITaskOrganizer; import android.window.ITaskOrganizerController; import android.window.TaskAppearedInfo; -import androidx.annotation.Nullable; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -263,28 +257,6 @@ public class ShellTaskOrganizerTests { assertTrue(mwListener.appeared.contains(task2)); } - @Test - public void testTaskInfoToTaskListenerType_whenLetterboxBoundsPassed_returnsLetterboxType() { - RunningTaskInfo taskInfo = createTaskInfo( - /* taskId */ 1, - WINDOWING_MODE_FULLSCREEN, - /* letterboxActivityBounds */ new Rect(1, 1, 1, 1)); - - assertEquals( - ShellTaskOrganizer.taskInfoToTaskListenerType(taskInfo), - TASK_LISTENER_TYPE_LETTERBOX); - } - - @Test - public void testTaskInfoToTaskListenerType_whenLetterboxBoundsIsNull_returnsFullscreenType() { - RunningTaskInfo taskInfo = createTaskInfo( - /* taskId */ 1, WINDOWING_MODE_FULLSCREEN, /* letterboxActivityBounds */ null); - - assertEquals( - ShellTaskOrganizer.taskInfoToTaskListenerType(taskInfo), - TASK_LISTENER_TYPE_FULLSCREEN); - } - private static RunningTaskInfo createTaskInfo(int taskId, int windowingMode) { RunningTaskInfo taskInfo = new RunningTaskInfo(); taskInfo.taskId = taskId; @@ -292,12 +264,4 @@ public class ShellTaskOrganizerTests { return taskInfo; } - private static RunningTaskInfo createTaskInfo( - int taskId, int windowingMode, @Nullable Rect letterboxActivityBounds) { - RunningTaskInfo taskInfo = new RunningTaskInfo(); - taskInfo.taskId = taskId; - taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode); - taskInfo.letterboxActivityBounds = Rect.copyOrNull(letterboxActivityBounds); - return taskInfo; - } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/letterbox/LetterboxConfigControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/letterbox/LetterboxConfigControllerTest.java deleted file mode 100644 index 29233366d4f3..000000000000 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/letterbox/LetterboxConfigControllerTest.java +++ /dev/null @@ -1,131 +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.letterbox; - -import static org.junit.Assert.assertEquals; - -import android.view.Gravity; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import com.android.wm.shell.R; -import com.android.wm.shell.ShellTestCase; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Tests for {@link LetterboxConfigController}. - */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public final class LetterboxConfigControllerTest extends ShellTestCase { - - private LetterboxConfigController mLetterboxConfigController; - - @Before - public void setUp() { - mLetterboxConfigController = new LetterboxConfigController(getContext()); - } - - @Test - public void testGetPortraitGravity_noOverrides_returnConfigValue() { - assertEquals( - mLetterboxConfigController.getPortraitGravity(), - getContext().getResources().getInteger(R.integer.config_letterboxPortraitGravity)); - } - - @Test - public void testGetLandscapeGravity_noOverrides_returnConfigValue() { - assertEquals( - mLetterboxConfigController.getLandscapeGravity(), - getContext().getResources().getInteger(R.integer.config_letterboxLandscapeGravity)); - } - - @Test - public void testSetPortraitGravity_validValue_savesValue() { - mLetterboxConfigController.setPortraitGravity(Gravity.BOTTOM); - assertEquals(mLetterboxConfigController.getPortraitGravity(), Gravity.BOTTOM); - - mLetterboxConfigController.setPortraitGravity(Gravity.CENTER); - assertEquals(mLetterboxConfigController.getPortraitGravity(), Gravity.CENTER); - - mLetterboxConfigController.setPortraitGravity(Gravity.TOP); - assertEquals(mLetterboxConfigController.getPortraitGravity(), Gravity.TOP); - } - - @Test - public void testSetLandscapeGravity_validValue_savesValue() { - mLetterboxConfigController.setLandscapeGravity(Gravity.LEFT); - assertEquals(mLetterboxConfigController.getLandscapeGravity(), Gravity.LEFT); - - mLetterboxConfigController.setLandscapeGravity(Gravity.CENTER); - assertEquals(mLetterboxConfigController.getLandscapeGravity(), Gravity.CENTER); - - mLetterboxConfigController.setLandscapeGravity(Gravity.RIGHT); - assertEquals(mLetterboxConfigController.getLandscapeGravity(), Gravity.RIGHT); - } - - @Test(expected = IllegalArgumentException.class) - public void testSetPortraitGravity_invalidValue_throwsException() { - mLetterboxConfigController.setPortraitGravity(Gravity.RIGHT); - } - - @Test(expected = IllegalArgumentException.class) - public void testSetLandscapeGravity_invalidValue_throwsException() { - mLetterboxConfigController.setLandscapeGravity(Gravity.TOP); - } - - @Test - public void testResetPortraitGravity() { - int defaultGravity = - getContext().getResources().getInteger(R.integer.config_letterboxPortraitGravity); - - mLetterboxConfigController.setPortraitGravity(Gravity.BOTTOM); - mLetterboxConfigController.resetPortraitGravity(); - assertEquals(mLetterboxConfigController.getPortraitGravity(), defaultGravity); - - mLetterboxConfigController.setPortraitGravity(Gravity.CENTER); - mLetterboxConfigController.resetPortraitGravity(); - assertEquals(mLetterboxConfigController.getPortraitGravity(), defaultGravity); - - mLetterboxConfigController.setPortraitGravity(Gravity.TOP); - mLetterboxConfigController.resetPortraitGravity(); - assertEquals(mLetterboxConfigController.getPortraitGravity(), defaultGravity); - } - - @Test - public void testResetLandscapeGravity() { - int defaultGravity = - getContext().getResources().getInteger(R.integer.config_letterboxLandscapeGravity); - - mLetterboxConfigController.setLandscapeGravity(Gravity.RIGHT); - mLetterboxConfigController.resetLandscapeGravity(); - assertEquals(mLetterboxConfigController.getLandscapeGravity(), defaultGravity); - - mLetterboxConfigController.setLandscapeGravity(Gravity.CENTER); - mLetterboxConfigController.resetLandscapeGravity(); - assertEquals(mLetterboxConfigController.getLandscapeGravity(), defaultGravity); - - mLetterboxConfigController.setLandscapeGravity(Gravity.LEFT); - mLetterboxConfigController.resetLandscapeGravity(); - assertEquals(mLetterboxConfigController.getLandscapeGravity(), defaultGravity); - } - -} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/letterbox/LetterboxTaskListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/letterbox/LetterboxTaskListenerTest.java deleted file mode 100644 index 5cbc7d927d61..000000000000 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/letterbox/LetterboxTaskListenerTest.java +++ /dev/null @@ -1,367 +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.letterbox; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; - -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; - -import android.app.ActivityManager.RunningTaskInfo; -import android.graphics.Insets; -import android.graphics.Rect; -import android.os.Handler; -import android.os.Looper; -import android.view.Gravity; -import android.view.SurfaceControl; -import android.view.WindowInsets; -import android.view.WindowManager; -import android.view.WindowMetrics; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import com.android.wm.shell.ShellTestCase; -import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.common.TransactionPool; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -/** - * Tests for {@link LetterboxTaskListener}. - */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public final class LetterboxTaskListenerTest extends ShellTestCase { - - @Mock private SurfaceControl mLeash; - @Mock private SurfaceControl.Transaction mTransaction; - @Mock private WindowManager mWindowManager; - @Mock private WindowMetrics mWindowMetrics; - @Mock private WindowInsets mWindowInsets; - private LetterboxTaskListener mLetterboxTaskListener; - private LetterboxConfigController mLetterboxConfigController; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - mLetterboxConfigController = new LetterboxConfigController(getContext()); - mLetterboxTaskListener = new LetterboxTaskListener( - new SyncTransactionQueue( - new TransactionPool() { - @Override - public SurfaceControl.Transaction acquire() { - return mTransaction; - } - - @Override - public void release(SurfaceControl.Transaction t) { - } - }, - new Handler(Looper.getMainLooper())), - mLetterboxConfigController, - mWindowManager); - - when(mWindowManager.getMaximumWindowMetrics()).thenReturn(mWindowMetrics); - when(mWindowMetrics.getWindowInsets()).thenReturn(mWindowInsets); - } - - @Test - public void testOnTaskInfoChanged_updatesPositionAndCrop() { - setWindowBoundsAndInsets( - /* windowBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds - Insets.NONE); - - mLetterboxConfigController.setLandscapeGravity(Gravity.CENTER); - mLetterboxTaskListener.onTaskAppeared( - createTaskInfo( - /* taskId */ 1, - /* maxBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds - /* parentBounds */ new Rect(0, 0, 200, 100), - /* activityBounds */ new Rect(75, 0, 125, 75), - /* taskBounds */ new Rect(50, 0, 125, 100), - /* activityInsets */ new Rect(0, 0, 0, 0)), - mLeash); - - // Task doesn't need to repositioned - verifySetPosition(50, 0); - // Should return activity coordinates offset by task coordinates - verifySetWindowCrop(new Rect(25, 0, 75, 75)); - - mLetterboxTaskListener.onTaskInfoChanged( - createTaskInfo( - /* taskId */ 1, - /* maxBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds - /* parentBounds */ new Rect(0, 0, 200, 100), - // Activity is offset by 25 to the left - /* activityBounds */ new Rect(50, 0, 100, 75), - /* taskBounds */ new Rect(50, 0, 125, 100), - /* activityInsets */ new Rect(0, 0, 0, 0))); - - // Task needs to be repositioned by 25 to the left - verifySetPosition(75, 0); - // Should return activity coordinates offset by task coordinates - verifySetWindowCrop(new Rect(0, 0, 50, 75)); - } - - @Test - public void testOnTaskInfoAppeared_landscapeWithLeftGravity() { - mLetterboxConfigController.setLandscapeGravity(Gravity.LEFT); - setWindowBoundsAndInsets( - /* windowBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds - Insets.of(/* left= */ 10, /* top= */ 10, /* right= */ 10, /* bottom= */ 10)); - - mLetterboxTaskListener.onTaskAppeared( - createTaskInfo( - /* taskId */ 1, - /* maxBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds - /* parentBounds */ new Rect(0, 0, 200, 100), - /* activityBounds */ new Rect(150, 0, 200, 75), - /* taskBounds */ new Rect(125, 0, 200, 100), - /* activityInsets */ new Rect(0, 10, 10, 0)), - mLeash); - - verifySetPosition(-15, 0); - // Should return activity coordinates offset by task coordinates minus unwanted right inset - verifySetWindowCrop(new Rect(25, 0, 65, 75)); - } - - @Test - public void testOnTaskInfoAppeared_landscapeWithCenterGravity() { - mLetterboxConfigController.setLandscapeGravity(Gravity.CENTER); - setWindowBoundsAndInsets( - /* windowBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds - Insets.of(/* left= */ 10, /* top= */ 10, /* right= */ 10, /* bottom= */ 10)); - - mLetterboxTaskListener.onTaskAppeared( - createTaskInfo( - /* taskId */ 1, - /* maxBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds - /* parentBounds */ new Rect(0, 0, 200, 100), - /* activityBounds */ new Rect(150, 0, 200, 75), - /* taskBounds */ new Rect(125, 0, 200, 100), - /* activityInsets */ new Rect(0, 10, 10, 0)), - mLeash); - - verifySetPosition(55, 0); - // Should return activity coordinates offset by task coordinates minus unwanted right inset - verifySetWindowCrop(new Rect(25, 0, 65, 75)); - } - - @Test - public void testOnTaskInfoAppeared_landscapeWithRightGravity() { - mLetterboxConfigController.setLandscapeGravity(Gravity.RIGHT); - setWindowBoundsAndInsets( - /* windowBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds - Insets.of(/* left= */ 10, /* top= */ 10, /* right= */ 10, /* bottom= */ 10)); - - mLetterboxTaskListener.onTaskAppeared( - createTaskInfo( - /* taskId */ 1, - /* maxBounds= */ new Rect(0, 0, 200, 100), // equal to parent bounds - /* parentBounds */ new Rect(0, 0, 200, 100), - /* activityBounds */ new Rect(50, 0, 100, 75), - /* taskBounds */ new Rect(25, 0, 100, 100), - /* activityInsets */ new Rect(0, 10, 10, 0)), - mLeash); - - verifySetPosition(115, 0); - // Should return activity coordinates offset by task coordinates - verifySetWindowCrop(new Rect(25, 0, 75, 75)); - } - - @Test - public void testOnTaskInfoAppeared_portraitWithTopGravity() { - mLetterboxConfigController.setPortraitGravity(Gravity.TOP); - setWindowBoundsAndInsets( - /* windowBounds= */ new Rect(0, 0, 100, 150), // equal to parent bounds - Insets.of(/* left= */ 10, /* top= */ 10, /* right= */ 10, /* bottom= */ 20)); - - mLetterboxTaskListener.onTaskAppeared( - createTaskInfo( - /* taskId */ 1, - /* maxBounds= */ new Rect(0, 0, 100, 150), // equal to parent bounds - /* parentBounds */ new Rect(0, 0, 100, 150), - /* activityBounds */ new Rect(0, 75, 50, 125), - /* taskBounds */ new Rect(0, 50, 100, 125), - /* activityInsets */ new Rect(10, 0, 0, 0)), - mLeash); - - verifySetPosition(20, -15); - // Should return activity coordinates offset by task coordinates minus unwanted left inset - verifySetWindowCrop(new Rect(10, 25, 50, 75)); - } - - @Test - public void testOnTaskInfoAppeared_portraitWithCenterGravity() { - mLetterboxConfigController.setPortraitGravity(Gravity.CENTER); - setWindowBoundsAndInsets( - /* windowBounds= */ new Rect(0, 0, 100, 150), // equal to parent bounds - Insets.of(/* left= */ 10, /* top= */ 10, /* right= */ 10, /* bottom= */ 20)); - - mLetterboxTaskListener.onTaskAppeared( - createTaskInfo( - /* taskId */ 1, - /* maxBounds= */ new Rect(0, 0, 100, 150), // equal to parent bounds - /* parentBounds */ new Rect(0, 0, 100, 150), - /* activityBounds */ new Rect(0, 75, 50, 125), - /* taskBounds */ new Rect(0, 50, 100, 125), - /* activityInsets */ new Rect(10, 0, 0, 0)), - mLeash); - - verifySetPosition(20, 20); - // Should return activity coordinates offset by task coordinates minus unwanted left inset - verifySetWindowCrop(new Rect(10, 25, 50, 75)); - } - - @Test - public void testOnTaskInfoAppeared_portraitWithCenterGravity_visibleLeftInset() { - mLetterboxConfigController.setPortraitGravity(Gravity.CENTER); - setWindowBoundsAndInsets( - /* windowBounds= */ new Rect(0, 0, 100, 150), // equal to parent bounds - Insets.of(/* left= */ 10, /* top= */ 10, /* right= */ 10, /* bottom= */ 20)); - - mLetterboxTaskListener.onTaskAppeared( - createTaskInfo( - /* taskId */ 1, - /* maxBounds= */ new Rect(0, 0, 100, 150), // equal to parent bounds - /* parentBounds */ new Rect(0, 0, 100, 150), - /* activityBounds */ new Rect(0, 75, 50, 125), - /* taskBounds */ new Rect(0, 50, 100, 125), - // Activity is drawn under the left inset. - /* activityInsets */ new Rect(0, 0, 0, 0)), - mLeash); - - verifySetPosition(20, 20); - // Should return activity coordinates offset by task coordinates - verifySetWindowCrop(new Rect(0, 25, 50, 75)); - } - - @Test - public void testOnTaskInfoAppeared_portraitWithBottomGravity() { - mLetterboxConfigController.setPortraitGravity(Gravity.BOTTOM); - setWindowBoundsAndInsets( - /* windowBounds= */ new Rect(0, 0, 100, 150), // equal to parent bounds - Insets.of(/* left= */ 10, /* top= */ 10, /* right= */ 10, /* bottom= */ 20)); - - mLetterboxTaskListener.onTaskAppeared( - createTaskInfo( - /* taskId */ 1, - /* maxBounds= */ new Rect(0, 0, 100, 150), // equal to parent bounds - /* parentBounds */ new Rect(0, 0, 100, 150), - /* activityBounds */ new Rect(0, 75, 50, 125), - /* taskBounds */ new Rect(0, 50, 100, 125), - /* activityInsets */ new Rect(10, 0, 0, 0)), - mLeash); - - verifySetPosition(20, 55); - // Should return activity coordinates offset by task coordinates minus unwanted left inset - verifySetWindowCrop(new Rect(10, 25, 50, 75)); - } - - @Test - public void testOnTaskInfoAppeared_partlyOverlapsWithAllInsets() { - mLetterboxConfigController.setPortraitGravity(Gravity.TOP); - setWindowBoundsAndInsets( - /* windowBounds= */ new Rect(0, 0, 200, 125), // equal to parent bounds - Insets.of(/* left= */ 25, /* top= */ 25, /* right= */ 35, /* bottom= */ 15)); - - mLetterboxTaskListener.onTaskAppeared( - createTaskInfo( - /* taskId */ 1, - /* maxBounds= */ new Rect(0, 0, 200, 125), // equal to parent bounds - /* parentBounds */ new Rect(0, 0, 200, 125), - /* activityBounds */ new Rect(15, 0, 175, 120), - /* taskBounds */ new Rect(0, 0, 200, 125), - /* activityInsets */ new Rect(10, 25, 10, 10)), // equal to parent bounds - mLeash); - - // Activity fully covers parent bounds with insets so doesn't need to be moved. - verifySetPosition(0, 0); - // Should return activity coordinates offset by task coordinates - verifySetWindowCrop(new Rect(15, 0, 175, 120)); - } - - @Test - public void testOnTaskInfoAppeared_parentShiftedLikeInOneHandedMode() { - mLetterboxConfigController.setPortraitGravity(Gravity.TOP); - setWindowBoundsAndInsets( - /* windowBounds= */ new Rect(0, 0, 100, 150), - Insets.of(/* left= */ 0, /* top= */ 10, /* right= */ 0, /* bottom= */ 0)); - - mLetterboxTaskListener.onTaskAppeared( - createTaskInfo( - /* taskId */ 1, - /* maxBounds= */ new Rect(0, 0, 100, 150), - /* parentBounds */ new Rect(0, 75, 100, 225), - /* activityBounds */ new Rect(25, 75, 75, 125), - /* taskBounds */ new Rect(0, 75, 100, 125), - /* activityInsets */ new Rect(10, 0, 0, 0)), - mLeash); - - verifySetPosition(0, 0); - verifySetWindowCrop(new Rect(25, 0, 75, 50)); - } - - @Test(expected = IllegalStateException.class) - public void testOnTaskAppeared_calledSecondTimeWithSameTaskId_throwsException() { - setWindowBoundsAndInsets(new Rect(), Insets.NONE); - RunningTaskInfo taskInfo = - createTaskInfo(/* taskId */ 1, new Rect(), new Rect(), new Rect(), new Rect(), - new Rect()); - mLetterboxTaskListener.onTaskAppeared(taskInfo, mLeash); - mLetterboxTaskListener.onTaskAppeared(taskInfo, mLeash); - } - - private void setWindowBoundsAndInsets(Rect windowBounds, Insets insets) { - when(mWindowMetrics.getBounds()).thenReturn(windowBounds); - when(mWindowInsets.getInsets(anyInt())).thenReturn(insets); - } - - private void verifySetPosition(int x, int y) { - verify(mTransaction).setPosition(eq(mLeash), eq((float) x), eq((float) y)); - } - - private void verifySetWindowCrop(final Rect crop) { - // Should return activty coordinates offset by task coordinates - verify(mTransaction).setWindowCrop(eq(mLeash), eq(crop)); - } - - private static RunningTaskInfo createTaskInfo( - int taskId, - final Rect maxBounds, - final Rect parentBounds, - final Rect activityBounds, - final Rect taskBounds, - final Rect activityInsets) { - RunningTaskInfo taskInfo = new RunningTaskInfo(); - taskInfo.taskId = taskId; - taskInfo.configuration.windowConfiguration.setMaxBounds(maxBounds); - taskInfo.parentBounds = parentBounds; - taskInfo.configuration.windowConfiguration.setBounds(taskBounds); - taskInfo.letterboxActivityBounds = Rect.copyOrNull(activityBounds); - taskInfo.letterboxActivityInsets = Rect.copyOrNull(activityInsets); - - return taskInfo; - } -} |