Add handle menu to bar type window caption.
Adds a menu on clicking handle that will enable windowing changes. Still to implement for this prototype: More button, Split Screen button, Overtop/Float button. Bug: 239960071 Test: Launch various tasks, confirm handle menu appears, drag functionality is unaffected. Change-Id: I0aeeffb44e6bfd580c70270e562d8c04031892de
This commit is contained in:
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
49
libs/WindowManager/Shell/res/layout/caption_handle_menu.xml
Normal file
49
libs/WindowManager/Shell/res/layout/caption_handle_menu.xml
Normal file
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user