diff options
15 files changed, 623 insertions, 39 deletions
diff --git a/libs/WindowManager/Shell/res/drawable/caption_desktop_button.xml b/libs/WindowManager/Shell/res/drawable/caption_desktop_button.xml new file mode 100644 index 000000000000..8779cc09715b --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/caption_desktop_button.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2022 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. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="32.0dp" + android:height="32.0dp" + android:viewportWidth="32.0" + android:viewportHeight="32.0" +> + <group android:scaleX="0.5" + android:scaleY="0.5" + android:translateX="6.0" + android:translateY="6.0"> + <path + android:fillColor="@android:color/black" + android:pathData="M5.958,37.708Q4.458,37.708 3.354,36.604Q2.25,35.5 2.25,34V18.292Q2.25,16.792 3.354,15.688Q4.458,14.583 5.958,14.583H9.5V5.958Q9.5,4.458 10.625,3.354Q11.75,2.25 13.208,2.25H34Q35.542,2.25 36.646,3.354Q37.75,4.458 37.75,5.958V21.667Q37.75,23.167 36.646,24.271Q35.542,25.375 34,25.375H30.5V34Q30.5,35.5 29.396,36.604Q28.292,37.708 26.792,37.708ZM5.958,34H26.792Q26.792,34 26.792,34Q26.792,34 26.792,34V21.542H5.958V34Q5.958,34 5.958,34Q5.958,34 5.958,34ZM30.5,21.667H34Q34,21.667 34,21.667Q34,21.667 34,21.667V9.208H13.208V14.583H26.833Q28.375,14.583 29.438,15.667Q30.5,16.75 30.5,18.25Z"/> + </group> +</vector> diff --git a/libs/WindowManager/Shell/res/drawable/caption_floating_button.xml b/libs/WindowManager/Shell/res/drawable/caption_floating_button.xml new file mode 100644 index 000000000000..ea0fbb0e5d33 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/caption_floating_button.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2022 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. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="32.0dp" + android:height="32.0dp" + android:viewportWidth="32.0" + android:viewportHeight="32.0" +> + <group android:scaleX="0.5" + android:scaleY="0.5" + android:translateX="6.0" + android:translateY="6.0"> + <path + android:fillColor="@android:color/black" + android:pathData="M18.167,21.875H29.833V10.208H18.167ZM7.875,35.833Q6.375,35.833 5.271,34.729Q4.167,33.625 4.167,32.125V7.875Q4.167,6.375 5.271,5.271Q6.375,4.167 7.875,4.167H32.125Q33.625,4.167 34.729,5.271Q35.833,6.375 35.833,7.875V32.125Q35.833,33.625 34.729,34.729Q33.625,35.833 32.125,35.833ZM7.875,32.125H32.125Q32.125,32.125 32.125,32.125Q32.125,32.125 32.125,32.125V7.875Q32.125,7.875 32.125,7.875Q32.125,7.875 32.125,7.875H7.875Q7.875,7.875 7.875,7.875Q7.875,7.875 7.875,7.875V32.125Q7.875,32.125 7.875,32.125Q7.875,32.125 7.875,32.125ZM7.875,7.875Q7.875,7.875 7.875,7.875Q7.875,7.875 7.875,7.875V32.125Q7.875,32.125 7.875,32.125Q7.875,32.125 7.875,32.125Q7.875,32.125 7.875,32.125Q7.875,32.125 7.875,32.125V7.875Q7.875,7.875 7.875,7.875Q7.875,7.875 7.875,7.875Z"/> + </group> +</vector> diff --git a/libs/WindowManager/Shell/res/drawable/caption_fullscreen_button.xml b/libs/WindowManager/Shell/res/drawable/caption_fullscreen_button.xml new file mode 100644 index 000000000000..c55cbe2d054c --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/caption_fullscreen_button.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2022 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. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="32.0dp" + android:height="32.0dp" + android:viewportWidth="32.0" + android:viewportHeight="32.0" +> + <group android:scaleX="0.5" + android:scaleY="0.5" + android:translateX="6.0" + android:translateY="6.0"> + <path + android:fillColor="@android:color/black" + android:pathData="M34.042,14.625V9.333Q34.042,9.333 34.042,9.333Q34.042,9.333 34.042,9.333H28.708V5.708H33.917Q35.458,5.708 36.562,6.833Q37.667,7.958 37.667,9.458V14.625ZM2.375,14.625V9.458Q2.375,7.958 3.479,6.833Q4.583,5.708 6.125,5.708H11.292V9.333H6Q6,9.333 6,9.333Q6,9.333 6,9.333V14.625ZM28.708,34.25V30.667H34.042Q34.042,30.667 34.042,30.667Q34.042,30.667 34.042,30.667V25.333H37.667V30.542Q37.667,32 36.562,33.125Q35.458,34.25 33.917,34.25ZM6.125,34.25Q4.583,34.25 3.479,33.125Q2.375,32 2.375,30.542V25.333H6V30.667Q6,30.667 6,30.667Q6,30.667 6,30.667H11.292V34.25ZM9.333,27.292V12.667H30.708V27.292ZM12.917,23.708H27.125V16.25H12.917ZM12.917,23.708V16.25V23.708Z"/> + </group> +</vector> diff --git a/libs/WindowManager/Shell/res/drawable/caption_more_button.xml b/libs/WindowManager/Shell/res/drawable/caption_more_button.xml new file mode 100644 index 000000000000..447df43dfddd --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/caption_more_button.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2022 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. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="32.0dp" + android:height="32.0dp" + android:viewportWidth="32.0" + android:viewportHeight="32.0" +> + <group android:scaleX="0.5" + android:scaleY="0.5" + android:translateX="6.0" + android:translateY="6.0"> + <path + android:fillColor="@android:color/black" + android:pathData="M8.083,22.833Q6.917,22.833 6.104,22Q5.292,21.167 5.292,20Q5.292,18.833 6.125,18Q6.958,17.167 8.125,17.167Q9.292,17.167 10.125,18Q10.958,18.833 10.958,20Q10.958,21.167 10.125,22Q9.292,22.833 8.083,22.833ZM20,22.833Q18.833,22.833 18,22Q17.167,21.167 17.167,20Q17.167,18.833 18,18Q18.833,17.167 20,17.167Q21.167,17.167 22,18Q22.833,18.833 22.833,20Q22.833,21.167 22,22Q21.167,22.833 20,22.833ZM31.875,22.833Q30.708,22.833 29.875,22Q29.042,21.167 29.042,20Q29.042,18.833 29.875,18Q30.708,17.167 31.917,17.167Q33.083,17.167 33.896,18Q34.708,18.833 34.708,20Q34.708,21.167 33.875,22Q33.042,22.833 31.875,22.833Z"/> + </group> +</vector> diff --git a/libs/WindowManager/Shell/res/drawable/caption_split_screen_button.xml b/libs/WindowManager/Shell/res/drawable/caption_split_screen_button.xml new file mode 100644 index 000000000000..c334a543a86a --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/caption_split_screen_button.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2022 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. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="32.0dp" + android:height="32.0dp" + android:viewportWidth="32.0" + android:viewportHeight="32.0" +> + <group android:translateX="6.0" + android:translateY="8.0"> + <path + android:fillColor="@android:color/black" + android:pathData="M18 14L13 14L13 2L18 2L18 14ZM20 14L20 2C20 0.9 19.1 -3.93402e-08 18 -8.74228e-08L13 -3.0598e-07C11.9 -3.54062e-07 11 0.9 11 2L11 14C11 15.1 11.9 16 13 16L18 16C19.1 16 20 15.1 20 14ZM7 14L2 14L2 2L7 2L7 14ZM9 14L9 2C9 0.9 8.1 -5.20166e-07 7 -5.68248e-07L2 -7.86805e-07C0.9 -8.34888e-07 -3.93403e-08 0.9 -8.74228e-08 2L-6.11959e-07 14C-6.60042e-07 15.1 0.9 16 2 16L7 16C8.1 16 9 15.1 9 14Z"/> </group> +</vector> diff --git a/libs/WindowManager/Shell/res/drawable/handle_menu_background.xml b/libs/WindowManager/Shell/res/drawable/handle_menu_background.xml new file mode 100644 index 000000000000..e307f007e4a4 --- /dev/null +++ b/libs/WindowManager/Shell/res/drawable/handle_menu_background.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2022 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. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="210.0dp" + android:height="64.0dp" + android:tint="@color/decor_button_light_color" +> + <group android:scaleX="0.5" + android:scaleY="0.5" + android:translateX="8.0" + android:translateY="8.0" > + <path + android:fillColor="@android:color/white" + android:pathData="M18.3334 14L13.3334 14L13.3334 2L18.3334 2L18.3334 14ZM20.3334 14L20.3334 2C20.3334 0.9 19.4334 -3.93402e-08 18.3334 -8.74228e-08L13.3334 -3.0598e-07C12.2334 -3.54062e-07 11.3334 0.9 11.3334 2L11.3334 14C11.3334 15.1 12.2334 16 13.3334 16L18.3334 16C19.4334 16 20.3334 15.1 20.3334 14ZM7.33337 14L2.33337 14L2.33337 2L7.33337 2L7.33337 14ZM9.33337 14L9.33337 2C9.33337 0.899999 8.43337 -5.20166e-07 7.33337 -5.68248e-07L2.33337 -7.86805e-07C1.23337 -8.34888e-07 0.333374 0.899999 0.333374 2L0.333373 14C0.333373 15.1 1.23337 16 2.33337 16L7.33337 16C8.43337 16 9.33337 15.1 9.33337 14Z"/> + </group> +</vector> diff --git a/libs/WindowManager/Shell/res/layout/caption_handle_menu.xml b/libs/WindowManager/Shell/res/layout/caption_handle_menu.xml new file mode 100644 index 000000000000..d9a140b810f8 --- /dev/null +++ b/libs/WindowManager/Shell/res/layout/caption_handle_menu.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="utf-8"?> + <!-- + ~ Copyright (C) 2022 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. + --> +<com.android.wm.shell.windowdecor.WindowDecorLinearLayout +xmlns:android="http://schemas.android.com/apk/res/android" +android:id="@+id/handle_menu" +android:layout_width="wrap_content" +android:layout_height="wrap_content" +android:gravity="center_horizontal" +android:background="@drawable/decor_caption_title"> + <Button + style="@style/CaptionButtonStyle" + android:id="@+id/fullscreen_button" + android:contentDescription="@string/fullscreen_text" + android:background="@drawable/caption_fullscreen_button"/> + <Button + style="@style/CaptionButtonStyle" + android:id="@+id/split_screen_button" + android:contentDescription="@string/split_screen_text" + android:background="@drawable/caption_split_screen_button"/> + <Button + style="@style/CaptionButtonStyle" + android:id="@+id/floating_button" + android:contentDescription="@string/float_button_text" + android:background="@drawable/caption_floating_button"/> + <Button + style="@style/CaptionButtonStyle" + android:id="@+id/desktop_button" + android:contentDescription="@string/desktop_text" + android:background="@drawable/caption_desktop_button"/> + <Button + style="@style/CaptionButtonStyle" + android:id="@+id/more_button" + android:contentDescription="@string/more_button_text" + android:background="@drawable/caption_more_button"/> +</com.android.wm.shell.windowdecor.WindowDecorLinearLayout>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/layout/caption_window_decoration.xml b/libs/WindowManager/Shell/res/layout/caption_window_decoration.xml index 38cd5702f134..51e634c17532 100644 --- a/libs/WindowManager/Shell/res/layout/caption_window_decoration.xml +++ b/libs/WindowManager/Shell/res/layout/caption_window_decoration.xml @@ -19,14 +19,10 @@ android:id="@+id/caption" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:gravity="center_horizontal" android:background="@drawable/decor_caption_title"> <Button + style="@style/CaptionButtonStyle" android:id="@+id/back_button" - android:layout_width="32dp" - android:layout_height="32dp" - android:layout_margin="5dp" - android:padding="4dp" android:contentDescription="@string/back_button_text" android:background="@drawable/decor_back_button_dark" /> @@ -39,11 +35,8 @@ android:contentDescription="@string/handle_text" android:background="@drawable/decor_handle_dark"/> <Button + style="@style/CaptionButtonStyle" android:id="@+id/close_window" - android:layout_width="32dp" - android:layout_height="32dp" - android:layout_margin="5dp" - android:padding="4dp" android:contentDescription="@string/close_button_text" android:background="@drawable/decor_close_button_dark"/> </com.android.wm.shell.windowdecor.WindowDecorLinearLayout>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml index d8a507469722..9fab3a1eb4b9 100644 --- a/libs/WindowManager/Shell/res/values/strings.xml +++ b/libs/WindowManager/Shell/res/values/strings.xml @@ -197,4 +197,14 @@ <string name="back_button_text">Back</string> <!-- Accessibility text for the caption handle [CHAR LIMIT=NONE] --> <string name="handle_text">Handle</string> + <!-- Accessibility text for the handle fullscreen button [CHAR LIMIT=NONE] --> + <string name="fullscreen_text">Fullscreen</string> + <!-- Accessibility text for the handle desktop button [CHAR LIMIT=NONE] --> + <string name="desktop_text">Desktop Mode</string> + <!-- Accessibility text for the handle split screen button [CHAR LIMIT=NONE] --> + <string name="split_screen_text">Split Screen</string> + <!-- Accessibility text for the handle more options button [CHAR LIMIT=NONE] --> + <string name="more_button_text">More</string> + <!-- Accessibility text for the handle floating window button [CHAR LIMIT=NONE] --> + <string name="float_button_text">Float</string> </resources> diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml index 19f7c3ef4364..a8597210d72e 100644 --- a/libs/WindowManager/Shell/res/values/styles.xml +++ b/libs/WindowManager/Shell/res/values/styles.xml @@ -30,6 +30,13 @@ <item name="android:activityCloseExitAnimation">@anim/forced_resizable_exit</item> </style> + <style name="CaptionButtonStyle"> + <item name="android:layout_width">32dp</item> + <item name="android:layout_height">32dp</item> + <item name="android:layout_margin">5dp</item> + <item name="android:padding">4dp</item> + </style> + <style name="DockedDividerBackground"> <item name="android:layout_width">match_parent</item> <item name="android:layout_height">@dimen/split_divider_bar_width</item> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java index dca516a327b0..36dd8edaa8b7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java @@ -26,11 +26,16 @@ import android.app.ActivityTaskManager; import android.content.Context; import android.hardware.input.InputManager; import android.os.Handler; +import android.os.Looper; import android.os.SystemClock; import android.util.Log; import android.util.SparseArray; import android.view.Choreographer; +import android.view.InputChannel; import android.view.InputDevice; +import android.view.InputEvent; +import android.view.InputEventReceiver; +import android.view.InputMonitor; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.MotionEvent; @@ -64,8 +69,11 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { private final SyncTransactionQueue mSyncQueue; private FreeformTaskTransitionStarter mTransitionStarter; private DesktopModeController mDesktopModeController; + private EventReceiver mEventReceiver; + private InputMonitor mInputMonitor; private final SparseArray<CaptionWindowDecoration> mWindowDecorByTaskId = new SparseArray<>(); + private final DragStartListenerImpl mDragStartListener = new DragStartListenerImpl(); public CaptionWindowDecorViewModel( Context context, @@ -108,12 +116,19 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { mSyncQueue); mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration); - TaskPositioner taskPositioner = new TaskPositioner(mTaskOrganizer, windowDecoration); + TaskPositioner taskPositioner = new TaskPositioner(mTaskOrganizer, windowDecoration, + mDragStartListener); CaptionTouchEventListener touchEventListener = new CaptionTouchEventListener(taskInfo, taskPositioner); windowDecoration.setCaptionListeners(touchEventListener, touchEventListener); windowDecoration.setDragResizeCallback(taskPositioner); setupWindowDecorationForTransition(taskInfo, startT, finishT); + if (mInputMonitor == null) { + mInputMonitor = InputManager.getInstance().monitorGestureInput( + "caption-touch", mContext.getDisplayId()); + mEventReceiver = new EventReceiver( + mInputMonitor.getInputChannel(), Looper.myLooper()); + } return true; } @@ -165,6 +180,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { @Override public void onClick(View v) { + CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId); final int id = v.getId(); if (id == R.id.close_window) { WindowContainerTransaction wct = new WindowContainerTransaction(); @@ -176,6 +192,15 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { } } else if (id == R.id.back_button) { injectBackKey(); + } else if (id == R.id.caption_handle) { + decoration.createHandleMenu(); + } else if (id == R.id.desktop_button) { + mDesktopModeController.setDesktopModeActive(true); + decoration.closeHandleMenu(); + } else if (id == R.id.fullscreen_button) { + mDesktopModeController.setDesktopModeActive(false); + decoration.closeHandleMenu(); + decoration.setButtonVisibility(); } } private void injectBackKey() { @@ -257,6 +282,36 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { } } + // InputEventReceiver to listen for touch input outside of caption bounds + private class EventReceiver extends InputEventReceiver { + EventReceiver(InputChannel channel, Looper looper) { + super(channel, looper); + } + + @Override + public void onInputEvent(InputEvent event) { + boolean handled = false; + if (event instanceof MotionEvent + && ((MotionEvent) event).getActionMasked() == MotionEvent.ACTION_UP) { + handled = true; + CaptionWindowDecorViewModel.this.handleMotionEvent((MotionEvent) event); + } + finishInputEvent(event, handled); + } + } + + // If any input received is outside of caption bounds, turn off handle menu + private void handleMotionEvent(MotionEvent ev) { + int size = mWindowDecorByTaskId.size(); + for (int i = 0; i < size; i++) { + CaptionWindowDecoration decoration = mWindowDecorByTaskId.valueAt(i); + if (decoration != null) { + decoration.closeHandleMenuIfNeeded(ev); + } + } + } + + private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) { if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true; return DesktopModeStatus.IS_SUPPORTED @@ -264,4 +319,11 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { && mDisplayController.getDisplayContext(taskInfo.displayId) .getResources().getConfiguration().smallestScreenWidthDp >= 600; } + + private class DragStartListenerImpl implements TaskPositioner.DragStartListener{ + @Override + public void onDragStart(int taskId) { + mWindowDecorByTaskId.get(taskId).closeHandleMenu(); + } + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java index 9d61c14e1435..03cad043ed67 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java @@ -20,10 +20,14 @@ import android.app.ActivityManager; import android.app.WindowConfiguration; import android.content.Context; import android.content.res.ColorStateList; +import android.content.res.Resources; import android.graphics.Color; +import android.graphics.Point; +import android.graphics.Rect; import android.graphics.drawable.VectorDrawable; import android.os.Handler; import android.view.Choreographer; +import android.view.MotionEvent; import android.view.SurfaceControl; import android.view.View; import android.view.ViewConfiguration; @@ -58,6 +62,8 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL private boolean mDesktopActive; + private AdditionalWindow mHandleMenu; + CaptionWindowDecoration( Context context, DisplayController displayController, @@ -123,7 +129,20 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL if (isDragResizeable) { mRelayoutParams.setOutsets(outsetLeftId, outsetTopId, outsetRightId, outsetBottomId); } + final Resources resources = mDecorWindowContext.getResources(); + final Rect taskBounds = taskInfo.configuration.windowConfiguration.getBounds(); + final int captionHeight = loadDimensionPixelSize(resources, + mRelayoutParams.mCaptionHeightId); + final int captionWidth = loadDimensionPixelSize(resources, + mRelayoutParams.mCaptionWidthId); + final int captionLeft = taskBounds.width() / 2 + - captionWidth / 2; + final int captionTop = taskBounds.top + <= captionHeight / 2 ? 0 : -captionHeight / 2; + mRelayoutParams.setCaptionPosition(captionLeft, captionTop); + relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult); + taskInfo = null; // Clear it just in case we use it accidentally mTaskOrganizer.applyTransaction(wct); @@ -137,15 +156,14 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL } // If this task is not focused, do not show caption. - setCaptionVisibility(taskInfo.isFocused); + setCaptionVisibility(mTaskInfo.isFocused); // Only handle should show if Desktop Mode is inactive. boolean desktopCurrentStatus = DesktopModeStatus.isActive(mContext); - if (mDesktopActive != desktopCurrentStatus && taskInfo.isFocused) { + if (mDesktopActive != desktopCurrentStatus && mTaskInfo.isFocused) { mDesktopActive = desktopCurrentStatus; setButtonVisibility(); } - taskInfo = null; // Clear it just in case we use it accidentally if (!isDragResizeable) { closeDragResizeListener(); @@ -184,9 +202,22 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL back.setOnClickListener(mOnCaptionButtonClickListener); View handle = caption.findViewById(R.id.caption_handle); handle.setOnTouchListener(mOnCaptionTouchListener); + handle.setOnClickListener(mOnCaptionButtonClickListener); setButtonVisibility(); } + private void setupHandleMenu() { + View menu = mHandleMenu.mWindowViewHost.getView(); + View fullscreen = menu.findViewById(R.id.fullscreen_button); + fullscreen.setOnClickListener(mOnCaptionButtonClickListener); + View desktop = menu.findViewById(R.id.desktop_button); + desktop.setOnClickListener(mOnCaptionButtonClickListener); + View split = menu.findViewById(R.id.split_screen_button); + split.setOnClickListener(mOnCaptionButtonClickListener); + View more = menu.findViewById(R.id.more_button); + more.setOnClickListener(mOnCaptionButtonClickListener); + } + /** * Sets caption visibility based on task focus. * @@ -194,8 +225,9 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL */ private void setCaptionVisibility(boolean visible) { int v = visible ? View.VISIBLE : View.GONE; - View caption = mResult.mRootView.findViewById(R.id.caption); - caption.setVisibility(v); + View captionView = mResult.mRootView.findViewById(R.id.caption); + captionView.setVisibility(v); + if (!visible) closeHandleMenu(); } /** @@ -203,6 +235,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL * */ public void setButtonVisibility() { + mDesktopActive = DesktopModeStatus.isActive(mContext); int v = mDesktopActive ? View.VISIBLE : View.GONE; View caption = mResult.mRootView.findViewById(R.id.caption); View back = caption.findViewById(R.id.back_button); @@ -220,6 +253,10 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL caption.getBackground().setTint(v == View.VISIBLE ? Color.WHITE : Color.TRANSPARENT); } + public boolean isHandleMenuActive() { + return mHandleMenu != null; + } + private void closeDragResizeListener() { if (mDragResizeListener == null) { return; @@ -228,9 +265,67 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL mDragResizeListener = null; } + /** + * Create and display handle menu window + */ + public void createHandleMenu() { + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + final Resources resources = mDecorWindowContext.getResources(); + int x = mRelayoutParams.mCaptionX; + int y = mRelayoutParams.mCaptionY; + int width = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionWidthId); + int height = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionHeightId); + String namePrefix = "Caption Menu"; + mHandleMenu = addWindow(R.layout.caption_handle_menu, namePrefix, t, + x - mResult.mDecorContainerOffsetX, y - mResult.mDecorContainerOffsetY, + width, height); + mSyncQueue.runInSync(transaction -> { + transaction.merge(t); + t.close(); + }); + setupHandleMenu(); + } + + /** + * Close the handle menu window + */ + public void closeHandleMenu() { + if (!isHandleMenuActive()) return; + mHandleMenu.releaseView(); + mHandleMenu = null; + } + + @Override + void releaseViews() { + closeHandleMenu(); + super.releaseViews(); + } + + /** + * Close an open handle menu if input is outside of menu coordinates + * @param ev the tapped point to compare against + * @return + */ + public void closeHandleMenuIfNeeded(MotionEvent ev) { + if (mHandleMenu != null) { + Point positionInParent = mTaskOrganizer.getRunningTaskInfo(mTaskInfo.taskId) + .positionInParent; + final Resources resources = mDecorWindowContext.getResources(); + ev.offsetLocation(-mRelayoutParams.mCaptionX, -mRelayoutParams.mCaptionY); + ev.offsetLocation(-positionInParent.x, -positionInParent.y); + int width = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionWidthId); + int height = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionHeightId); + if (!(ev.getX() >= 0 && ev.getY() >= 0 + && ev.getX() <= width && ev.getY() <= height)) { + closeHandleMenu(); + } + } + } + @Override public void close() { closeDragResizeListener(); + closeHandleMenu(); super.close(); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java index 27c10114ac0e..f0f2db7ded80 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskPositioner.java @@ -42,14 +42,18 @@ class TaskPositioner implements DragResizeCallback { private final Rect mResizeTaskBounds = new Rect(); private int mCtrlType; + private DragStartListener mDragStartListener; - TaskPositioner(ShellTaskOrganizer taskOrganizer, WindowDecoration windowDecoration) { + TaskPositioner(ShellTaskOrganizer taskOrganizer, WindowDecoration windowDecoration, + DragStartListener dragStartListener) { mTaskOrganizer = taskOrganizer; mWindowDecoration = windowDecoration; + mDragStartListener = dragStartListener; } @Override public void onDragResizeStart(int ctrlType, float x, float y) { + mDragStartListener.onDragStart(mWindowDecoration.mTaskInfo.taskId); mCtrlType = ctrlType; mTaskBoundsAtDragStart.set( @@ -97,4 +101,12 @@ class TaskPositioner implements DragResizeCallback { mTaskOrganizer.applyTransaction(wct); } } + + interface DragStartListener { + /** + * Inform the implementing class that a drag resize has started + * @param taskId id of this positioner's {@link WindowDecoration} + */ + void onDragStart(int taskId); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java index b314163802ca..7ecb3f3f6355 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java @@ -200,16 +200,17 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> final Rect taskBounds = taskConfig.windowConfiguration.getBounds(); final Resources resources = mDecorWindowContext.getResources(); - final int decorContainerOffsetX = -loadDimensionPixelSize(resources, params.mOutsetLeftId); - final int decorContainerOffsetY = -loadDimensionPixelSize(resources, params.mOutsetTopId); + outResult.mDecorContainerOffsetX = -loadDimensionPixelSize(resources, params.mOutsetLeftId); + outResult.mDecorContainerOffsetY = -loadDimensionPixelSize(resources, params.mOutsetTopId); outResult.mWidth = taskBounds.width() + loadDimensionPixelSize(resources, params.mOutsetRightId) - - decorContainerOffsetX; + - outResult.mDecorContainerOffsetX; outResult.mHeight = taskBounds.height() + loadDimensionPixelSize(resources, params.mOutsetBottomId) - - decorContainerOffsetY; + - outResult.mDecorContainerOffsetY; startT.setPosition( - mDecorationContainerSurface, decorContainerOffsetX, decorContainerOffsetY) + mDecorationContainerSurface, + outResult.mDecorContainerOffsetX, outResult.mDecorContainerOffsetY) .setWindowCrop(mDecorationContainerSurface, outResult.mWidth, outResult.mHeight) // TODO(b/244455401): Change the z-order when it's better organized @@ -252,14 +253,11 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> final int captionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId); final int captionWidth = loadDimensionPixelSize(resources, params.mCaptionWidthId); - //Prevent caption from going offscreen if task is too high up - final int captionYPos = taskBounds.top <= captionHeight / 2 ? 0 : captionHeight / 2; - startT.setPosition( - mCaptionContainerSurface, -decorContainerOffsetX - + taskBounds.width() / 2 - captionWidth / 2, - -decorContainerOffsetY - captionYPos) - .setWindowCrop(mCaptionContainerSurface, taskBounds.width(), captionHeight) + mCaptionContainerSurface, + -outResult.mDecorContainerOffsetX + params.mCaptionX, + -outResult.mDecorContainerOffsetY + params.mCaptionY) + .setWindowCrop(mCaptionContainerSurface, captionWidth, captionHeight) .show(mCaptionContainerSurface); if (mCaptionWindowManager == null) { @@ -292,7 +290,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> // Caption insets mCaptionInsetsRect.set(taskBounds); mCaptionInsetsRect.bottom = - mCaptionInsetsRect.top + captionHeight - captionYPos; + mCaptionInsetsRect.top + captionHeight + params.mCaptionY; wct.addRectInsetsProvider(mTaskInfo.token, mCaptionInsetsRect, CAPTION_INSETS_TYPES); } else { @@ -302,10 +300,10 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> // Task surface itself Point taskPosition = mTaskInfo.positionInParent; mTaskSurfaceCrop.set( - decorContainerOffsetX, - decorContainerOffsetY, - outResult.mWidth + decorContainerOffsetX, - outResult.mHeight + decorContainerOffsetY); + outResult.mDecorContainerOffsetX, + outResult.mDecorContainerOffsetY, + outResult.mWidth + outResult.mDecorContainerOffsetX, + outResult.mHeight + outResult.mDecorContainerOffsetY); startT.show(mTaskSurface); finishT.setPosition(mTaskSurface, taskPosition.x, taskPosition.y) .setCrop(mTaskSurface, mTaskSurfaceCrop); @@ -326,7 +324,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> return true; } - private void releaseViews() { + void releaseViews() { if (mViewHost != null) { mViewHost.release(); mViewHost = null; @@ -369,20 +367,60 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> releaseViews(); } - private static int loadDimensionPixelSize(Resources resources, int resourceId) { + static int loadDimensionPixelSize(Resources resources, int resourceId) { if (resourceId == Resources.ID_NULL) { return 0; } return resources.getDimensionPixelSize(resourceId); } - private static float loadDimension(Resources resources, int resourceId) { + static float loadDimension(Resources resources, int resourceId) { if (resourceId == Resources.ID_NULL) { return 0; } return resources.getDimension(resourceId); } + /** + * Create a window associated with this WindowDecoration. + * Note that subclass must dispose of this when the task is hidden/closed. + * @param layoutId layout to make the window from + * @param t the transaction to apply + * @param xPos x position of new window + * @param yPos y position of new window + * @param width width of new window + * @param height height of new window + * @return + */ + AdditionalWindow addWindow(int layoutId, String namePrefix, + SurfaceControl.Transaction t, int xPos, int yPos, int width, int height) { + final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get(); + SurfaceControl windowSurfaceControl = builder + .setName(namePrefix + " of Task=" + mTaskInfo.taskId) + .setContainerLayer() + .setParent(mDecorationContainerSurface) + .build(); + View v = LayoutInflater.from(mDecorWindowContext).inflate(layoutId, null); + + t.setPosition( + windowSurfaceControl, xPos, yPos) + .setWindowCrop(windowSurfaceControl, width, height) + .show(windowSurfaceControl); + final WindowManager.LayoutParams lp = + new WindowManager.LayoutParams(width, height, + WindowManager.LayoutParams.TYPE_APPLICATION, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT); + lp.setTitle("Additional window of Task=" + mTaskInfo.taskId); + lp.setTrustedOverlay(); + WindowlessWindowManager windowManager = new WindowlessWindowManager(mTaskInfo.configuration, + windowSurfaceControl, null /* hostInputToken */); + SurfaceControlViewHost viewHost = mSurfaceControlViewHostFactory + .create(mDecorWindowContext, mDisplay, windowManager); + viewHost.setView(v, lp); + return new AdditionalWindow(windowSurfaceControl, viewHost, + mSurfaceControlTransactionSupplier); + } + static class RelayoutParams{ RunningTaskInfo mRunningTaskInfo; int mLayoutResId; @@ -395,6 +433,9 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> int mOutsetLeftId; int mOutsetRightId; + int mCaptionX; + int mCaptionY; + void setOutsets(int leftId, int topId, int rightId, int bottomId) { mOutsetLeftId = leftId; mOutsetTopId = topId; @@ -402,6 +443,11 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mOutsetBottomId = bottomId; } + void setCaptionPosition(int left, int top) { + mCaptionX = left; + mCaptionY = top; + } + void reset() { mLayoutResId = Resources.ID_NULL; mCaptionHeightId = Resources.ID_NULL; @@ -412,6 +458,9 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mOutsetBottomId = Resources.ID_NULL; mOutsetLeftId = Resources.ID_NULL; mOutsetRightId = Resources.ID_NULL; + + mCaptionX = 0; + mCaptionY = 0; } } @@ -419,10 +468,14 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> int mWidth; int mHeight; T mRootView; + int mDecorContainerOffsetX; + int mDecorContainerOffsetY; void reset() { mWidth = 0; mHeight = 0; + mDecorContainerOffsetX = 0; + mDecorContainerOffsetY = 0; mRootView = null; } } @@ -432,4 +485,41 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> return new SurfaceControlViewHost(c, d, wmm); } } + + /** + * Subclass for additional windows associated with this WindowDecoration + */ + static class AdditionalWindow { + SurfaceControl mWindowSurface; + SurfaceControlViewHost mWindowViewHost; + Supplier<SurfaceControl.Transaction> mTransactionSupplier; + + private AdditionalWindow(SurfaceControl surfaceControl, + SurfaceControlViewHost surfaceControlViewHost, + Supplier<SurfaceControl.Transaction> transactionSupplier) { + mWindowSurface = surfaceControl; + mWindowViewHost = surfaceControlViewHost; + mTransactionSupplier = transactionSupplier; + } + + void releaseView() { + WindowlessWindowManager windowManager = mWindowViewHost.getWindowlessWM(); + + if (mWindowViewHost != null) { + mWindowViewHost.release(); + mWindowViewHost = null; + } + windowManager = null; + final SurfaceControl.Transaction t = mTransactionSupplier.get(); + boolean released = false; + if (mWindowSurface != null) { + t.remove(mWindowSurface); + mWindowSurface = null; + released = true; + } + if (released) { + t.apply(); + } + } + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java index 4d37e5dbc4dc..15181b1549f5 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java @@ -35,6 +35,7 @@ import static org.mockito.Mockito.verify; import android.app.ActivityManager; import android.content.Context; +import android.content.res.Resources; import android.graphics.Color; import android.graphics.Point; import android.graphics.Rect; @@ -64,6 +65,7 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.mockito.Mock; +import org.mockito.Mockito; import java.util.ArrayList; import java.util.List; @@ -102,12 +104,14 @@ public class WindowDecorationTests extends ShellTestCase { private final List<SurfaceControl.Builder> mMockSurfaceControlBuilders = new ArrayList<>(); private SurfaceControl.Transaction mMockSurfaceControlStartT; private SurfaceControl.Transaction mMockSurfaceControlFinishT; + private SurfaceControl.Transaction mMockSurfaceControlAddWindowT; private WindowDecoration.RelayoutParams mRelayoutParams = new WindowDecoration.RelayoutParams(); @Before public void setUp() { mMockSurfaceControlStartT = createMockSurfaceControlTransaction(); mMockSurfaceControlFinishT = createMockSurfaceControlTransaction(); + mMockSurfaceControlAddWindowT = createMockSurfaceControlTransaction(); doReturn(mMockSurfaceControlViewHost).when(mMockSurfaceControlViewHostFactory) .create(any(), any(), any()); @@ -227,8 +231,8 @@ public class WindowDecorationTests extends ShellTestCase { verify(captionContainerSurfaceBuilder).setParent(decorContainerSurface); verify(captionContainerSurfaceBuilder).setContainerLayer(); - verify(mMockSurfaceControlStartT).setPosition(captionContainerSurface, -46, 8); - verify(mMockSurfaceControlStartT).setWindowCrop(captionContainerSurface, 300, 64); + verify(mMockSurfaceControlStartT).setPosition(captionContainerSurface, 20, 40); + verify(mMockSurfaceControlStartT).setWindowCrop(captionContainerSurface, 432, 64); verify(mMockSurfaceControlStartT).show(captionContainerSurface); verify(mMockSurfaceControlViewHostFactory).create(any(), eq(defaultDisplay), any()); @@ -242,7 +246,7 @@ public class WindowDecorationTests extends ShellTestCase { verify(mMockView).setTaskFocusState(true); verify(mMockWindowContainerTransaction) .addRectInsetsProvider(taskInfo.token, - new Rect(100, 300, 400, 332), + new Rect(100, 300, 400, 364), new int[] { InsetsState.ITYPE_CAPTION_BAR }); } @@ -366,6 +370,71 @@ public class WindowDecorationTests extends ShellTestCase { verify(mMockSurfaceControlViewHost).setView(same(mMockView), any()); } + @Test + public void testAddWindow() { + final Display defaultDisplay = mock(Display.class); + doReturn(defaultDisplay).when(mMockDisplayController) + .getDisplay(Display.DEFAULT_DISPLAY); + + final SurfaceControl decorContainerSurface = mock(SurfaceControl.class); + final SurfaceControl.Builder decorContainerSurfaceBuilder = + createMockSurfaceControlBuilder(decorContainerSurface); + mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder); + final SurfaceControl taskBackgroundSurface = mock(SurfaceControl.class); + final SurfaceControl.Builder taskBackgroundSurfaceBuilder = + createMockSurfaceControlBuilder(taskBackgroundSurface); + mMockSurfaceControlBuilders.add(taskBackgroundSurfaceBuilder); + final SurfaceControl captionContainerSurface = mock(SurfaceControl.class); + final SurfaceControl.Builder captionContainerSurfaceBuilder = + createMockSurfaceControlBuilder(captionContainerSurface); + mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder); + + final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class); + mMockSurfaceControlTransactions.add(t); + final ActivityManager.TaskDescription.Builder taskDescriptionBuilder = + new ActivityManager.TaskDescription.Builder() + .setBackgroundColor(Color.YELLOW); + final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder() + .setDisplayId(Display.DEFAULT_DISPLAY) + .setTaskDescriptionBuilder(taskDescriptionBuilder) + .setBounds(TASK_BOUNDS) + .setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y) + .setVisible(true) + .build(); + taskInfo.isFocused = true; + taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2; + mRelayoutParams.setOutsets( + R.dimen.test_window_decor_left_outset, + R.dimen.test_window_decor_top_outset, + R.dimen.test_window_decor_right_outset, + R.dimen.test_window_decor_bottom_outset); + final SurfaceControl taskSurface = mock(SurfaceControl.class); + final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface); + windowDecor.relayout(taskInfo); + + final SurfaceControl additionalWindowSurface = mock(SurfaceControl.class); + final SurfaceControl.Builder additionalWindowSurfaceBuilder = + createMockSurfaceControlBuilder(additionalWindowSurface); + mMockSurfaceControlBuilders.add(additionalWindowSurfaceBuilder); + + WindowDecoration.AdditionalWindow additionalWindow = windowDecor.addTestWindow(); + + verify(additionalWindowSurfaceBuilder).setContainerLayer(); + verify(additionalWindowSurfaceBuilder).setParent(decorContainerSurface); + verify(additionalWindowSurfaceBuilder).build(); + verify(mMockSurfaceControlAddWindowT).setPosition(additionalWindowSurface, 20, 40); + verify(mMockSurfaceControlAddWindowT).setWindowCrop(additionalWindowSurface, 432, 64); + verify(mMockSurfaceControlAddWindowT).show(additionalWindowSurface); + verify(mMockSurfaceControlViewHostFactory, Mockito.times(2)) + .create(any(), eq(defaultDisplay), any()); + assertThat(additionalWindow.mWindowViewHost).isNotNull(); + + additionalWindow.releaseView(); + + assertThat(additionalWindow.mWindowViewHost).isNull(); + assertThat(additionalWindow.mWindowSurface).isNull(); + } + private TestWindowDecoration createWindowDecoration( ActivityManager.RunningTaskInfo taskInfo, SurfaceControl testSurface) { return new TestWindowDecoration(InstrumentationRegistry.getInstrumentation().getContext(), @@ -429,5 +498,20 @@ public class WindowDecorationTests extends ShellTestCase { relayout(mRelayoutParams, mMockSurfaceControlStartT, mMockSurfaceControlFinishT, mMockWindowContainerTransaction, mMockView, mRelayoutResult); } + + private WindowDecoration.AdditionalWindow addTestWindow() { + final Resources resources = mDecorWindowContext.getResources(); + int x = mRelayoutParams.mCaptionX; + int y = mRelayoutParams.mCaptionY; + int width = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionWidthId); + int height = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionHeightId); + String name = "Test Window"; + WindowDecoration.AdditionalWindow additionalWindow = + addWindow(R.layout.caption_handle_menu, name, mMockSurfaceControlAddWindowT, + x - mRelayoutResult.mDecorContainerOffsetX, + y - mRelayoutResult.mDecorContainerOffsetY, + width, height); + return additionalWindow; + } } } |