From 6dd79270b083c8a47f17f0b43ee44293e5b1235e Mon Sep 17 00:00:00 2001 From: Mariia Sandrikova Date: Wed, 8 Dec 2021 14:13:53 +0000 Subject: [PATCH] [1/n] Camera Compat UI: Refactor size compat mode UI controller. First CL in the chain that will add a camera compat UI control which will be used to correct stretched issues in TextureView and SurfaceView cause by apps not handling mismatch between camera buffers and view size correctly. This change is necessary because right now there are 2 instances of WindowlessWindowManager (SizeCompatUIWindowManager) in SizeCompatUILayout: for a hint and for a restart button. They are also positioned relatively by computing offsets in java code rather than by specifying layout with XML. This code structure will cause issues since with upcoming camera changes there will be a need to coordinate relative position of 4 UI elements (2 buttons and 2 hints). What is changing in this CL: - Majority of changes are simple renaming from "size compat UI" to "compat UI" since, starting from the next change, two different compat controls will be supported. - Merging 2 instances of SizeCompatUIWindowManager into one (CompatUIWindowManager) and for that create a joint layout in compat_ui_layout.xml - Merge SizeCompatLayout logic into CompatUIWindowManager since in absence of 2 instances of WindowlessWindowManager there is no good reason to keep them separate. - Merging and renaming tests to follow refactoring of the implementation. Test: atest WMShellUnitTests (the same set of tests should be sufficient since this refactoring just reorganized the structure of the implementation) Bug: 206602997 Change-Id: I7141c69b7ba4742c5b7b2018329b91a1b37b45bc --- ...hint_bubble.xml => compat_hint_bubble.xml} | 4 +- ...t_hint_point.xml => compat_hint_point.xml} | 4 +- .../drawable/size_compat_restart_button.xml | 6 +- .../Shell/res/layout/compat_mode_hint.xml | 46 +++ ...ize_compat_ui.xml => compat_ui_layout.xml} | 11 +- .../res/layout/size_compat_mode_hint.xml | 58 --- .../WindowManager/Shell/res/values/colors.xml | 6 +- libs/WindowManager/Shell/res/values/dimen.xml | 12 +- .../android/wm/shell/ShellTaskOrganizer.java | 56 +-- .../CompatUI.java} | 8 +- .../CompatUIController.java} | 79 ++-- .../wm/shell/compatui/CompatUILayout.java | 89 +++++ .../shell/compatui/CompatUIWindowManager.java | 321 ++++++++++++++++ .../wm/shell/dagger/WMShellBaseModule.java | 16 +- .../wm/shell/pip/PipTaskOrganizer.java | 4 +- .../sizecompatui/SizeCompatHintPopup.java | 66 ---- .../sizecompatui/SizeCompatRestartButton.java | 76 ---- .../sizecompatui/SizeCompatUILayout.java | 344 ------------------ .../SizeCompatUIWindowManager.java | 127 ------- .../wm/shell/ShellTaskOrganizerTests.java | 20 +- .../CompatUIControllerTest.java} | 48 +-- .../CompatUILayoutTest.java} | 58 +-- .../compatui/CompatUIWindowManagerTest.java | 253 +++++++++++++ .../sizecompatui/SizeCompatHintPopupTest.java | 85 ----- .../sizecompatui/SizeCompatUILayoutTest.java | 284 --------------- .../com/android/systemui/SystemUIFactory.java | 4 +- .../systemui/dagger/SysUIComponent.java | 4 +- .../android/systemui/dagger/WMComponent.java | 4 +- .../com/android/systemui/wmshell/WMShell.java | 18 +- .../android/systemui/wmshell/WMShellTest.java | 10 +- 30 files changed, 908 insertions(+), 1213 deletions(-) rename libs/WindowManager/Shell/res/drawable/{size_compat_hint_bubble.xml => compat_hint_bubble.xml} (84%) rename libs/WindowManager/Shell/res/drawable/{size_compat_hint_point.xml => compat_hint_point.xml} (88%) create mode 100644 libs/WindowManager/Shell/res/layout/compat_mode_hint.xml rename libs/WindowManager/Shell/res/layout/{size_compat_ui.xml => compat_ui_layout.xml} (82%) delete mode 100644 libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml rename libs/WindowManager/Shell/src/com/android/wm/shell/{sizecompatui/SizeCompatUI.java => compatui/CompatUI.java} (87%) rename libs/WindowManager/Shell/src/com/android/wm/shell/{sizecompatui/SizeCompatUIController.java => compatui/CompatUIController.java} (81%) create mode 100644 libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java create mode 100644 libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java delete mode 100644 libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java delete mode 100644 libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java delete mode 100644 libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java delete mode 100644 libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java rename libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/{sizecompatui/SizeCompatUIControllerTest.java => compatui/CompatUIControllerTest.java} (83%) rename libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/{sizecompatui/SizeCompatRestartButtonTest.java => compatui/CompatUILayoutTest.java} (50%) create mode 100644 libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java delete mode 100644 libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java delete mode 100644 libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml b/libs/WindowManager/Shell/res/drawable/compat_hint_bubble.xml similarity index 84% rename from libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml rename to libs/WindowManager/Shell/res/drawable/compat_hint_bubble.xml index 22cd384e1be06..26848b13a1bcf 100644 --- a/libs/WindowManager/Shell/res/drawable/size_compat_hint_bubble.xml +++ b/libs/WindowManager/Shell/res/drawable/compat_hint_bubble.xml @@ -16,6 +16,6 @@ --> - - + + \ No newline at end of file diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml b/libs/WindowManager/Shell/res/drawable/compat_hint_point.xml similarity index 88% rename from libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml rename to libs/WindowManager/Shell/res/drawable/compat_hint_point.xml index af9063a94afb8..0e0ca37aaf254 100644 --- a/libs/WindowManager/Shell/res/drawable/size_compat_hint_point.xml +++ b/libs/WindowManager/Shell/res/drawable/compat_hint_point.xml @@ -15,11 +15,11 @@ ~ limitations under the License. --> diff --git a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml index 18caa3582537f..ab74e43472c34 100644 --- a/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml +++ b/libs/WindowManager/Shell/res/drawable/size_compat_restart_button.xml @@ -20,16 +20,16 @@ android:viewportWidth="48" android:viewportHeight="48"> diff --git a/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml b/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml new file mode 100644 index 0000000000000..c04e258ea784f --- /dev/null +++ b/libs/WindowManager/Shell/res/layout/compat_mode_hint.xml @@ -0,0 +1,46 @@ + + + + + + + + + diff --git a/libs/WindowManager/Shell/res/layout/size_compat_ui.xml b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml similarity index 82% rename from libs/WindowManager/Shell/res/layout/size_compat_ui.xml rename to libs/WindowManager/Shell/res/layout/compat_ui_layout.xml index 82ebee263a645..6f946b25eaa56 100644 --- a/libs/WindowManager/Shell/res/layout/size_compat_ui.xml +++ b/libs/WindowManager/Shell/res/layout/compat_ui_layout.xml @@ -14,10 +14,15 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - + android:layout_height="wrap_content" + android:orientation="vertical" + android:gravity="bottom|end"> + + - + diff --git a/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml b/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml deleted file mode 100644 index d0e7c42dbf8bd..0000000000000 --- a/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml index 23a21724e43d0..cf596f7d15dcc 100644 --- a/libs/WindowManager/Shell/res/values/colors.xml +++ b/libs/WindowManager/Shell/res/values/colors.xml @@ -30,9 +30,9 @@ @color/GM2_grey_800 @color/GM2_grey_700 - - @android:color/system_neutral1_800 - @android:color/system_neutral1_50 + + @android:color/system_neutral1_800 + @android:color/system_neutral1_50 #E8EAED diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index 9e77578eafd8c..18e91f41a698d 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -206,11 +206,15 @@ 64dp - - 28dp + + 28dp - - 10dp + + 10dp + + + 7dp 200dp 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 8e98b82088dc5..8b3a35688f110 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -52,8 +52,8 @@ import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.FrameworkStatsLog; import com.android.wm.shell.common.ScreenshotUtils; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.compatui.CompatUIController; import com.android.wm.shell.recents.RecentTasksController; -import com.android.wm.shell.sizecompatui.SizeCompatUIController; import com.android.wm.shell.startingsurface.StartingWindowController; import java.io.PrintWriter; @@ -69,7 +69,7 @@ import java.util.function.Consumer; * TODO(b/167582004): may consider consolidating this class and TaskOrganizer */ public class ShellTaskOrganizer extends TaskOrganizer implements - SizeCompatUIController.SizeCompatUICallback { + CompatUIController.CompatUICallback { // Intentionally using negative numbers here so the positive numbers can be used // for task id specific listeners that will be added later. @@ -98,9 +98,9 @@ public class ShellTaskOrganizer extends TaskOrganizer implements default void onTaskInfoChanged(RunningTaskInfo taskInfo) {} default void onTaskVanished(RunningTaskInfo taskInfo) {} default void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {} - /** Whether this task listener supports size compat UI. */ - default boolean supportSizeCompatUI() { - // All TaskListeners should support size compat except PIP. + /** Whether this task listener supports compat UI. */ + default boolean supportCompatUI() { + // All TaskListeners should support compat UI except PIP. return true; } /** Attaches the a child window surface to the task surface. */ @@ -159,11 +159,11 @@ public class ShellTaskOrganizer extends TaskOrganizer implements private StartingWindowController mStartingWindow; /** - * In charge of showing size compat UI. Can be {@code null} if device doesn't support size + * In charge of showing compat UI. Can be {@code null} if device doesn't support size * compat. */ @Nullable - private final SizeCompatUIController mSizeCompatUI; + private final CompatUIController mCompatUI; @Nullable private final Optional mRecentTasks; @@ -172,32 +172,32 @@ public class ShellTaskOrganizer extends TaskOrganizer implements private RunningTaskInfo mLastFocusedTaskInfo; public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context) { - this(null /* taskOrganizerController */, mainExecutor, context, null /* sizeCompatUI */, + this(null /* taskOrganizerController */, mainExecutor, context, null /* compatUI */, Optional.empty() /* recentTasksController */); } public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable - SizeCompatUIController sizeCompatUI) { - this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI, + CompatUIController compatUI) { + this(null /* taskOrganizerController */, mainExecutor, context, compatUI, Optional.empty() /* recentTasksController */); } public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable - SizeCompatUIController sizeCompatUI, + CompatUIController compatUI, Optional recentTasks) { - this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI, + this(null /* taskOrganizerController */, mainExecutor, context, compatUI, recentTasks); } @VisibleForTesting ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController, ShellExecutor mainExecutor, - Context context, @Nullable SizeCompatUIController sizeCompatUI, + Context context, @Nullable CompatUIController compatUI, Optional recentTasks) { super(taskOrganizerController, mainExecutor); - mSizeCompatUI = sizeCompatUI; + mCompatUI = compatUI; mRecentTasks = recentTasks; - if (sizeCompatUI != null) { - sizeCompatUI.setSizeCompatUICallback(this); + if (compatUI != null) { + compatUI.setCompatUICallback(this); } } @@ -428,7 +428,7 @@ public class ShellTaskOrganizer extends TaskOrganizer implements listener.onTaskAppeared(info.getTaskInfo(), info.getLeash()); } notifyLocusVisibilityIfNeeded(info.getTaskInfo()); - notifySizeCompatUI(info.getTaskInfo(), listener); + notifyCompatUI(info.getTaskInfo(), listener); } /** @@ -459,8 +459,8 @@ public class ShellTaskOrganizer extends TaskOrganizer implements } notifyLocusVisibilityIfNeeded(taskInfo); if (updated || !taskInfo.equalsForSizeCompat(data.getTaskInfo())) { - // Notify the size compat UI if the listener or task info changed. - notifySizeCompatUI(taskInfo, newListener); + // Notify the compat UI if the listener or task info changed. + notifyCompatUI(taskInfo, newListener); } if (data.getTaskInfo().getWindowingMode() != taskInfo.getWindowingMode()) { // Notify the recent tasks when a task changes windowing modes @@ -504,8 +504,8 @@ public class ShellTaskOrganizer extends TaskOrganizer implements listener.onTaskVanished(taskInfo); } notifyLocusVisibilityIfNeeded(taskInfo); - // Pass null for listener to remove the size compat UI on this task if there is any. - notifySizeCompatUI(taskInfo, null /* taskListener */); + // Pass null for listener to remove the compat UI on this task if there is any. + notifyCompatUI(taskInfo, null /* taskListener */); // Notify the recent tasks that a task has been removed mRecentTasks.ifPresent(recentTasks -> recentTasks.onTaskRemoved(taskInfo)); } @@ -618,28 +618,28 @@ public class ShellTaskOrganizer extends TaskOrganizer implements } /** - * Notifies {@link SizeCompatUIController} about the size compat info changed on the give Task + * Notifies {@link CompatUIController} about the compat info changed on the give Task * to update the UI accordingly. * * @param taskInfo the new Task info * @param taskListener listener to handle the Task Surface placement. {@code null} if task is * vanished. */ - private void notifySizeCompatUI(RunningTaskInfo taskInfo, @Nullable TaskListener taskListener) { - if (mSizeCompatUI == null) { + private void notifyCompatUI(RunningTaskInfo taskInfo, @Nullable TaskListener taskListener) { + if (mCompatUI == null) { return; } - // The task is vanished or doesn't support size compat UI, notify to remove size compat UI + // The task is vanished or doesn't support compat UI, notify to remove compat UI // on this Task if there is any. - if (taskListener == null || !taskListener.supportSizeCompatUI() + if (taskListener == null || !taskListener.supportCompatUI() || !taskInfo.topActivityInSizeCompat || !taskInfo.isVisible) { - mSizeCompatUI.onSizeCompatInfoChanged(taskInfo.displayId, taskInfo.taskId, + mCompatUI.onCompatInfoChanged(taskInfo.displayId, taskInfo.taskId, null /* taskConfig */, null /* taskListener */); return; } - mSizeCompatUI.onSizeCompatInfoChanged(taskInfo.displayId, taskInfo.taskId, + mCompatUI.onCompatInfoChanged(taskInfo.displayId, taskInfo.taskId, taskInfo.configuration, taskListener); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java similarity index 87% rename from libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java rename to libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java index a703114194a0e..99dbfe01964cd 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUI.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java @@ -14,17 +14,17 @@ * limitations under the License. */ -package com.android.wm.shell.sizecompatui; +package com.android.wm.shell.compatui; import com.android.wm.shell.common.annotations.ExternalThread; /** - * Interface to engage size compat UI. + * Interface to engage compat UI. */ @ExternalThread -public interface SizeCompatUI { +public interface CompatUI { /** - * Called when the keyguard occluded state changes. Removes all size compat UIs if the + * Called when the keyguard occluded state changes. Removes all compat UIs if the * keyguard is now occluded. * @param occluded indicates if the keyguard is now occluded. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java similarity index 81% rename from libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java rename to libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java index e06070ab12e5e..e0b23873a980b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.sizecompatui; +package com.android.wm.shell.compatui; import android.annotation.Nullable; import android.content.Context; @@ -48,20 +48,20 @@ import java.util.function.Predicate; /** * Controls to show/update restart-activity buttons on Tasks based on whether the foreground - * activities are in size compatibility mode. + * activities are in compatibility mode. */ -public class SizeCompatUIController implements OnDisplaysChangedListener, +public class CompatUIController implements OnDisplaysChangedListener, DisplayImeController.ImePositionProcessor { /** Callback for size compat UI interaction. */ - public interface SizeCompatUICallback { + public interface CompatUICallback { /** Called when the size compat restart button appears. */ void onSizeCompatRestartButtonAppeared(int taskId); /** Called when the size compat restart button is clicked. */ void onSizeCompatRestartButtonClicked(int taskId); } - private static final String TAG = "SizeCompatUIController"; + private static final String TAG = "CompatUIController"; /** Whether the IME is shown on display id. */ private final Set mDisplaysWithIme = new ArraySet<>(1); @@ -71,7 +71,7 @@ public class SizeCompatUIController implements OnDisplaysChangedListener, new SparseArray<>(0); /** The showing UIs by task id. */ - private final SparseArray mActiveLayouts = new SparseArray<>(0); + private final SparseArray mActiveLayouts = new SparseArray<>(0); /** Avoid creating display context frequently for non-default display. */ private final SparseArray> mDisplayContextCache = new SparseArray<>(0); @@ -82,17 +82,17 @@ public class SizeCompatUIController implements OnDisplaysChangedListener, private final DisplayImeController mImeController; private final SyncTransactionQueue mSyncQueue; private final ShellExecutor mMainExecutor; - private final SizeCompatUIImpl mImpl = new SizeCompatUIImpl(); + private final CompatUIImpl mImpl = new CompatUIImpl(); - private SizeCompatUICallback mCallback; + private CompatUICallback mCallback; /** Only show once automatically in the process life. */ private boolean mHasShownHint; - /** Indicates if the keyguard is currently occluded, in which case size compat UIs shouldn't + /** Indicates if the keyguard is currently occluded, in which case compat UIs shouldn't * be shown. */ private boolean mKeyguardOccluded; - public SizeCompatUIController(Context context, + public CompatUIController(Context context, DisplayController displayController, DisplayInsetsController displayInsetsController, DisplayImeController imeController, @@ -108,35 +108,36 @@ public class SizeCompatUIController implements OnDisplaysChangedListener, mImeController.addPositionProcessor(this); } - public SizeCompatUI asSizeCompatUI() { + /** Returns implementation of {@link CompatUI}. */ + public CompatUI asCompatUI() { return mImpl; } /** Sets the callback for UI interactions. */ - public void setSizeCompatUICallback(SizeCompatUICallback callback) { + public void setCompatUICallback(CompatUICallback callback) { mCallback = callback; } /** - * Called when the Task info changed. Creates and updates the size compat UI if there is an + * Called when the Task info changed. Creates and updates the compat UI if there is an * activity in size compat, or removes the UI if there is no size compat activity. * * @param displayId display the task and activity are in. * @param taskId task the activity is in. - * @param taskConfig task config to place the size compat UI with. + * @param taskConfig task config to place the compat UI with. * @param taskListener listener to handle the Task Surface placement. */ - public void onSizeCompatInfoChanged(int displayId, int taskId, + public void onCompatInfoChanged(int displayId, int taskId, @Nullable Configuration taskConfig, @Nullable ShellTaskOrganizer.TaskListener taskListener) { if (taskConfig == null || taskListener == null) { - // Null token means the current foreground activity is not in size compatibility mode. + // Null token means the current foreground activity is not in compatibility mode. removeLayout(taskId); } else if (mActiveLayouts.contains(taskId)) { // UI already exists, update the UI layout. updateLayout(taskId, taskConfig, taskListener); } else { - // Create a new size compat UI. + // Create a new compat UI. createLayout(displayId, taskId, taskConfig, taskListener); } } @@ -151,7 +152,7 @@ public class SizeCompatUIController implements OnDisplaysChangedListener, mDisplayContextCache.remove(displayId); removeOnInsetsChangedListener(displayId); - // Remove all size compat UIs on the removed display. + // Remove all compat UIs on the removed display. final List toRemoveTaskIds = new ArrayList<>(); forAllLayoutsOnDisplay(displayId, layout -> toRemoveTaskIds.add(layout.getTaskId())); for (int i = toRemoveTaskIds.size() - 1; i >= 0; i--) { @@ -194,7 +195,7 @@ public class SizeCompatUIController implements OnDisplaysChangedListener, mDisplaysWithIme.remove(displayId); } - // Hide the size compat UIs when input method is showing. + // Hide the compat UIs when input method is showing. forAllLayoutsOnDisplay(displayId, layout -> layout.updateVisibility(showOnDisplay(displayId))); } @@ -202,7 +203,7 @@ public class SizeCompatUIController implements OnDisplaysChangedListener, @VisibleForTesting void onKeyguardOccludedChanged(boolean occluded) { mKeyguardOccluded = occluded; - // Hide the size compat UIs when keyguard is occluded. + // Hide the compat UIs when keyguard is occluded. forAllLayouts(layout -> layout.updateVisibility(showOnDisplay(layout.getDisplayId()))); } @@ -222,34 +223,34 @@ public class SizeCompatUIController implements OnDisplaysChangedListener, return; } - final SizeCompatUILayout layout = createLayout(context, displayId, taskId, taskConfig, - taskListener); - mActiveLayouts.put(taskId, layout); - layout.createSizeCompatButton(showOnDisplay(displayId)); + final CompatUIWindowManager compatUIWindowManager = + createLayout(context, displayId, taskId, taskConfig, taskListener); + mActiveLayouts.put(taskId, compatUIWindowManager); + compatUIWindowManager.createLayout(showOnDisplay(displayId)); } @VisibleForTesting - SizeCompatUILayout createLayout(Context context, int displayId, int taskId, + CompatUIWindowManager createLayout(Context context, int displayId, int taskId, Configuration taskConfig, ShellTaskOrganizer.TaskListener taskListener) { - final SizeCompatUILayout layout = new SizeCompatUILayout(mSyncQueue, mCallback, context, - taskConfig, taskId, taskListener, mDisplayController.getDisplayLayout(displayId), - mHasShownHint); + final CompatUIWindowManager compatUIWindowManager = new CompatUIWindowManager(context, + taskConfig, mSyncQueue, mCallback, taskId, taskListener, + mDisplayController.getDisplayLayout(displayId), mHasShownHint); // Only show hint for the first time. mHasShownHint = true; - return layout; + return compatUIWindowManager; } private void updateLayout(int taskId, Configuration taskConfig, ShellTaskOrganizer.TaskListener taskListener) { - final SizeCompatUILayout layout = mActiveLayouts.get(taskId); + final CompatUIWindowManager layout = mActiveLayouts.get(taskId); if (layout == null) { return; } - layout.updateSizeCompatInfo(taskConfig, taskListener, showOnDisplay(layout.getDisplayId())); + layout.updateCompatInfo(taskConfig, taskListener, showOnDisplay(layout.getDisplayId())); } private void removeLayout(int taskId) { - final SizeCompatUILayout layout = mActiveLayouts.get(taskId); + final CompatUIWindowManager layout = mActiveLayouts.get(taskId); if (layout != null) { layout.release(); mActiveLayouts.remove(taskId); @@ -275,19 +276,19 @@ public class SizeCompatUIController implements OnDisplaysChangedListener, return context; } - private void forAllLayoutsOnDisplay(int displayId, Consumer callback) { + private void forAllLayoutsOnDisplay(int displayId, Consumer callback) { forAllLayouts(layout -> layout.getDisplayId() == displayId, callback); } - private void forAllLayouts(Consumer callback) { + private void forAllLayouts(Consumer callback) { forAllLayouts(layout -> true, callback); } - private void forAllLayouts(Predicate condition, - Consumer callback) { + private void forAllLayouts(Predicate condition, + Consumer callback) { for (int i = 0; i < mActiveLayouts.size(); i++) { final int taskId = mActiveLayouts.keyAt(i); - final SizeCompatUILayout layout = mActiveLayouts.get(taskId); + final CompatUIWindowManager layout = mActiveLayouts.get(taskId); if (layout != null && condition.test(layout)) { callback.accept(layout); } @@ -298,11 +299,11 @@ public class SizeCompatUIController implements OnDisplaysChangedListener, * The interface for calls from outside the Shell, within the host process. */ @ExternalThread - private class SizeCompatUIImpl implements SizeCompatUI { + private class CompatUIImpl implements CompatUI { @Override public void onKeyguardOccludedChanged(boolean occluded) { mMainExecutor.execute(() -> { - SizeCompatUIController.this.onKeyguardOccludedChanged(occluded); + CompatUIController.this.onKeyguardOccludedChanged(occluded); }); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java new file mode 100644 index 0000000000000..ea4f209684385 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUILayout.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.compatui; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.ImageButton; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.android.wm.shell.R; + +/** + * Container for compat UI controls. + */ +public class CompatUILayout extends LinearLayout { + + private CompatUIWindowManager mWindowManager; + + public CompatUILayout(Context context) { + this(context, null); + } + + public CompatUILayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public CompatUILayout(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public CompatUILayout(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + void inject(CompatUIWindowManager windowManager) { + mWindowManager = windowManager; + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + // Need to relayout after changes like hiding / showing a hint since they affect size. + // Doing this directly in setSizeCompatHintVisibility can result in flaky animation. + mWindowManager.relayout(); + } + + void setSizeCompatHintVisibility(boolean show) { + final LinearLayout sizeCompatHint = findViewById(R.id.size_compat_hint); + int visibility = show ? View.VISIBLE : View.GONE; + if (sizeCompatHint.getVisibility() == visibility) { + return; + } + sizeCompatHint.setVisibility(visibility); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + final ImageButton restartButton = findViewById(R.id.size_compat_restart_button); + restartButton.setOnClickListener(view -> mWindowManager.onRestartButtonClicked()); + restartButton.setOnLongClickListener(view -> { + mWindowManager.onRestartButtonLongClicked(); + return true; + }); + + final LinearLayout sizeCompatHint = findViewById(R.id.size_compat_hint); + ((TextView) sizeCompatHint.findViewById(R.id.compat_mode_hint_text)) + .setText(R.string.restart_button_description); + sizeCompatHint.setOnClickListener(view -> setSizeCompatHintVisibility(/* show= */ false)); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java new file mode 100644 index 0000000000000..997ad04e3b579 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.compatui; + +import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; + +import android.annotation.Nullable; +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.os.Binder; +import android.util.Log; +import android.view.IWindow; +import android.view.LayoutInflater; +import android.view.SurfaceControl; +import android.view.SurfaceControlViewHost; +import android.view.SurfaceSession; +import android.view.View; +import android.view.WindowManager; +import android.view.WindowlessWindowManager; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.wm.shell.R; +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.common.SyncTransactionQueue; + +/** + * Holds view hierarchy of a root surface and helps to inflate and manage layout for compat + * controls. + */ +class CompatUIWindowManager extends WindowlessWindowManager { + + private static final String TAG = "CompatUIWindowManager"; + + private final SyncTransactionQueue mSyncQueue; + private final CompatUIController.CompatUICallback mCallback; + private final int mDisplayId; + private final int mTaskId; + private final Rect mStableBounds; + + private Context mContext; + private Configuration mTaskConfig; + private ShellTaskOrganizer.TaskListener mTaskListener; + private DisplayLayout mDisplayLayout; + + @VisibleForTesting + boolean mShouldShowHint; + + @Nullable + @VisibleForTesting + CompatUILayout mCompatUILayout; + + @Nullable + private SurfaceControlViewHost mViewHost; + @Nullable + private SurfaceControl mLeash; + + CompatUIWindowManager(Context context, Configuration taskConfig, + SyncTransactionQueue syncQueue, CompatUIController.CompatUICallback callback, + int taskId, ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout, + boolean hasShownHint) { + super(taskConfig, null /* rootSurface */, null /* hostInputToken */); + mContext = context; + mSyncQueue = syncQueue; + mCallback = callback; + mTaskConfig = taskConfig; + mDisplayId = mContext.getDisplayId(); + mTaskId = taskId; + mTaskListener = taskListener; + mDisplayLayout = displayLayout; + mShouldShowHint = !hasShownHint; + mStableBounds = new Rect(); + mDisplayLayout.getStableBounds(mStableBounds); + } + + @Override + public void setConfiguration(Configuration configuration) { + super.setConfiguration(configuration); + mContext = mContext.createConfigurationContext(configuration); + } + + @Override + protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) { + // Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later. + final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession()) + .setContainerLayer() + .setName("CompatUILeash") + .setHidden(false) + .setCallsite("CompatUIWindowManager#attachToParentSurface"); + attachToParentSurface(builder); + mLeash = builder.build(); + b.setParent(mLeash); + } + + /** Creates the layout for compat controls. */ + void createLayout(boolean show) { + if (!show || mCompatUILayout != null) { + // Wait until compat controls should be visible. + return; + } + + initCompatUi(); + updateSurfacePosition(); + + mCallback.onSizeCompatRestartButtonAppeared(mTaskId); + } + + /** Called when compat info changed. */ + void updateCompatInfo(Configuration taskConfig, + ShellTaskOrganizer.TaskListener taskListener, boolean show) { + final Configuration prevTaskConfig = mTaskConfig; + final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener; + mTaskConfig = taskConfig; + mTaskListener = taskListener; + + // Update configuration. + mContext = mContext.createConfigurationContext(taskConfig); + setConfiguration(taskConfig); + + if (mCompatUILayout == null || prevTaskListener != taskListener) { + // TaskListener changed, recreate the layout for new surface parent. + release(); + createLayout(show); + return; + } + + if (!taskConfig.windowConfiguration.getBounds() + .equals(prevTaskConfig.windowConfiguration.getBounds())) { + // Reposition the UI surfaces. + updateSurfacePosition(); + } + + if (taskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection()) { + // Update layout for RTL. + mCompatUILayout.setLayoutDirection(taskConfig.getLayoutDirection()); + updateSurfacePosition(); + } + } + + /** Called when the visibility of the UI should change. */ + void updateVisibility(boolean show) { + if (mCompatUILayout == null) { + // Layout may not have been created because it was hidden previously. + createLayout(show); + return; + } + + // Hide compat UIs when IME is showing. + final int newVisibility = show ? View.VISIBLE : View.GONE; + if (mCompatUILayout.getVisibility() != newVisibility) { + mCompatUILayout.setVisibility(newVisibility); + } + } + + /** Called when display layout changed. */ + void updateDisplayLayout(DisplayLayout displayLayout) { + final Rect prevStableBounds = mStableBounds; + final Rect curStableBounds = new Rect(); + displayLayout.getStableBounds(curStableBounds); + mDisplayLayout = displayLayout; + if (!prevStableBounds.equals(curStableBounds)) { + // Stable bounds changed, update UI surface positions. + updateSurfacePosition(); + mStableBounds.set(curStableBounds); + } + } + + /** Called when it is ready to be placed compat UI surface. */ + void attachToParentSurface(SurfaceControl.Builder b) { + mTaskListener.attachChildSurfaceToTask(mTaskId, b); + } + + /** Called when the restart button is clicked. */ + void onRestartButtonClicked() { + mCallback.onSizeCompatRestartButtonClicked(mTaskId); + } + + /** Called when the restart button is long clicked. */ + void onRestartButtonLongClicked() { + if (mCompatUILayout == null) { + return; + } + mCompatUILayout.setSizeCompatHintVisibility(/* show= */ true); + } + + int getDisplayId() { + return mDisplayId; + } + + int getTaskId() { + return mTaskId; + } + + /** Releases the surface control and tears down the view hierarchy. */ + void release() { + mCompatUILayout = null; + + if (mViewHost != null) { + mViewHost.release(); + mViewHost = null; + } + + if (mLeash != null) { + final SurfaceControl leash = mLeash; + mSyncQueue.runInSync(t -> t.remove(leash)); + mLeash = null; + } + } + + void relayout() { + mViewHost.relayout(getWindowLayoutParams()); + updateSurfacePosition(); + } + + @VisibleForTesting + void updateSurfacePosition() { + if (mCompatUILayout == null || mLeash == null) { + return; + } + + // Use stable bounds to prevent controls from overlapping with system bars. + final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds(); + final Rect stableBounds = new Rect(); + mDisplayLayout.getStableBounds(stableBounds); + stableBounds.intersect(taskBounds); + + // Position of the button in the container coordinate. + final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL + ? stableBounds.left - taskBounds.left + : stableBounds.right - taskBounds.left - mCompatUILayout.getMeasuredWidth(); + final int positionY = stableBounds.bottom - taskBounds.top + - mCompatUILayout.getMeasuredHeight(); + + updateSurfacePosition(positionX, positionY); + } + + private int getLayoutDirection() { + return mContext.getResources().getConfiguration().getLayoutDirection(); + } + + private void updateSurfacePosition(int positionX, int positionY) { + mSyncQueue.runInSync(t -> { + if (mLeash == null || !mLeash.isValid()) { + Log.w(TAG, "The leash has been released."); + return; + } + t.setPosition(mLeash, positionX, positionY); + // The compat UI should be the topmost child of the Task in case there can be more + // than one children. + t.setLayer(mLeash, Integer.MAX_VALUE); + }); + } + + /** Inflates {@link CompatUILayout} on to the root surface. */ + private void initCompatUi() { + if (mViewHost != null) { + throw new IllegalStateException( + "A UI has already been created with this window manager."); + } + + // Construction extracted into the separate methods to allow injection for tests. + mViewHost = createSurfaceViewHost(); + mCompatUILayout = inflateCompatUILayout(); + mCompatUILayout.inject(this); + + mCompatUILayout.setSizeCompatHintVisibility(mShouldShowHint); + + mViewHost.setView(mCompatUILayout, getWindowLayoutParams()); + + // Only show by default for the first time. + mShouldShowHint = false; + } + + @VisibleForTesting + CompatUILayout inflateCompatUILayout() { + return (CompatUILayout) LayoutInflater.from(mContext) + .inflate(R.layout.compat_ui_layout, null); + } + + @VisibleForTesting + SurfaceControlViewHost createSurfaceViewHost() { + return new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); + } + + /** Gets the layout params. */ + private WindowManager.LayoutParams getWindowLayoutParams() { + // Measure how big the hint is since its size depends on the text size. + mCompatUILayout.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); + final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams( + // Cannot be wrap_content as this determines the actual window size + mCompatUILayout.getMeasuredWidth(), mCompatUILayout.getMeasuredHeight(), + TYPE_APPLICATION_OVERLAY, + FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL, + PixelFormat.TRANSLUCENT); + winParams.token = new Binder(); + winParams.setTitle(CompatUILayout.class.getSimpleName() + mTaskId); + winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; + return winParams; + } + +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index 9500e8aecb570..6d4b2fa60b550 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -54,6 +54,8 @@ import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.annotations.ShellAnimationThread; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.common.annotations.ShellSplashscreenThread; +import com.android.wm.shell.compatui.CompatUI; +import com.android.wm.shell.compatui.CompatUIController; import com.android.wm.shell.displayareahelper.DisplayAreaHelper; import com.android.wm.shell.displayareahelper.DisplayAreaHelperController; import com.android.wm.shell.draganddrop.DragAndDrop; @@ -75,8 +77,6 @@ import com.android.wm.shell.pip.phone.PipAppOpsListener; import com.android.wm.shell.pip.phone.PipTouchHandler; import com.android.wm.shell.recents.RecentTasks; import com.android.wm.shell.recents.RecentTasksController; -import com.android.wm.shell.sizecompatui.SizeCompatUI; -import com.android.wm.shell.sizecompatui.SizeCompatUIController; import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.startingsurface.StartingSurface; @@ -173,25 +173,25 @@ public abstract class WMShellBaseModule { @Provides static ShellTaskOrganizer provideShellTaskOrganizer(@ShellMainThread ShellExecutor mainExecutor, Context context, - SizeCompatUIController sizeCompatUI, + CompatUIController compatUI, Optional recentTasksOptional ) { - return new ShellTaskOrganizer(mainExecutor, context, sizeCompatUI, recentTasksOptional); + return new ShellTaskOrganizer(mainExecutor, context, compatUI, recentTasksOptional); } @WMSingleton @Provides - static SizeCompatUI provideSizeCompatUI(SizeCompatUIController sizeCompatUIController) { - return sizeCompatUIController.asSizeCompatUI(); + static CompatUI provideCompatUI(CompatUIController compatUIController) { + return compatUIController.asCompatUI(); } @WMSingleton @Provides - static SizeCompatUIController provideSizeCompatUIController(Context context, + static CompatUIController provideCompatUIController(Context context, DisplayController displayController, DisplayInsetsController displayInsetsController, DisplayImeController imeController, SyncTransactionQueue syncQueue, @ShellMainThread ShellExecutor mainExecutor) { - return new SizeCompatUIController(context, displayController, displayInsetsController, + return new CompatUIController(context, displayController, displayInsetsController, imeController, syncQueue, mainExecutor); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 854fc60e15e8d..f0b2716f05d86 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -778,8 +778,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } @Override - public boolean supportSizeCompatUI() { - // PIP doesn't support size compat. + public boolean supportCompatUI() { + // PIP doesn't support compat. return false; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java deleted file mode 100644 index ff6f913207f65..0000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.sizecompatui; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.View; -import android.widget.FrameLayout; -import android.widget.LinearLayout; - -import androidx.annotation.Nullable; - -import com.android.wm.shell.R; - -/** Popup to show the hint about the {@link SizeCompatRestartButton}. */ -public class SizeCompatHintPopup extends FrameLayout implements View.OnClickListener { - - private SizeCompatUILayout mLayout; - - public SizeCompatHintPopup(Context context) { - super(context); - } - - public SizeCompatHintPopup(Context context, @Nullable AttributeSet attrs) { - super(context, attrs); - } - - public SizeCompatHintPopup(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - public SizeCompatHintPopup(Context context, AttributeSet attrs, int defStyleAttr, - int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - } - - void inject(SizeCompatUILayout layout) { - mLayout = layout; - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - final LinearLayout hintPopup = findViewById(R.id.size_compat_hint_popup); - hintPopup.setOnClickListener(this); - } - - @Override - public void onClick(View v) { - mLayout.dismissHint(); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java deleted file mode 100644 index d75fe5173c5f4..0000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.sizecompatui; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.View; -import android.widget.FrameLayout; -import android.widget.ImageButton; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.android.wm.shell.R; - -/** Button to restart the size compat activity. */ -public class SizeCompatRestartButton extends FrameLayout implements View.OnClickListener, - View.OnLongClickListener { - - private SizeCompatUILayout mLayout; - - public SizeCompatRestartButton(@NonNull Context context) { - super(context); - } - - public SizeCompatRestartButton(@NonNull Context context, @Nullable AttributeSet attrs) { - super(context, attrs); - } - - public SizeCompatRestartButton(@NonNull Context context, @Nullable AttributeSet attrs, - int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - public SizeCompatRestartButton(@NonNull Context context, @Nullable AttributeSet attrs, - int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - } - - void inject(SizeCompatUILayout layout) { - mLayout = layout; - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - final ImageButton restartButton = findViewById(R.id.size_compat_restart_button); - restartButton.setOnClickListener(this); - restartButton.setOnLongClickListener(this); - } - - @Override - public void onClick(View v) { - mLayout.onRestartButtonClicked(); - } - - @Override - public boolean onLongClick(View v) { - mLayout.onRestartButtonLongClicked(); - return true; - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java deleted file mode 100644 index c35b89af6c1bd..0000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java +++ /dev/null @@ -1,344 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.sizecompatui; - -import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; -import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; - -import android.annotation.Nullable; -import android.content.Context; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.graphics.PixelFormat; -import android.graphics.Rect; -import android.os.Binder; -import android.util.Log; -import android.view.SurfaceControl; -import android.view.View; -import android.view.WindowManager; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.wm.shell.R; -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.common.DisplayLayout; -import com.android.wm.shell.common.SyncTransactionQueue; - -/** - * Records and handles layout of size compat UI on a task with size compat activity. Helps to - * calculate proper bounds when configuration or UI position changes. - */ -class SizeCompatUILayout { - private static final String TAG = "SizeCompatUILayout"; - - final SyncTransactionQueue mSyncQueue; - private final SizeCompatUIController.SizeCompatUICallback mCallback; - private Context mContext; - private Configuration mTaskConfig; - private final int mDisplayId; - private final int mTaskId; - private ShellTaskOrganizer.TaskListener mTaskListener; - private DisplayLayout mDisplayLayout; - private final Rect mStableBounds; - private final int mButtonWidth; - private final int mButtonHeight; - private final int mPopupOffsetX; - private final int mPopupOffsetY; - - @VisibleForTesting - final SizeCompatUIWindowManager mButtonWindowManager; - @VisibleForTesting - @Nullable - SizeCompatUIWindowManager mHintWindowManager; - @VisibleForTesting - @Nullable - SizeCompatRestartButton mButton; - @VisibleForTesting - @Nullable - SizeCompatHintPopup mHint; - @VisibleForTesting - boolean mShouldShowHint; - - SizeCompatUILayout(SyncTransactionQueue syncQueue, - SizeCompatUIController.SizeCompatUICallback callback, Context context, - Configuration taskConfig, int taskId, ShellTaskOrganizer.TaskListener taskListener, - DisplayLayout displayLayout, boolean hasShownHint) { - mSyncQueue = syncQueue; - mCallback = callback; - mContext = context.createConfigurationContext(taskConfig); - mTaskConfig = taskConfig; - mDisplayId = mContext.getDisplayId(); - mTaskId = taskId; - mTaskListener = taskListener; - mDisplayLayout = displayLayout; - mShouldShowHint = !hasShownHint; - mButtonWindowManager = new SizeCompatUIWindowManager(mContext, taskConfig, this); - - mStableBounds = new Rect(); - mDisplayLayout.getStableBounds(mStableBounds); - - final Resources resources = mContext.getResources(); - mButtonWidth = resources.getDimensionPixelSize(R.dimen.size_compat_button_width); - mButtonHeight = resources.getDimensionPixelSize(R.dimen.size_compat_button_height); - mPopupOffsetX = (mButtonWidth / 2) - resources.getDimensionPixelSize( - R.dimen.size_compat_hint_corner_radius) - (resources.getDimensionPixelSize( - R.dimen.size_compat_hint_point_width) / 2); - mPopupOffsetY = mButtonHeight; - } - - /** Creates the activity restart button window. */ - void createSizeCompatButton(boolean show) { - if (!show || mButton != null) { - // Wait until button should be visible. - return; - } - mButton = mButtonWindowManager.createSizeCompatButton(); - updateButtonSurfacePosition(); - - if (mShouldShowHint) { - // Only show by default for the first time. - mShouldShowHint = false; - createSizeCompatHint(); - } - - mCallback.onSizeCompatRestartButtonAppeared(mTaskId); - } - - /** Creates the restart button hint window. */ - private void createSizeCompatHint() { - if (mHint != null) { - // Hint already shown. - return; - } - mHintWindowManager = createHintWindowManager(); - mHint = mHintWindowManager.createSizeCompatHint(); - updateHintSurfacePosition(); - } - - @VisibleForTesting - SizeCompatUIWindowManager createHintWindowManager() { - return new SizeCompatUIWindowManager(mContext, mTaskConfig, this); - } - - /** Dismisses the hint window. */ - void dismissHint() { - mHint = null; - if (mHintWindowManager != null) { - mHintWindowManager.release(); - mHintWindowManager = null; - } - } - - /** Releases the UI windows. */ - void release() { - dismissHint(); - mButton = null; - mButtonWindowManager.release(); - } - - /** Called when size compat info changed. */ - void updateSizeCompatInfo(Configuration taskConfig, - ShellTaskOrganizer.TaskListener taskListener, boolean show) { - final Configuration prevTaskConfig = mTaskConfig; - final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener; - mTaskConfig = taskConfig; - mTaskListener = taskListener; - - // Update configuration. - mContext = mContext.createConfigurationContext(taskConfig); - mButtonWindowManager.setConfiguration(taskConfig); - if (mHintWindowManager != null) { - mHintWindowManager.setConfiguration(taskConfig); - } - - if (mButton == null || prevTaskListener != taskListener) { - // TaskListener changed, recreate the button for new surface parent. - release(); - createSizeCompatButton(show); - return; - } - - if (!taskConfig.windowConfiguration.getBounds() - .equals(prevTaskConfig.windowConfiguration.getBounds())) { - // Reposition the UI surfaces. - updateAllSurfacePositions(); - } - - if (taskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection()) { - // Update layout for RTL. - mButton.setLayoutDirection(taskConfig.getLayoutDirection()); - updateButtonSurfacePosition(); - if (mHint != null) { - mHint.setLayoutDirection(taskConfig.getLayoutDirection()); - updateHintSurfacePosition(); - } - } - } - - /** Called when display layout changed. */ - void updateDisplayLayout(DisplayLayout displayLayout) { - final Rect prevStableBounds = mStableBounds; - final Rect curStableBounds = new Rect(); - displayLayout.getStableBounds(curStableBounds); - mDisplayLayout = displayLayout; - if (!prevStableBounds.equals(curStableBounds)) { - // Stable bounds changed, update UI surface positions. - updateAllSurfacePositions(); - mStableBounds.set(curStableBounds); - } - } - - /** Called when the visibility of the UI should change. */ - void updateVisibility(boolean show) { - if (mButton == null) { - // Button may not have been created because it was hidden previously. - createSizeCompatButton(show); - return; - } - - // Hide size compat UIs when IME is showing. - final int newVisibility = show ? View.VISIBLE : View.GONE; - if (mButton.getVisibility() != newVisibility) { - mButton.setVisibility(newVisibility); - } - if (mHint != null && mHint.getVisibility() != newVisibility) { - mHint.setVisibility(newVisibility); - } - } - - /** Gets the layout params for restart button. */ - WindowManager.LayoutParams getButtonWindowLayoutParams() { - final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams( - // Cannot be wrap_content as this determines the actual window size - mButtonWidth, mButtonHeight, - TYPE_APPLICATION_OVERLAY, - FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL, - PixelFormat.TRANSLUCENT); - winParams.token = new Binder(); - winParams.setTitle(SizeCompatRestartButton.class.getSimpleName() + getTaskId()); - winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; - return winParams; - } - - /** Gets the layout params for hint popup. */ - WindowManager.LayoutParams getHintWindowLayoutParams(SizeCompatHintPopup hint) { - final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams( - // Cannot be wrap_content as this determines the actual window size - hint.getMeasuredWidth(), hint.getMeasuredHeight(), - TYPE_APPLICATION_OVERLAY, - FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL, - PixelFormat.TRANSLUCENT); - winParams.token = new Binder(); - winParams.setTitle(SizeCompatHintPopup.class.getSimpleName() + getTaskId()); - winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; - winParams.windowAnimations = android.R.style.Animation_InputMethod; - return winParams; - } - - /** Called when it is ready to be placed size compat UI surface. */ - void attachToParentSurface(SurfaceControl.Builder b) { - mTaskListener.attachChildSurfaceToTask(mTaskId, b); - } - - /** Called when the restart button is clicked. */ - void onRestartButtonClicked() { - mCallback.onSizeCompatRestartButtonClicked(mTaskId); - } - - /** Called when the restart button is long clicked. */ - void onRestartButtonLongClicked() { - createSizeCompatHint(); - } - - private void updateAllSurfacePositions() { - updateButtonSurfacePosition(); - updateHintSurfacePosition(); - } - - @VisibleForTesting - void updateButtonSurfacePosition() { - if (mButton == null || mButtonWindowManager.getSurfaceControl() == null) { - return; - } - final SurfaceControl leash = mButtonWindowManager.getSurfaceControl(); - - // Use stable bounds to prevent the button from overlapping with system bars. - final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds(); - final Rect stableBounds = new Rect(); - mDisplayLayout.getStableBounds(stableBounds); - stableBounds.intersect(taskBounds); - - // Position of the button in the container coordinate. - final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL - ? stableBounds.left - taskBounds.left - : stableBounds.right - taskBounds.left - mButtonWidth; - final int positionY = stableBounds.bottom - taskBounds.top - mButtonHeight; - - updateSurfacePosition(leash, positionX, positionY); - } - - @VisibleForTesting - void updateHintSurfacePosition() { - if (mHint == null || mHintWindowManager == null - || mHintWindowManager.getSurfaceControl() == null) { - return; - } - final SurfaceControl leash = mHintWindowManager.getSurfaceControl(); - - // Use stable bounds to prevent the hint from overlapping with system bars. - final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds(); - final Rect stableBounds = new Rect(); - mDisplayLayout.getStableBounds(stableBounds); - stableBounds.intersect(taskBounds); - - // Position of the hint in the container coordinate. - final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL - ? stableBounds.left - taskBounds.left + mPopupOffsetX - : stableBounds.right - taskBounds.left - mPopupOffsetX - mHint.getMeasuredWidth(); - final int positionY = - stableBounds.bottom - taskBounds.top - mPopupOffsetY - mHint.getMeasuredHeight(); - - updateSurfacePosition(leash, positionX, positionY); - } - - private void updateSurfacePosition(SurfaceControl leash, int positionX, int positionY) { - mSyncQueue.runInSync(t -> { - if (!leash.isValid()) { - Log.w(TAG, "The leash has been released."); - return; - } - t.setPosition(leash, positionX, positionY); - // The size compat UI should be the topmost child of the Task in case there can be more - // than one children. - t.setLayer(leash, Integer.MAX_VALUE); - }); - } - - int getDisplayId() { - return mDisplayId; - } - - int getTaskId() { - return mTaskId; - } - - private int getLayoutDirection() { - return mContext.getResources().getConfiguration().getLayoutDirection(); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java deleted file mode 100644 index 82f69c3e2985c..0000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.sizecompatui; - -import android.annotation.Nullable; -import android.content.Context; -import android.content.res.Configuration; -import android.view.IWindow; -import android.view.LayoutInflater; -import android.view.SurfaceControl; -import android.view.SurfaceControlViewHost; -import android.view.SurfaceSession; -import android.view.View; -import android.view.WindowlessWindowManager; - -import com.android.wm.shell.R; - -/** - * Holds view hierarchy of a root surface and helps to inflate {@link SizeCompatRestartButton} or - * {@link SizeCompatHintPopup}. - */ -class SizeCompatUIWindowManager extends WindowlessWindowManager { - - private Context mContext; - private final SizeCompatUILayout mLayout; - - @Nullable - private SurfaceControlViewHost mViewHost; - @Nullable - private SurfaceControl mLeash; - - SizeCompatUIWindowManager(Context context, Configuration config, SizeCompatUILayout layout) { - super(config, null /* rootSurface */, null /* hostInputToken */); - mContext = context; - mLayout = layout; - } - - @Override - public void setConfiguration(Configuration configuration) { - super.setConfiguration(configuration); - mContext = mContext.createConfigurationContext(configuration); - } - - @Override - protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) { - // Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later. - final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession()) - .setContainerLayer() - .setName("SizeCompatUILeash") - .setHidden(false) - .setCallsite("SizeCompatUIWindowManager#attachToParentSurface"); - mLayout.attachToParentSurface(builder); - mLeash = builder.build(); - b.setParent(mLeash); - } - - /** Inflates {@link SizeCompatRestartButton} on to the root surface. */ - SizeCompatRestartButton createSizeCompatButton() { - if (mViewHost != null) { - throw new IllegalStateException( - "A UI has already been created with this window manager."); - } - - mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); - - final SizeCompatRestartButton button = (SizeCompatRestartButton) - LayoutInflater.from(mContext).inflate(R.layout.size_compat_ui, null); - button.inject(mLayout); - mViewHost.setView(button, mLayout.getButtonWindowLayoutParams()); - return button; - } - - /** Inflates {@link SizeCompatHintPopup} on to the root surface. */ - SizeCompatHintPopup createSizeCompatHint() { - if (mViewHost != null) { - throw new IllegalStateException( - "A UI has already been created with this window manager."); - } - - mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); - - final SizeCompatHintPopup hint = (SizeCompatHintPopup) - LayoutInflater.from(mContext).inflate(R.layout.size_compat_mode_hint, null); - // Measure how big the hint is. - hint.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); - hint.inject(mLayout); - mViewHost.setView(hint, mLayout.getHintWindowLayoutParams(hint)); - return hint; - } - - /** Releases the surface control and tears down the view hierarchy. */ - void release() { - if (mViewHost != null) { - mViewHost.release(); - mViewHost = null; - } - - if (mLeash != null) { - final SurfaceControl leash = mLeash; - mLayout.mSyncQueue.runInSync(t -> t.remove(leash)); - mLeash = null; - } - } - - /** - * Gets {@link SurfaceControl} of the surface holding size compat UI view. @return {@code null} - * if not feasible. - */ - @Nullable - SurfaceControl getSurfaceControl() { - return mLeash; - } -} 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 1fcbf14fb7328..a3b98a8fc8807 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 @@ -56,7 +56,7 @@ import androidx.test.filters.SmallTest; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TransactionPool; -import com.android.wm.shell.sizecompatui.SizeCompatUIController; +import com.android.wm.shell.compatui.CompatUIController; import org.junit.Before; import org.junit.Test; @@ -82,7 +82,7 @@ public class ShellTaskOrganizerTests { @Mock private Context mContext; @Mock - private SizeCompatUIController mSizeCompatUI; + private CompatUIController mCompatUI; ShellTaskOrganizer mOrganizer; private final SyncTransactionQueue mSyncTransactionQueue = mock(SyncTransactionQueue.class); @@ -132,7 +132,7 @@ public class ShellTaskOrganizerTests { .when(mTaskOrganizerController).registerTaskOrganizer(any()); } catch (RemoteException e) {} mOrganizer = spy(new ShellTaskOrganizer(mTaskOrganizerController, mTestExecutor, mContext, - mSizeCompatUI, Optional.empty())); + mCompatUI, Optional.empty())); } @Test @@ -334,34 +334,34 @@ public class ShellTaskOrganizerTests { mOrganizer.onTaskAppeared(taskInfo1, null); // sizeCompatActivity is null if top activity is not in size compat. - verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId, + verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId, null /* taskConfig */, null /* taskListener */); // sizeCompatActivity is non-null if top activity is in size compat. - clearInvocations(mSizeCompatUI); + clearInvocations(mCompatUI); final RunningTaskInfo taskInfo2 = createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode()); taskInfo2.displayId = taskInfo1.displayId; taskInfo2.topActivityInSizeCompat = true; taskInfo2.isVisible = true; mOrganizer.onTaskInfoChanged(taskInfo2); - verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId, + verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId, taskInfo1.configuration, taskListener); // Not show size compat UI if task is not visible. - clearInvocations(mSizeCompatUI); + clearInvocations(mCompatUI); final RunningTaskInfo taskInfo3 = createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode()); taskInfo3.displayId = taskInfo1.displayId; taskInfo3.topActivityInSizeCompat = true; taskInfo3.isVisible = false; mOrganizer.onTaskInfoChanged(taskInfo3); - verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId, + verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId, null /* taskConfig */, null /* taskListener */); - clearInvocations(mSizeCompatUI); + clearInvocations(mCompatUI); mOrganizer.onTaskVanished(taskInfo1); - verify(mSizeCompatUI).onSizeCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId, + verify(mCompatUI).onCompatInfoChanged(taskInfo1.displayId, taskInfo1.taskId, null /* taskConfig */, null /* taskListener */); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java similarity index 83% rename from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java index 877b19223bf6a..f622edb7f134d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUIControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.sizecompatui; +package com.android.wm.shell.compatui; import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; @@ -56,18 +56,18 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; /** - * Tests for {@link SizeCompatUIController}. + * Tests for {@link CompatUIController}. * * Build/Install/Run: - * atest WMShellUnitTests:SizeCompatUIControllerTest + * atest WMShellUnitTests:CompatUIControllerTest */ @RunWith(AndroidTestingRunner.class) @SmallTest -public class SizeCompatUIControllerTest extends ShellTestCase { +public class CompatUIControllerTest extends ShellTestCase { private static final int DISPLAY_ID = 0; private static final int TASK_ID = 12; - private SizeCompatUIController mController; + private CompatUIController mController; private @Mock DisplayController mMockDisplayController; private @Mock DisplayInsetsController mMockDisplayInsetsController; private @Mock DisplayLayout mMockDisplayLayout; @@ -75,7 +75,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase { private @Mock ShellTaskOrganizer.TaskListener mMockTaskListener; private @Mock SyncTransactionQueue mMockSyncQueue; private @Mock ShellExecutor mMockExecutor; - private @Mock SizeCompatUILayout mMockLayout; + private @Mock CompatUIWindowManager mMockLayout; @Captor ArgumentCaptor mOnInsetsChangedListenerCaptor; @@ -87,10 +87,10 @@ public class SizeCompatUIControllerTest extends ShellTestCase { doReturn(mMockDisplayLayout).when(mMockDisplayController).getDisplayLayout(anyInt()); doReturn(DISPLAY_ID).when(mMockLayout).getDisplayId(); doReturn(TASK_ID).when(mMockLayout).getTaskId(); - mController = new SizeCompatUIController(mContext, mMockDisplayController, + mController = new CompatUIController(mContext, mMockDisplayController, mMockDisplayInsetsController, mMockImeController, mMockSyncQueue, mMockExecutor) { @Override - SizeCompatUILayout createLayout(Context context, int displayId, int taskId, + CompatUIWindowManager createLayout(Context context, int displayId, int taskId, Configuration taskConfig, ShellTaskOrganizer.TaskListener taskListener) { return mMockLayout; } @@ -105,24 +105,24 @@ public class SizeCompatUIControllerTest extends ShellTestCase { } @Test - public void testOnSizeCompatInfoChanged() { + public void testOnCompatInfoChanged() { final Configuration taskConfig = new Configuration(); // Verify that the restart button is added with non-null size compat info. - mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); + mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); verify(mController).createLayout(any(), eq(DISPLAY_ID), eq(TASK_ID), eq(taskConfig), eq(mMockTaskListener)); // Verify that the restart button is updated with non-null new size compat info. final Configuration newTaskConfig = new Configuration(); - mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, newTaskConfig, mMockTaskListener); + mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, newTaskConfig, mMockTaskListener); - verify(mMockLayout).updateSizeCompatInfo(taskConfig, mMockTaskListener, + verify(mMockLayout).updateCompatInfo(taskConfig, mMockTaskListener, true /* show */); // Verify that the restart button is removed with null size compat info. - mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, null, mMockTaskListener); + mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, null, mMockTaskListener); verify(mMockLayout).release(); } @@ -140,7 +140,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase { public void testOnDisplayRemoved() { mController.onDisplayAdded(DISPLAY_ID); final Configuration taskConfig = new Configuration(); - mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, + mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); mController.onDisplayRemoved(DISPLAY_ID + 1); @@ -158,7 +158,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase { @Test public void testOnDisplayConfigurationChanged() { final Configuration taskConfig = new Configuration(); - mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, + mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); final Configuration newTaskConfig = new Configuration(); @@ -175,7 +175,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase { public void testInsetsChanged() { mController.onDisplayAdded(DISPLAY_ID); final Configuration taskConfig = new Configuration(); - mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, + mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); InsetsState insetsState = new InsetsState(); InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR); @@ -197,7 +197,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase { @Test public void testChangeButtonVisibilityOnImeShowHide() { final Configuration taskConfig = new Configuration(); - mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); + mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); // Verify that the restart button is hidden after IME is showing. mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */); @@ -205,9 +205,9 @@ public class SizeCompatUIControllerTest extends ShellTestCase { verify(mMockLayout).updateVisibility(false); // Verify button remains hidden while IME is showing. - mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); + mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); - verify(mMockLayout).updateSizeCompatInfo(taskConfig, mMockTaskListener, + verify(mMockLayout).updateCompatInfo(taskConfig, mMockTaskListener, false /* show */); // Verify button is shown after IME is hidden. @@ -219,7 +219,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase { @Test public void testChangeButtonVisibilityOnKeyguardOccludedChanged() { final Configuration taskConfig = new Configuration(); - mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); + mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); // Verify that the restart button is hidden after keyguard becomes occluded. mController.onKeyguardOccludedChanged(true); @@ -227,9 +227,9 @@ public class SizeCompatUIControllerTest extends ShellTestCase { verify(mMockLayout).updateVisibility(false); // Verify button remains hidden while keyguard is occluded. - mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); + mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); - verify(mMockLayout).updateSizeCompatInfo(taskConfig, mMockTaskListener, + verify(mMockLayout).updateCompatInfo(taskConfig, mMockTaskListener, false /* show */); // Verify button is shown after keyguard becomes not occluded. @@ -241,7 +241,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase { @Test public void testButtonRemainsHiddenOnKeyguardOccludedFalseWhenImeIsShowing() { final Configuration taskConfig = new Configuration(); - mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); + mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */); mController.onKeyguardOccludedChanged(true); @@ -264,7 +264,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase { @Test public void testButtonRemainsHiddenOnImeHideWhenKeyguardIsOccluded() { final Configuration taskConfig = new Configuration(); - mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); + mController.onCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener); mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */); mController.onKeyguardOccludedChanged(true); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java similarity index 50% rename from libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java index a20a5e9e8d917..2c3987bc358d5 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java @@ -14,17 +14,20 @@ * limitations under the License. */ -package com.android.wm.shell.sizecompatui; +package com.android.wm.shell.compatui; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.verify; import android.content.res.Configuration; import android.testing.AndroidTestingRunner; import android.view.LayoutInflater; +import android.view.SurfaceControlViewHost; import android.widget.ImageButton; +import android.widget.LinearLayout; import androidx.test.filters.SmallTest; @@ -41,55 +44,68 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; /** - * Tests for {@link SizeCompatRestartButton}. + * Tests for {@link CompatUILayout}. * * Build/Install/Run: - * atest WMShellUnitTests:SizeCompatRestartButtonTest + * atest WMShellUnitTests:CompatUILayoutTest */ @RunWith(AndroidTestingRunner.class) @SmallTest -public class SizeCompatRestartButtonTest extends ShellTestCase { +public class CompatUILayoutTest extends ShellTestCase { private static final int TASK_ID = 1; @Mock private SyncTransactionQueue mSyncTransactionQueue; - @Mock private SizeCompatUIController.SizeCompatUICallback mCallback; + @Mock private CompatUIController.CompatUICallback mCallback; @Mock private ShellTaskOrganizer.TaskListener mTaskListener; - @Mock private DisplayLayout mDisplayLayout; + @Mock private SurfaceControlViewHost mViewHost; - private SizeCompatUILayout mLayout; - private SizeCompatRestartButton mButton; + private CompatUIWindowManager mWindowManager; + private CompatUILayout mCompatUILayout; @Before public void setUp() { MockitoAnnotations.initMocks(this); - mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mCallback, mContext, - new Configuration(), TASK_ID, mTaskListener, mDisplayLayout, + mWindowManager = new CompatUIWindowManager(mContext, new Configuration(), + mSyncTransactionQueue, mCallback, TASK_ID, mTaskListener, new DisplayLayout(), false /* hasShownHint */); - mButton = (SizeCompatRestartButton) - LayoutInflater.from(mContext).inflate(R.layout.size_compat_ui, null); - mButton.inject(mLayout); - spyOn(mLayout); + mCompatUILayout = (CompatUILayout) + LayoutInflater.from(mContext).inflate(R.layout.compat_ui_layout, null); + mCompatUILayout.inject(mWindowManager); + + spyOn(mWindowManager); + spyOn(mCompatUILayout); + doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost(); } @Test - public void testOnClick() { - final ImageButton button = mButton.findViewById(R.id.size_compat_restart_button); + public void testOnClickForRestartButton() { + final ImageButton button = mCompatUILayout.findViewById(R.id.size_compat_restart_button); button.performClick(); - verify(mLayout).onRestartButtonClicked(); + verify(mWindowManager).onRestartButtonClicked(); + doReturn(mCompatUILayout).when(mWindowManager).inflateCompatUILayout(); verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID); } @Test - public void testOnLongClick() { - doNothing().when(mLayout).onRestartButtonLongClicked(); + public void testOnLongClickForRestartButton() { + doNothing().when(mWindowManager).onRestartButtonLongClicked(); - final ImageButton button = mButton.findViewById(R.id.size_compat_restart_button); + final ImageButton button = mCompatUILayout.findViewById(R.id.size_compat_restart_button); button.performLongClick(); - verify(mLayout).onRestartButtonLongClicked(); + verify(mWindowManager).onRestartButtonLongClicked(); + } + + @Test + public void testOnClickForSizeCompatHint() { + mWindowManager.createLayout(true /* show */); + final LinearLayout sizeCompatHint = mCompatUILayout.findViewById(R.id.size_compat_hint); + sizeCompatHint.performClick(); + + verify(mCompatUILayout).setSizeCompatHintVisibility(/* show= */ false); } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java new file mode 100644 index 0000000000000..d5dcf2e11a46e --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.compatui; + +import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.content.res.Configuration; +import android.graphics.Rect; +import android.testing.AndroidTestingRunner; +import android.view.DisplayInfo; +import android.view.InsetsSource; +import android.view.InsetsState; +import android.view.SurfaceControl; +import android.view.SurfaceControlViewHost; +import android.view.View; + +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.ShellTestCase; +import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.common.SyncTransactionQueue; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Tests for {@link CompatUIWindowManager}. + * + * Build/Install/Run: + * atest WMShellUnitTests:CompatUIWindowManagerTest + */ +@RunWith(AndroidTestingRunner.class) +@SmallTest +public class CompatUIWindowManagerTest extends ShellTestCase { + + private static final int TASK_ID = 1; + + @Mock private SyncTransactionQueue mSyncTransactionQueue; + @Mock private CompatUIController.CompatUICallback mCallback; + @Mock private ShellTaskOrganizer.TaskListener mTaskListener; + @Mock private CompatUILayout mCompatUILayout; + @Mock private SurfaceControlViewHost mViewHost; + private Configuration mTaskConfig; + + private CompatUIWindowManager mWindowManager; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mTaskConfig = new Configuration(); + + mWindowManager = new CompatUIWindowManager(mContext, new Configuration(), + mSyncTransactionQueue, mCallback, TASK_ID, mTaskListener, new DisplayLayout(), + false /* hasShownHint */); + + spyOn(mWindowManager); + doReturn(mCompatUILayout).when(mWindowManager).inflateCompatUILayout(); + doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost(); + } + + @Test + public void testCreateSizeCompatButton() { + // Not create layout if show is false. + mWindowManager.createLayout(false /* show */); + + verify(mWindowManager, never()).inflateCompatUILayout(); + + // Not create hint popup. + mWindowManager.mShouldShowHint = false; + mWindowManager.createLayout(true /* show */); + + verify(mWindowManager).inflateCompatUILayout(); + verify(mCompatUILayout).setSizeCompatHintVisibility(false /* show */); + + // Create hint popup. + mWindowManager.release(); + mWindowManager.mShouldShowHint = true; + mWindowManager.createLayout(true /* show */); + + verify(mWindowManager, times(2)).inflateCompatUILayout(); + assertNotNull(mCompatUILayout); + verify(mCompatUILayout).setSizeCompatHintVisibility(true /* show */); + assertFalse(mWindowManager.mShouldShowHint); + } + + @Test + public void testRelease() { + mWindowManager.createLayout(true /* show */); + + verify(mWindowManager).inflateCompatUILayout(); + + mWindowManager.release(); + + verify(mViewHost).release(); + } + + @Test + public void testUpdateCompatInfo() { + mWindowManager.createLayout(true /* show */); + + // No diff + clearInvocations(mWindowManager); + mWindowManager.updateCompatInfo(mTaskConfig, mTaskListener, true /* show */); + + verify(mWindowManager, never()).updateSurfacePosition(); + verify(mWindowManager, never()).release(); + verify(mWindowManager, never()).createLayout(anyBoolean()); + + // Change task listener, recreate button. + clearInvocations(mWindowManager); + final ShellTaskOrganizer.TaskListener newTaskListener = mock( + ShellTaskOrganizer.TaskListener.class); + mWindowManager.updateCompatInfo(mTaskConfig, newTaskListener, + true /* show */); + + verify(mWindowManager).release(); + verify(mWindowManager).createLayout(anyBoolean()); + + // Change task bounds, update position. + clearInvocations(mWindowManager); + final Configuration newTaskConfiguration = new Configuration(); + newTaskConfiguration.windowConfiguration.setBounds(new Rect(0, 1000, 0, 2000)); + mWindowManager.updateCompatInfo(newTaskConfiguration, newTaskListener, + true /* show */); + + verify(mWindowManager).updateSurfacePosition(); + } + + @Test + public void testUpdateDisplayLayout() { + final DisplayInfo displayInfo = new DisplayInfo(); + displayInfo.logicalWidth = 1000; + displayInfo.logicalHeight = 2000; + final DisplayLayout displayLayout1 = new DisplayLayout(displayInfo, + mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false); + + mWindowManager.updateDisplayLayout(displayLayout1); + verify(mWindowManager).updateSurfacePosition(); + + // No update if the display bounds is the same. + clearInvocations(mWindowManager); + final DisplayLayout displayLayout2 = new DisplayLayout(displayInfo, + mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false); + mWindowManager.updateDisplayLayout(displayLayout2); + verify(mWindowManager, never()).updateSurfacePosition(); + } + + @Test + public void testUpdateDisplayLayoutInsets() { + final DisplayInfo displayInfo = new DisplayInfo(); + displayInfo.logicalWidth = 1000; + displayInfo.logicalHeight = 2000; + final DisplayLayout displayLayout = new DisplayLayout(displayInfo, + mContext.getResources(), /* hasNavigationBar= */ true, /* hasStatusBar= */ false); + + mWindowManager.updateDisplayLayout(displayLayout); + verify(mWindowManager).updateSurfacePosition(); + + // Update if the insets change on the existing display layout + clearInvocations(mWindowManager); + InsetsState insetsState = new InsetsState(); + InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR); + insetsSource.setFrame(0, 0, 1000, 1000); + insetsState.addSource(insetsSource); + displayLayout.setInsets(mContext.getResources(), insetsState); + mWindowManager.updateDisplayLayout(displayLayout); + verify(mWindowManager).updateSurfacePosition(); + } + + @Test + public void testUpdateVisibility() { + // Create button if it is not created. + mWindowManager.mCompatUILayout = null; + mWindowManager.updateVisibility(true /* show */); + + verify(mWindowManager).createLayout(true /* show */); + + // Hide button. + clearInvocations(mWindowManager); + doReturn(View.VISIBLE).when(mCompatUILayout).getVisibility(); + mWindowManager.updateVisibility(false /* show */); + + verify(mWindowManager, never()).createLayout(anyBoolean()); + verify(mCompatUILayout).setVisibility(View.GONE); + + // Show button. + doReturn(View.GONE).when(mCompatUILayout).getVisibility(); + mWindowManager.updateVisibility(true /* show */); + + verify(mWindowManager, never()).createLayout(anyBoolean()); + verify(mCompatUILayout).setVisibility(View.VISIBLE); + } + + @Test + public void testAttachToParentSurface() { + final SurfaceControl.Builder b = new SurfaceControl.Builder(); + mWindowManager.attachToParentSurface(b); + + verify(mTaskListener).attachChildSurfaceToTask(TASK_ID, b); + } + + @Test + public void testOnRestartButtonClicked() { + mWindowManager.onRestartButtonClicked(); + + verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID); + } + + @Test + public void testOnRestartButtonLongClicked_showHint() { + // Not create hint popup. + mWindowManager.mShouldShowHint = false; + mWindowManager.createLayout(true /* show */); + + verify(mWindowManager).inflateCompatUILayout(); + verify(mCompatUILayout).setSizeCompatHintVisibility(false /* show */); + + mWindowManager.onRestartButtonLongClicked(); + + verify(mCompatUILayout).setSizeCompatHintVisibility(true /* show */); + } + +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java deleted file mode 100644 index 3a14a336190d3..0000000000000 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.sizecompatui; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; - -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.verify; - -import android.content.res.Configuration; -import android.testing.AndroidTestingRunner; -import android.view.LayoutInflater; -import android.widget.LinearLayout; - -import androidx.test.filters.SmallTest; - -import com.android.wm.shell.R; -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.ShellTestCase; -import com.android.wm.shell.common.DisplayLayout; -import com.android.wm.shell.common.SyncTransactionQueue; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -/** - * Tests for {@link SizeCompatHintPopup}. - * - * Build/Install/Run: - * atest WMShellUnitTests:SizeCompatHintPopupTest - */ -@RunWith(AndroidTestingRunner.class) -@SmallTest -public class SizeCompatHintPopupTest extends ShellTestCase { - - @Mock private SyncTransactionQueue mSyncTransactionQueue; - @Mock private SizeCompatUIController.SizeCompatUICallback mCallback; - @Mock private ShellTaskOrganizer.TaskListener mTaskListener; - @Mock private DisplayLayout mDisplayLayout; - - private SizeCompatUILayout mLayout; - private SizeCompatHintPopup mHint; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - final int taskId = 1; - mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mCallback, mContext, - new Configuration(), taskId, mTaskListener, mDisplayLayout, - false /* hasShownHint */); - mHint = (SizeCompatHintPopup) - LayoutInflater.from(mContext).inflate(R.layout.size_compat_mode_hint, null); - mHint.inject(mLayout); - - spyOn(mLayout); - } - - @Test - public void testOnClick() { - doNothing().when(mLayout).dismissHint(); - - final LinearLayout hintPopup = mHint.findViewById(R.id.size_compat_hint_popup); - hintPopup.performClick(); - - verify(mLayout).dismissHint(); - } -} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java deleted file mode 100644 index eb9305b2e9957..0000000000000 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.sizecompatui; - -import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import android.content.res.Configuration; -import android.graphics.Rect; -import android.testing.AndroidTestingRunner; -import android.view.DisplayInfo; -import android.view.InsetsSource; -import android.view.InsetsState; -import android.view.SurfaceControl; -import android.view.View; - -import androidx.test.filters.SmallTest; - -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.ShellTestCase; -import com.android.wm.shell.common.DisplayLayout; -import com.android.wm.shell.common.SyncTransactionQueue; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -/** - * Tests for {@link SizeCompatUILayout}. - * - * Build/Install/Run: - * atest WMShellUnitTests:SizeCompatUILayoutTest - */ -@RunWith(AndroidTestingRunner.class) -@SmallTest -public class SizeCompatUILayoutTest extends ShellTestCase { - - private static final int TASK_ID = 1; - - @Mock private SyncTransactionQueue mSyncTransactionQueue; - @Mock private SizeCompatUIController.SizeCompatUICallback mCallback; - @Mock private ShellTaskOrganizer.TaskListener mTaskListener; - @Mock private DisplayLayout mDisplayLayout; - @Mock private SizeCompatRestartButton mButton; - @Mock private SizeCompatHintPopup mHint; - private Configuration mTaskConfig; - - private SizeCompatUILayout mLayout; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mTaskConfig = new Configuration(); - - mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mCallback, mContext, - new Configuration(), TASK_ID, mTaskListener, new DisplayLayout(), - false /* hasShownHint */); - - spyOn(mLayout); - spyOn(mLayout.mButtonWindowManager); - doReturn(mButton).when(mLayout.mButtonWindowManager).createSizeCompatButton(); - - final SizeCompatUIWindowManager hintWindowManager = mLayout.createHintWindowManager(); - spyOn(hintWindowManager); - doReturn(mHint).when(hintWindowManager).createSizeCompatHint(); - doReturn(hintWindowManager).when(mLayout).createHintWindowManager(); - } - - @Test - public void testCreateSizeCompatButton() { - // Not create button if show is false. - mLayout.createSizeCompatButton(false /* show */); - - verify(mLayout.mButtonWindowManager, never()).createSizeCompatButton(); - assertNull(mLayout.mButton); - assertNull(mLayout.mHintWindowManager); - assertNull(mLayout.mHint); - - // Not create hint popup. - mLayout.mShouldShowHint = false; - mLayout.createSizeCompatButton(true /* show */); - - verify(mLayout.mButtonWindowManager).createSizeCompatButton(); - assertNotNull(mLayout.mButton); - assertNull(mLayout.mHintWindowManager); - assertNull(mLayout.mHint); - - // Create hint popup. - mLayout.release(); - mLayout.mShouldShowHint = true; - mLayout.createSizeCompatButton(true /* show */); - - verify(mLayout.mButtonWindowManager, times(2)).createSizeCompatButton(); - assertNotNull(mLayout.mButton); - assertNotNull(mLayout.mHintWindowManager); - verify(mLayout.mHintWindowManager).createSizeCompatHint(); - assertNotNull(mLayout.mHint); - assertFalse(mLayout.mShouldShowHint); - } - - @Test - public void testRelease() { - mLayout.createSizeCompatButton(true /* show */); - final SizeCompatUIWindowManager hintWindowManager = mLayout.mHintWindowManager; - - mLayout.release(); - - assertNull(mLayout.mButton); - assertNull(mLayout.mHint); - verify(hintWindowManager).release(); - assertNull(mLayout.mHintWindowManager); - verify(mLayout.mButtonWindowManager).release(); - } - - @Test - public void testUpdateSizeCompatInfo() { - mLayout.createSizeCompatButton(true /* show */); - - // No diff - clearInvocations(mLayout); - mLayout.updateSizeCompatInfo(mTaskConfig, mTaskListener, true /* show */); - - verify(mLayout, never()).updateButtonSurfacePosition(); - verify(mLayout, never()).release(); - verify(mLayout, never()).createSizeCompatButton(anyBoolean()); - - // Change task listener, recreate button. - clearInvocations(mLayout); - final ShellTaskOrganizer.TaskListener newTaskListener = mock( - ShellTaskOrganizer.TaskListener.class); - mLayout.updateSizeCompatInfo(mTaskConfig, newTaskListener, - true /* show */); - - verify(mLayout).release(); - verify(mLayout).createSizeCompatButton(anyBoolean()); - - // Change task bounds, update position. - clearInvocations(mLayout); - final Configuration newTaskConfiguration = new Configuration(); - newTaskConfiguration.windowConfiguration.setBounds(new Rect(0, 1000, 0, 2000)); - mLayout.updateSizeCompatInfo(newTaskConfiguration, newTaskListener, - true /* show */); - - verify(mLayout).updateButtonSurfacePosition(); - verify(mLayout).updateHintSurfacePosition(); - } - - @Test - public void testUpdateDisplayLayout() { - final DisplayInfo displayInfo = new DisplayInfo(); - displayInfo.logicalWidth = 1000; - displayInfo.logicalHeight = 2000; - final DisplayLayout displayLayout1 = new DisplayLayout(displayInfo, - mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false); - - mLayout.updateDisplayLayout(displayLayout1); - verify(mLayout).updateButtonSurfacePosition(); - verify(mLayout).updateHintSurfacePosition(); - - // No update if the display bounds is the same. - clearInvocations(mLayout); - final DisplayLayout displayLayout2 = new DisplayLayout(displayInfo, - mContext.getResources(), /* hasNavigationBar= */ false, /* hasStatusBar= */ false); - mLayout.updateDisplayLayout(displayLayout2); - verify(mLayout, never()).updateButtonSurfacePosition(); - verify(mLayout, never()).updateHintSurfacePosition(); - } - - @Test - public void testUpdateDisplayLayoutInsets() { - final DisplayInfo displayInfo = new DisplayInfo(); - displayInfo.logicalWidth = 1000; - displayInfo.logicalHeight = 2000; - final DisplayLayout displayLayout = new DisplayLayout(displayInfo, - mContext.getResources(), /* hasNavigationBar= */ true, /* hasStatusBar= */ false); - - mLayout.updateDisplayLayout(displayLayout); - verify(mLayout).updateButtonSurfacePosition(); - verify(mLayout).updateHintSurfacePosition(); - - // Update if the insets change on the existing display layout - clearInvocations(mLayout); - InsetsState insetsState = new InsetsState(); - InsetsSource insetsSource = new InsetsSource(ITYPE_EXTRA_NAVIGATION_BAR); - insetsSource.setFrame(0, 0, 1000, 1000); - insetsState.addSource(insetsSource); - displayLayout.setInsets(mContext.getResources(), insetsState); - mLayout.updateDisplayLayout(displayLayout); - verify(mLayout).updateButtonSurfacePosition(); - verify(mLayout).updateHintSurfacePosition(); - } - - @Test - public void testUpdateVisibility() { - // Create button if it is not created. - mLayout.mButton = null; - mLayout.updateVisibility(true /* show */); - - verify(mLayout).createSizeCompatButton(true /* show */); - - // Hide button. - clearInvocations(mLayout); - doReturn(View.VISIBLE).when(mButton).getVisibility(); - mLayout.updateVisibility(false /* show */); - - verify(mLayout, never()).createSizeCompatButton(anyBoolean()); - verify(mButton).setVisibility(View.GONE); - - // Show button. - doReturn(View.GONE).when(mButton).getVisibility(); - mLayout.updateVisibility(true /* show */); - - verify(mLayout, never()).createSizeCompatButton(anyBoolean()); - verify(mButton).setVisibility(View.VISIBLE); - } - - @Test - public void testAttachToParentSurface() { - final SurfaceControl.Builder b = new SurfaceControl.Builder(); - mLayout.attachToParentSurface(b); - - verify(mTaskListener).attachChildSurfaceToTask(TASK_ID, b); - } - - @Test - public void testOnRestartButtonClicked() { - mLayout.onRestartButtonClicked(); - - verify(mCallback).onSizeCompatRestartButtonClicked(TASK_ID); - } - - @Test - public void testOnRestartButtonLongClicked_showHint() { - mLayout.dismissHint(); - - assertNull(mLayout.mHint); - - mLayout.onRestartButtonLongClicked(); - - assertNotNull(mLayout.mHint); - } - - @Test - public void testDismissHint() { - mLayout.onRestartButtonLongClicked(); - final SizeCompatUIWindowManager hintWindowManager = mLayout.mHintWindowManager; - assertNotNull(mLayout.mHint); - assertNotNull(hintWindowManager); - - mLayout.dismissHint(); - - assertNull(mLayout.mHint); - assertNull(mLayout.mHintWindowManager); - verify(hintWindowManager).release(); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index bb0e79fe23a85..db88569382a78 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -121,7 +121,7 @@ public class SystemUIFactory { .setDisplayAreaHelper(mWMComponent.getDisplayAreaHelper()) .setTaskSurfaceHelper(mWMComponent.getTaskSurfaceHelper()) .setRecentTasks(mWMComponent.getRecentTasks()) - .setSizeCompatUI(Optional.of(mWMComponent.getSizeCompatUI())) + .setCompatUI(Optional.of(mWMComponent.getCompatUI())) .setDragAndDrop(Optional.of(mWMComponent.getDragAndDrop())); } else { // TODO: Call on prepareSysUIComponentBuilder but not with real components. Other option @@ -141,7 +141,7 @@ public class SystemUIFactory { .setStartingSurface(Optional.ofNullable(null)) .setTaskSurfaceHelper(Optional.ofNullable(null)) .setRecentTasks(Optional.ofNullable(null)) - .setSizeCompatUI(Optional.ofNullable(null)) + .setCompatUI(Optional.ofNullable(null)) .setDragAndDrop(Optional.ofNullable(null)); } mSysUIComponent = builder.build(); diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index c5d3a70676d00..d950d6d8c07e1 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -31,6 +31,7 @@ import com.android.wm.shell.ShellCommandHandler; import com.android.wm.shell.TaskViewFactory; import com.android.wm.shell.apppairs.AppPairs; import com.android.wm.shell.bubbles.Bubbles; +import com.android.wm.shell.compatui.CompatUI; import com.android.wm.shell.displayareahelper.DisplayAreaHelper; import com.android.wm.shell.draganddrop.DragAndDrop; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; @@ -38,7 +39,6 @@ import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.recents.RecentTasks; -import com.android.wm.shell.sizecompatui.SizeCompatUI; import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.startingsurface.StartingSurface; import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelper; @@ -110,7 +110,7 @@ public interface SysUIComponent { Builder setRecentTasks(Optional r); @BindsInstance - Builder setSizeCompatUI(Optional s); + Builder setCompatUI(Optional s); @BindsInstance Builder setDragAndDrop(Optional d); diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java index 90a3ad225f517..b815d4e9884b6 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java @@ -25,6 +25,7 @@ import com.android.wm.shell.ShellInit; import com.android.wm.shell.TaskViewFactory; import com.android.wm.shell.apppairs.AppPairs; import com.android.wm.shell.bubbles.Bubbles; +import com.android.wm.shell.compatui.CompatUI; import com.android.wm.shell.dagger.TvWMShellModule; import com.android.wm.shell.dagger.WMShellModule; import com.android.wm.shell.dagger.WMSingleton; @@ -35,7 +36,6 @@ import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.recents.RecentTasks; -import com.android.wm.shell.sizecompatui.SizeCompatUI; import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.startingsurface.StartingSurface; import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelper; @@ -119,7 +119,7 @@ public interface WMComponent { Optional getRecentTasks(); @WMSingleton - SizeCompatUI getSizeCompatUI(); + CompatUI getCompatUI(); @WMSingleton DragAndDrop getDragAndDrop(); diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index b546edf428b8f..1fd47a4656238 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -56,6 +56,7 @@ import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.tracing.ProtoTracer; import com.android.systemui.tracing.nano.SystemUiTraceProto; import com.android.wm.shell.ShellCommandHandler; +import com.android.wm.shell.compatui.CompatUI; import com.android.wm.shell.draganddrop.DragAndDrop; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; @@ -66,7 +67,6 @@ import com.android.wm.shell.onehanded.OneHandedTransitionCallback; import com.android.wm.shell.onehanded.OneHandedUiEventLogger; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.protolog.ShellProtoLogImpl; -import com.android.wm.shell.sizecompatui.SizeCompatUI; import com.android.wm.shell.splitscreen.SplitScreen; import java.io.FileDescriptor; @@ -114,7 +114,7 @@ public final class WMShell extends SystemUI private final Optional mOneHandedOptional; private final Optional mHideDisplayCutoutOptional; private final Optional mShellCommandHandler; - private final Optional mSizeCompatUIOptional; + private final Optional mCompatUIOptional; private final Optional mDragAndDropOptional; private final CommandQueue mCommandQueue; @@ -132,7 +132,7 @@ public final class WMShell extends SystemUI private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback; private KeyguardUpdateMonitorCallback mPipKeyguardCallback; private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback; - private KeyguardUpdateMonitorCallback mSizeCompatUIKeyguardCallback; + private KeyguardUpdateMonitorCallback mCompatUIKeyguardCallback; private WakefulnessLifecycle.Observer mWakefulnessObserver; @Inject @@ -143,7 +143,7 @@ public final class WMShell extends SystemUI Optional oneHandedOptional, Optional hideDisplayCutoutOptional, Optional shellCommandHandler, - Optional sizeCompatUIOptional, + Optional sizeCompatUIOptional, Optional dragAndDropOptional, CommandQueue commandQueue, ConfigurationController configurationController, @@ -169,7 +169,7 @@ public final class WMShell extends SystemUI mWakefulnessLifecycle = wakefulnessLifecycle; mProtoTracer = protoTracer; mShellCommandHandler = shellCommandHandler; - mSizeCompatUIOptional = sizeCompatUIOptional; + mCompatUIOptional = sizeCompatUIOptional; mDragAndDropOptional = dragAndDropOptional; mSysUiMainExecutor = sysUiMainExecutor; } @@ -185,7 +185,7 @@ public final class WMShell extends SystemUI mSplitScreenOptional.ifPresent(this::initSplitScreen); mOneHandedOptional.ifPresent(this::initOneHanded); mHideDisplayCutoutOptional.ifPresent(this::initHideDisplayCutout); - mSizeCompatUIOptional.ifPresent(this::initSizeCompatUi); + mCompatUIOptional.ifPresent(this::initCompatUi); mDragAndDropOptional.ifPresent(this::initDragAndDrop); } @@ -391,14 +391,14 @@ public final class WMShell extends SystemUI } @VisibleForTesting - void initSizeCompatUi(SizeCompatUI sizeCompatUI) { - mSizeCompatUIKeyguardCallback = new KeyguardUpdateMonitorCallback() { + void initCompatUi(CompatUI sizeCompatUI) { + mCompatUIKeyguardCallback = new KeyguardUpdateMonitorCallback() { @Override public void onKeyguardOccludedChanged(boolean occluded) { sizeCompatUI.onKeyguardOccludedChanged(occluded); } }; - mKeyguardUpdateMonitor.registerCallback(mSizeCompatUIKeyguardCallback); + mKeyguardUpdateMonitor.registerCallback(mCompatUIKeyguardCallback); } void initDragAndDrop(DragAndDrop dragAndDrop) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java index 1e15d2ae0bdb2..2f2e536322db2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java @@ -35,6 +35,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.tracing.ProtoTracer; import com.android.wm.shell.ShellCommandHandler; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.compatui.CompatUI; import com.android.wm.shell.draganddrop.DragAndDrop; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; @@ -42,7 +43,6 @@ import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.onehanded.OneHandedEventCallback; import com.android.wm.shell.onehanded.OneHandedTransitionCallback; import com.android.wm.shell.pip.Pip; -import com.android.wm.shell.sizecompatui.SizeCompatUI; import com.android.wm.shell.splitscreen.SplitScreen; import org.junit.Before; @@ -78,7 +78,7 @@ public class WMShellTest extends SysuiTestCase { @Mock WakefulnessLifecycle mWakefulnessLifecycle; @Mock ProtoTracer mProtoTracer; @Mock ShellCommandHandler mShellCommandHandler; - @Mock SizeCompatUI mSizeCompatUI; + @Mock CompatUI mCompatUI; @Mock ShellExecutor mSysUiMainExecutor; @Mock DragAndDrop mDragAndDrop; @@ -88,7 +88,7 @@ public class WMShellTest extends SysuiTestCase { mWMShell = new WMShell(mContext, Optional.of(mPip), Optional.of(mLegacySplitScreen), Optional.of(mSplitScreen), Optional.of(mOneHanded), Optional.of(mHideDisplayCutout), - Optional.of(mShellCommandHandler), Optional.of(mSizeCompatUI), + Optional.of(mShellCommandHandler), Optional.of(mCompatUI), Optional.of(mDragAndDrop), mCommandQueue, mConfigurationController, mKeyguardUpdateMonitor, mNavigationModeController, mScreenLifecycle, mSysUiState, mProtoTracer, @@ -136,8 +136,8 @@ public class WMShellTest extends SysuiTestCase { } @Test - public void initSizeCompatUI_registersCallbacks() { - mWMShell.initSizeCompatUi(mSizeCompatUI); + public void initCompatUI_registersCallbacks() { + mWMShell.initCompatUi(mCompatUI); verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class)); }