Merge "Update caption handle menu to new spec." into tm-qpr-dev

This commit is contained in:
Matt Sziklay
2023-02-10 02:15:30 +00:00
committed by Android (Google) Code Review
16 changed files with 408 additions and 77 deletions

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2023 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:translateY="4.0">
<path
android:fillColor="#FFFF0000"
android:pathData="M12.45,38.35 L9.65,35.55 21.2,24 9.65,12.45 12.45,9.65 24,21.2 35.55,9.65 38.35,12.45 26.8,24 38.35,35.55 35.55,38.35 24,26.8Z"/>
</group>
</vector>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2023 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="24.0dp"
android:height="24.0dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
>
<group android:scaleX="1.25"
android:scaleY="1.75"
android:translateY="6.0">
<path
android:fillColor="@android:color/black"
android:pathData="M10.3937 6.93935L11.3337 5.99935L6.00033 0.666016L0.666992 5.99935L1.60699 6.93935L6.00033 2.55268"/>
</group>
</vector>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2023 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:translateY="4.0">
<path
android:fillColor="@android:color/black"
android:pathData="M10,38V28.35H13V35H19.65V38ZM10,19.65V10H19.65V13H13V19.65ZM28.35,38V35H35V28.35H38V38ZM35,19.65V13H28.35V10H38V19.65Z"/>
</group>
</vector>

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2023 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="4.0"
android:translateY="6.0">
<path
android:fillColor="@android:color/black"
android:pathData="M13.7021 12.5833L16.5676 15.5L15.426 16.7333L12.526 13.8333L10.4426 15.9167V10.5H15.9176L13.7021 12.5833ZM13.8343 3.83333H15.501V5.5H13.8343V3.83333ZM15.501 2.16667H13.8343V0.566667C14.751 0.566667 15.501 1.33333 15.501 2.16667ZM10.501 0.5H12.1676V2.16667H10.501V0.5ZM13.8343 7.16667H15.501V8.83333H13.8343V7.16667ZM5.50098 15.5H3.83431V13.8333H5.50098V15.5ZM2.16764 5.5H0.500977V3.83333H2.16764V5.5ZM2.16764 0.566667V2.16667H0.500977C0.500977 1.33333 1.33431 0.566667 2.16764 0.566667ZM2.16764 12.1667H0.500977V10.5H2.16764V12.1667ZM5.50098 2.16667H3.83431V0.5H5.50098V2.16667ZM8.83431 2.16667H7.16764V0.5H8.83431V2.16667ZM8.83431 15.5H7.16764V13.8333H8.83431V15.5ZM2.16764 8.83333H0.500977V7.16667H2.16764V8.83333ZM2.16764 15.5667C1.25098 15.5667 0.500977 14.6667 0.500977 13.8333H2.16764V15.5667Z"/>
</group>
</vector>

View File

@@ -17,9 +17,10 @@
android:width="24dp" android:width="24dp"
android:height="24dp" android:height="24dp"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24"
android:tint="@color/decor_button_dark_color">
<group android:translateY="8.0"> <group android:translateY="8.0">
<path <path
android:fillColor="@android:color/black" android:pathData="M3,5V3H21V5Z"/> android:fillColor="@android:color/white" android:pathData="M3,5V3H21V5Z"/>
</group> </group>
</vector> </vector>

View File

@@ -18,4 +18,5 @@
xmlns:android="http://schemas.android.com/apk/res/android"> xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@android:color/white" /> <solid android:color="@android:color/white" />
<corners android:radius="20dp" /> <corners android:radius="20dp" />
<stroke android:width="1dp" android:color="#b3b3b3"/>
</shape> </shape>

View File

@@ -15,6 +15,8 @@
~ limitations under the License. ~ limitations under the License.
--> -->
<shape android:shape="rectangle" <shape android:shape="rectangle"
android:tintMode="multiply"
android:tint="@color/decor_title_color"
xmlns:android="http://schemas.android.com/apk/res/android"> xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@android:color/white" /> <solid android:color="@android:color/white" />
</shape> </shape>

View File

@@ -1,49 +1,129 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- <!--
~ Copyright (C) 2022 The Android Open Source Project ~ Copyright (C) 2022 The Android Open Source Project
~ ~
~ Licensed under the Apache License, Version 2.0 (the "License"); ~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License. ~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at ~ You may obtain a copy of the License at
~ ~
~ http://www.apache.org/licenses/LICENSE-2.0 ~ http://www.apache.org/licenses/LICENSE-2.0
~ ~
~ Unless required by applicable law or agreed to in writing, software ~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS, ~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and ~ See the License for the specific language governing permissions and
~ limitations under the License. ~ limitations under the License.
--> -->
<com.android.wm.shell.windowdecor.WindowDecorLinearLayout <com.android.wm.shell.windowdecor.WindowDecorLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/handle_menu" android:id="@+id/handle_menu"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:gravity="center_horizontal" android:orientation="vertical"
android:background="@drawable/desktop_mode_decor_menu_background"> android:background="@drawable/desktop_mode_decor_menu_background"
<Button android:elevation="@dimen/caption_menu_elevation"
style="@style/CaptionButtonStyle" android:divider="?android:attr/dividerHorizontal"
android:id="@+id/fullscreen_button" android:showDividers="middle"
android:contentDescription="@string/fullscreen_text" android:dividerPadding="18dip">
android:background="@drawable/caption_fullscreen_button"/> <RelativeLayout
<Button android:layout_width="wrap_content"
style="@style/CaptionButtonStyle" android:layout_height="wrap_content">
android:id="@+id/split_screen_button" <ImageView
android:contentDescription="@string/split_screen_text" android:id="@+id/application_icon"
android:background="@drawable/caption_split_screen_button"/> android:layout_width="24dp"
<Button android:layout_height="24dp"
style="@style/CaptionButtonStyle" android:layout_margin="12dp"
android:id="@+id/floating_button" android:contentDescription="@string/app_icon_text"
android:contentDescription="@string/float_button_text" android:layout_alignParentStart="true"
android:background="@drawable/caption_floating_button"/> android:layout_centerVertical="true"/>
<Button <TextView
style="@style/CaptionButtonStyle" android:id="@+id/application_name"
android:id="@+id/desktop_button" android:layout_width="wrap_content"
android:contentDescription="@string/desktop_text" android:layout_height="wrap_content"
android:background="@drawable/caption_desktop_button"/> android:layout_toEndOf="@+id/application_icon"
<Button android:layout_toStartOf="@+id/collapse_menu_button"
style="@style/CaptionButtonStyle" android:textColor="#FF000000"
android:id="@+id/more_button" android:layout_centerVertical="true"/>
android:contentDescription="@string/more_button_text" <Button
android:background="@drawable/caption_more_button"/> android:id="@+id/collapse_menu_button"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="10dp"
android:contentDescription="@string/collapse_menu_text"
android:layout_alignParentEnd="true"
android:background="@drawable/caption_collapse_menu_button"
android:layout_centerVertical="true"/>
</RelativeLayout>
<LinearLayout
android:id="@+id/windowing_mode_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal">
<Space
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="0.5" />
<Button
style="@style/CaptionWindowingButtonStyle"
android:id="@+id/fullscreen_button"
android:contentDescription="@string/fullscreen_text"
android:background="@drawable/caption_fullscreen_button"/>
<Space
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1" />
<Button
style="@style/CaptionWindowingButtonStyle"
android:id="@+id/split_screen_button"
android:contentDescription="@string/split_screen_text"
android:background="@drawable/caption_split_screen_button"/>
<Space
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1" />
<Button
style="@style/CaptionWindowingButtonStyle"
android:id="@+id/floating_button"
android:contentDescription="@string/float_button_text"
android:background="@drawable/caption_floating_button"/>
<Space
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1" />
<Button
style="@style/CaptionWindowingButtonStyle"
android:id="@+id/desktop_button"
android:contentDescription="@string/desktop_text"
android:background="@drawable/caption_desktop_button"/>
<Space
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="0.5" />
</LinearLayout>
<LinearLayout
android:id="@+id/menu_buttons_misc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<Button
style="@style/CaptionMenuButtonStyle"
android:id="@+id/screenshot_button"
android:contentDescription="@string/screenshot_text"
android:text="@string/screenshot_text"
android:drawableStart="@drawable/caption_screenshot_button"/>
<Button
style="@style/CaptionMenuButtonStyle"
android:id="@+id/select_button"
android:contentDescription="@string/select_text"
android:text="@string/select_text"
android:drawableStart="@drawable/caption_select_button"/>
<Button
style="@style/CaptionMenuButtonStyle"
android:id="@+id/close_button"
android:contentDescription="@string/close_text"
android:text="@string/close_text"
android:drawableStart="@drawable/caption_close_button"
android:textColor="#FFFF0000"/>
</LinearLayout>
</com.android.wm.shell.windowdecor.WindowDecorLinearLayout> </com.android.wm.shell.windowdecor.WindowDecorLinearLayout>

View File

@@ -21,10 +21,21 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:background="@drawable/desktop_mode_decor_title"> android:background="@drawable/desktop_mode_decor_title">
<Button
style="@style/CaptionButtonStyle"
android:id="@+id/back_button"
android:contentDescription="@string/back_button_text"
android:background="@drawable/decor_back_button_dark"/>
<Button <Button
android:id="@+id/caption_handle" android:id="@+id/caption_handle"
android:layout_width="128dp" android:layout_width="128dp"
android:layout_height="32dp" android:layout_height="32dp"
android:layout_margin="5dp"
android:contentDescription="@string/handle_text" android:contentDescription="@string/handle_text"
android:background="@drawable/decor_handle_dark"/> android:background="@drawable/decor_handle_dark"/>
<Button
style="@style/CaptionButtonStyle"
android:id="@+id/close_window"
android:contentDescription="@string/close_button_text"
android:background="@drawable/decor_close_button_dark"/>
</com.android.wm.shell.windowdecor.WindowDecorLinearLayout> </com.android.wm.shell.windowdecor.WindowDecorLinearLayout>

View File

@@ -367,8 +367,12 @@
<!-- Width of buttons (32dp each) + padding (128dp total). --> <!-- Width of buttons (32dp each) + padding (128dp total). -->
<dimen name="freeform_decor_caption_menu_width">256dp</dimen> <dimen name="freeform_decor_caption_menu_width">256dp</dimen>
<dimen name="freeform_decor_caption_menu_height">250dp</dimen>
<dimen name="freeform_resize_handle">30dp</dimen> <dimen name="freeform_resize_handle">30dp</dimen>
<dimen name="freeform_resize_corner">44dp</dimen> <dimen name="freeform_resize_corner">44dp</dimen>
<dimen name="caption_menu_elevation">4dp</dimen>
</resources> </resources>

View File

@@ -225,6 +225,8 @@
<string name="back_button_text">Back</string> <string name="back_button_text">Back</string>
<!-- Accessibility text for the caption handle [CHAR LIMIT=NONE] --> <!-- Accessibility text for the caption handle [CHAR LIMIT=NONE] -->
<string name="handle_text">Handle</string> <string name="handle_text">Handle</string>
<!-- Accessibility text for the handle menu app icon [CHAR LIMIT=NONE] -->
<string name="app_icon_text">App Icon</string>
<!-- Accessibility text for the handle fullscreen button [CHAR LIMIT=NONE] --> <!-- Accessibility text for the handle fullscreen button [CHAR LIMIT=NONE] -->
<string name="fullscreen_text">Fullscreen</string> <string name="fullscreen_text">Fullscreen</string>
<!-- Accessibility text for the handle desktop button [CHAR LIMIT=NONE] --> <!-- Accessibility text for the handle desktop button [CHAR LIMIT=NONE] -->
@@ -235,4 +237,12 @@
<string name="more_button_text">More</string> <string name="more_button_text">More</string>
<!-- Accessibility text for the handle floating window button [CHAR LIMIT=NONE] --> <!-- Accessibility text for the handle floating window button [CHAR LIMIT=NONE] -->
<string name="float_button_text">Float</string> <string name="float_button_text">Float</string>
<!-- Accessibility text for the handle menu select button [CHAR LIMIT=NONE] -->
<string name="select_text">Select</string>
<!-- Accessibility text for the handle menu screenshot button [CHAR LIMIT=NONE] -->
<string name="screenshot_text">Screenshot</string>
<!-- Accessibility text for the handle menu close button [CHAR LIMIT=NONE] -->
<string name="close_text">Close</string>
<!-- Accessibility text for the handle menu close menu button [CHAR LIMIT=NONE] -->
<string name="collapse_menu_text">Close Menu</string>
</resources> </resources>

View File

@@ -37,6 +37,22 @@
<item name="android:padding">4dp</item> <item name="android:padding">4dp</item>
</style> </style>
<style name="CaptionWindowingButtonStyle">
<item name="android:layout_width">32dp</item>
<item name="android:layout_height">32dp</item>
<item name="android:padding">4dp</item>
<item name="android:layout_marginTop">5dp</item>
<item name="android:layout_marginBottom">5dp</item>
</style>
<style name="CaptionMenuButtonStyle" parent="@style/Widget.AppCompat.Button.Borderless">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">52dp</item>
<item name="android:layout_marginStart">10dp</item>
<item name="android:padding">4dp</item>
<item name="android:gravity">start|center_vertical</item>
</style>
<style name="DockedDividerBackground"> <style name="DockedDividerBackground">
<item name="android:layout_width">match_parent</item> <item name="android:layout_width">match_parent</item>
<item name="android:layout_height">@dimen/split_divider_bar_width</item> <item name="android:layout_height">@dimen/split_divider_bar_width</item>

View File

@@ -156,6 +156,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
} }
decoration.relayout(taskInfo); decoration.relayout(taskInfo);
setupCaptionColor(taskInfo, decoration);
} }
@Override @Override
@@ -227,7 +228,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
public void onClick(View v) { public void onClick(View v) {
final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId); final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
final int id = v.getId(); final int id = v.getId();
if (id == R.id.caption_handle) { if (id == R.id.close_window || id == R.id.close_button) {
mTaskOperations.closeTask(mTaskToken);
} else if (id == R.id.back_button) {
mTaskOperations.injectBackKey();
} else if (id == R.id.caption_handle) {
decoration.createHandleMenu(); decoration.createHandleMenu();
} else if (id == R.id.desktop_button) { } else if (id == R.id.desktop_button) {
mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(true)); mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(true));
@@ -238,6 +243,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mDesktopTasksController.ifPresent(c -> c.moveToFullscreen(mTaskId)); mDesktopTasksController.ifPresent(c -> c.moveToFullscreen(mTaskId));
decoration.closeHandleMenu(); decoration.closeHandleMenu();
decoration.setButtonVisibility(false); decoration.setButtonVisibility(false);
} else if (id == R.id.collapse_menu_button) {
decoration.closeHandleMenu();
} }
} }
@@ -508,6 +515,13 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
} }
} }
private void setupCaptionColor(RunningTaskInfo taskInfo,
DesktopModeWindowDecoration decoration) {
if (taskInfo == null || taskInfo.taskDescription == null) return;
final int statusBarColor = taskInfo.taskDescription.getStatusBarColor();
decoration.setCaptionColor(statusBarColor);
}
private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) { private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true; if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true;
return DesktopModeStatus.isProto2Enabled() return DesktopModeStatus.isProto2Enabled()
@@ -546,6 +560,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
windowDecoration.setDragPositioningCallback(taskPositioner); windowDecoration.setDragPositioningCallback(taskPositioner);
windowDecoration.setDragDetector(touchEventListener.mDragDetector); windowDecoration.setDragDetector(touchEventListener.mDragDetector);
windowDecoration.relayout(taskInfo, startT, finishT); windowDecoration.relayout(taskInfo, startT, finishT);
setupCaptionColor(taskInfo, windowDecoration);
incrementEventReceiverTasks(taskInfo.displayId); incrementEventReceiverTasks(taskInfo.displayId);
} }

View File

@@ -20,18 +20,25 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.content.Context; import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList; import android.content.res.ColorStateList;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Point; import android.graphics.Point;
import android.graphics.PointF; import android.graphics.PointF;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.VectorDrawable; import android.graphics.drawable.VectorDrawable;
import android.os.Handler; import android.os.Handler;
import android.util.Log;
import android.view.Choreographer; import android.view.Choreographer;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.SurfaceControl; import android.view.SurfaceControl;
import android.view.View; import android.view.View;
import android.view.ViewConfiguration; import android.view.ViewConfiguration;
import android.widget.ImageView;
import android.widget.TextView;
import android.window.WindowContainerTransaction; import android.window.WindowContainerTransaction;
import com.android.wm.shell.R; import com.android.wm.shell.R;
@@ -48,6 +55,7 @@ import com.android.wm.shell.desktopmode.DesktopModeStatus;
* The shadow's thickness is 20dp when the window is in focus and 5dp when the window isn't. * The shadow's thickness is 20dp when the window is in focus and 5dp when the window isn't.
*/ */
public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLinearLayout> { public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLinearLayout> {
private static final String TAG = "DesktopModeWindowDecoration";
private final Handler mHandler; private final Handler mHandler;
private final Choreographer mChoreographer; private final Choreographer mChoreographer;
private final SyncTransactionQueue mSyncQueue; private final SyncTransactionQueue mSyncQueue;
@@ -59,12 +67,14 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private DragDetector mDragDetector; private DragDetector mDragDetector;
private RelayoutParams mRelayoutParams = new RelayoutParams(); private RelayoutParams mRelayoutParams = new RelayoutParams();
private final int mCaptionMenuWidthId = R.dimen.freeform_decor_caption_menu_width; private final int mCaptionMenuHeightId = R.dimen.freeform_decor_caption_menu_height;
private final WindowDecoration.RelayoutResult<WindowDecorLinearLayout> mResult = private final WindowDecoration.RelayoutResult<WindowDecorLinearLayout> mResult =
new WindowDecoration.RelayoutResult<>(); new WindowDecoration.RelayoutResult<>();
private boolean mDesktopActive; private boolean mDesktopActive;
private AdditionalWindow mHandleMenu; private AdditionalWindow mHandleMenu;
private final int mHandleMenuWidthId = R.dimen.freeform_decor_caption_menu_width;
private PointF mHandleMenuPosition = new PointF();
DesktopModeWindowDecoration( DesktopModeWindowDecoration(
Context context, Context context,
@@ -211,21 +221,46 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
final View fullscreen = menu.findViewById(R.id.fullscreen_button); final View fullscreen = menu.findViewById(R.id.fullscreen_button);
fullscreen.setOnClickListener(mOnCaptionButtonClickListener); fullscreen.setOnClickListener(mOnCaptionButtonClickListener);
final View desktop = menu.findViewById(R.id.desktop_button); final View desktop = menu.findViewById(R.id.desktop_button);
desktop.setOnClickListener(mOnCaptionButtonClickListener); if (DesktopModeStatus.isProto2Enabled()) {
desktop.setOnClickListener(mOnCaptionButtonClickListener);
} else if (DesktopModeStatus.isProto1Enabled()) {
desktop.setVisibility(View.GONE);
}
final View split = menu.findViewById(R.id.split_screen_button); final View split = menu.findViewById(R.id.split_screen_button);
split.setOnClickListener(mOnCaptionButtonClickListener); split.setOnClickListener(mOnCaptionButtonClickListener);
final View close = menu.findViewById(R.id.close_button);
close.setOnClickListener(mOnCaptionButtonClickListener);
final View collapse = menu.findViewById(R.id.collapse_menu_button);
collapse.setOnClickListener(mOnCaptionButtonClickListener);
menu.setOnTouchListener(mOnCaptionTouchListener);
String packageName = mTaskInfo.baseActivity.getPackageName();
PackageManager pm = mContext.getApplicationContext().getPackageManager();
// TODO(b/268363572): Use IconProvider or BaseIconCache to set drawable/name.
try {
ApplicationInfo applicationInfo = pm.getApplicationInfo(packageName,
PackageManager.ApplicationInfoFlags.of(0));
final ImageView appIcon = menu.findViewById(R.id.application_icon);
appIcon.setImageDrawable(pm.getApplicationIcon(applicationInfo));
final TextView appName = menu.findViewById(R.id.application_name);
appName.setText(pm.getApplicationLabel(applicationInfo));
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Package not found: " + packageName, e);
}
} }
/** /**
* Sets caption visibility based on task focus. * Sets caption visibility based on task focus.
* * Note: Only applicable to Desktop Proto 1; Proto 2 only closes handle menu on focus loss
* @param visible whether or not the caption should be visible * @param visible whether or not the caption should be visible
*/ */
private void setCaptionVisibility(boolean visible) { private void setCaptionVisibility(boolean visible) {
if (!visible) closeHandleMenu();
if (!DesktopModeStatus.isProto1Enabled()) return;
final int v = visible ? View.VISIBLE : View.GONE; final int v = visible ? View.VISIBLE : View.GONE;
final View captionView = mResult.mRootView.findViewById(R.id.desktop_mode_caption); final View captionView = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
captionView.setVisibility(v); captionView.setVisibility(v);
if (!visible) closeHandleMenu();
} }
/** /**
@@ -244,8 +279,13 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
* Show or hide buttons * Show or hide buttons
*/ */
void setButtonVisibility(boolean visible) { void setButtonVisibility(boolean visible) {
final int visibility = visible ? View.VISIBLE : View.GONE; final int visibility = visible && DesktopModeStatus.isProto1Enabled()
? View.VISIBLE : View.GONE;
final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption); final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
final View back = caption.findViewById(R.id.back_button);
final View close = caption.findViewById(R.id.close_window);
back.setVisibility(visibility);
close.setVisibility(visibility);
final int buttonTintColorRes = final int buttonTintColorRes =
mDesktopActive ? R.color.decor_button_dark_color mDesktopActive ? R.color.decor_button_dark_color
: R.color.decor_button_light_color; : R.color.decor_button_light_color;
@@ -254,13 +294,33 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
final View handle = caption.findViewById(R.id.caption_handle); final View handle = caption.findViewById(R.id.caption_handle);
final VectorDrawable handleBackground = (VectorDrawable) handle.getBackground(); final VectorDrawable handleBackground = (VectorDrawable) handle.getBackground();
handleBackground.setTintList(buttonTintColor); handleBackground.setTintList(buttonTintColor);
caption.getBackground().setTint(visible ? Color.WHITE : Color.TRANSPARENT);
} }
boolean isHandleMenuActive() { boolean isHandleMenuActive() {
return mHandleMenu != null; return mHandleMenu != null;
} }
void setCaptionColor(int captionColor) {
if (mResult.mRootView == null) {
return;
}
final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
final GradientDrawable captionDrawable = (GradientDrawable) caption.getBackground();
captionDrawable.setColor(captionColor);
final int buttonTintColorRes =
Color.valueOf(captionColor).luminance() < 0.5
? R.color.decor_button_light_color
: R.color.decor_button_dark_color;
final ColorStateList buttonTintColor =
caption.getResources().getColorStateList(buttonTintColorRes, null /* theme */);
final View handle = caption.findViewById(R.id.caption_handle);
final Drawable handleBackground = handle.getBackground();
handleBackground.setTintList(buttonTintColor);
}
private void closeDragResizeListener() { private void closeDragResizeListener() {
if (mDragResizeListener == null) { if (mDragResizeListener == null) {
return; return;
@@ -277,16 +337,21 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
final Resources resources = mDecorWindowContext.getResources(); final Resources resources = mDecorWindowContext.getResources();
final int captionWidth = mTaskInfo.getConfiguration() final int captionWidth = mTaskInfo.getConfiguration()
.windowConfiguration.getBounds().width(); .windowConfiguration.getBounds().width();
final int menuWidth = loadDimensionPixelSize( final int menuWidth = loadDimensionPixelSize(resources, mHandleMenuWidthId);
resources, mCaptionMenuWidthId); final int menuHeight = loadDimensionPixelSize(resources, mCaptionMenuHeightId);
final int height = loadDimensionPixelSize(
resources, mRelayoutParams.mCaptionHeightId); // Elevation gives the appearance of a changed x/y coordinate; this is to fix that
int elevationOffset = 2 * loadDimensionPixelSize(resources,
R.dimen.caption_menu_elevation);
final int x = mRelayoutParams.mCaptionX + (captionWidth / 2) - (menuWidth / 2) final int x = mRelayoutParams.mCaptionX + (captionWidth / 2) - (menuWidth / 2)
- mResult.mDecorContainerOffsetX; - mResult.mDecorContainerOffsetX - elevationOffset;
final int y = mRelayoutParams.mCaptionY - mResult.mDecorContainerOffsetY; final int y =
mRelayoutParams.mCaptionY - mResult.mDecorContainerOffsetY - elevationOffset;
mHandleMenuPosition.set(x, y);
String namePrefix = "Caption Menu"; String namePrefix = "Caption Menu";
mHandleMenu = addWindow(R.layout.desktop_mode_decor_handle_menu, namePrefix, t, x, y, mHandleMenu = addWindow(R.layout.desktop_mode_decor_handle_menu, namePrefix, t, x, y,
menuWidth, height); menuWidth, menuHeight, 2 * elevationOffset);
mSyncQueue.runInSync(transaction -> { mSyncQueue.runInSync(transaction -> {
transaction.merge(t); transaction.merge(t);
t.close(); t.close();
@@ -315,10 +380,17 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
* @param ev the tapped point to compare against * @param ev the tapped point to compare against
*/ */
void closeHandleMenuIfNeeded(MotionEvent ev) { void closeHandleMenuIfNeeded(MotionEvent ev) {
if (isHandleMenuActive()) { if (!isHandleMenuActive()) return;
if (!checkEventInCaptionView(ev, R.id.desktop_mode_caption)) {
closeHandleMenu(); // When this is called before the layout is fully inflated, width will be 0.
} // Menu is not visible in this scenario, so skip the check if that is the case.
if (mHandleMenu.mWindowViewHost.getView().getWidth() == 0) return;
PointF inputPoint = offsetCaptionLocation(ev);
if (!pointInView(mHandleMenu.mWindowViewHost.getView(),
inputPoint.x - mHandleMenuPosition.x - mResult.mDecorContainerOffsetX,
inputPoint.y - mHandleMenuPosition.y - mResult.mDecorContainerOffsetY)) {
closeHandleMenu();
} }
} }
@@ -380,12 +452,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
final int menuX = mRelayoutParams.mCaptionX + (captionWidth / 2) final int menuX = mRelayoutParams.mCaptionX + (captionWidth / 2)
- (menu.getWidth() / 2); - (menu.getWidth() / 2);
final PointF inputPoint = new PointF(ev.getX() - menuX, ev.getY()); final PointF inputPoint = new PointF(ev.getX() - menuX, ev.getY());
final View fullscreen = menu.findViewById(R.id.fullscreen_button); final View collapse = menu.findViewById(R.id.collapse_menu_button);
if (clickIfPointInView(inputPoint, fullscreen)) return; if (clickIfPointInView(inputPoint, collapse)) return;
final View desktop = menu.findViewById(R.id.desktop_button);
if (clickIfPointInView(inputPoint, desktop)) return;
final View split = menu.findViewById(R.id.split_screen_button);
if (clickIfPointInView(inputPoint, split)) return;
} }
} }

View File

@@ -391,10 +391,11 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
* @param yPos y position of new window * @param yPos y position of new window
* @param width width of new window * @param width width of new window
* @param height height of new window * @param height height of new window
* @param cropPadding padding to add to window crop to ensure shadows display properly
* @return * @return
*/ */
AdditionalWindow addWindow(int layoutId, String namePrefix, AdditionalWindow addWindow(int layoutId, String namePrefix, SurfaceControl.Transaction t,
SurfaceControl.Transaction t, int xPos, int yPos, int width, int height) { int xPos, int yPos, int width, int height, int cropPadding) {
final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get(); final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get();
SurfaceControl windowSurfaceControl = builder SurfaceControl windowSurfaceControl = builder
.setName(namePrefix + " of Task=" + mTaskInfo.taskId) .setName(namePrefix + " of Task=" + mTaskInfo.taskId)
@@ -405,7 +406,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
t.setPosition( t.setPosition(
windowSurfaceControl, xPos, yPos) windowSurfaceControl, xPos, yPos)
.setWindowCrop(windowSurfaceControl, width, height) .setWindowCrop(windowSurfaceControl, width + cropPadding, height + cropPadding)
.show(windowSurfaceControl); .show(windowSurfaceControl);
final WindowManager.LayoutParams lp = final WindowManager.LayoutParams lp =
new WindowManager.LayoutParams(width, height, new WindowManager.LayoutParams(width, height,
@@ -426,6 +427,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
RunningTaskInfo mRunningTaskInfo; RunningTaskInfo mRunningTaskInfo;
int mLayoutResId; int mLayoutResId;
int mCaptionHeightId; int mCaptionHeightId;
int mCaptionWidthId;
int mShadowRadiusId; int mShadowRadiusId;
int mOutsetTopId; int mOutsetTopId;
@@ -451,6 +453,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
void reset() { void reset() {
mLayoutResId = Resources.ID_NULL; mLayoutResId = Resources.ID_NULL;
mCaptionHeightId = Resources.ID_NULL; mCaptionHeightId = Resources.ID_NULL;
mCaptionWidthId = Resources.ID_NULL;
mShadowRadiusId = Resources.ID_NULL; mShadowRadiusId = Resources.ID_NULL;
mOutsetTopId = Resources.ID_NULL; mOutsetTopId = Resources.ID_NULL;

View File

@@ -431,7 +431,7 @@ public class WindowDecorationTests extends ShellTestCase {
verify(additionalWindowSurfaceBuilder).setParent(decorContainerSurface); verify(additionalWindowSurfaceBuilder).setParent(decorContainerSurface);
verify(additionalWindowSurfaceBuilder).build(); verify(additionalWindowSurfaceBuilder).build();
verify(mMockSurfaceControlAddWindowT).setPosition(additionalWindowSurface, 20, 40); verify(mMockSurfaceControlAddWindowT).setPosition(additionalWindowSurface, 20, 40);
verify(mMockSurfaceControlAddWindowT).setWindowCrop(additionalWindowSurface, 432, 64); verify(mMockSurfaceControlAddWindowT).setWindowCrop(additionalWindowSurface, 442, 74);
verify(mMockSurfaceControlAddWindowT).show(additionalWindowSurface); verify(mMockSurfaceControlAddWindowT).show(additionalWindowSurface);
verify(mMockSurfaceControlViewHostFactory, Mockito.times(2)) verify(mMockSurfaceControlViewHostFactory, Mockito.times(2))
.create(any(), eq(defaultDisplay), any()); .create(any(), eq(defaultDisplay), any());
@@ -565,7 +565,7 @@ public class WindowDecorationTests extends ShellTestCase {
mMockSurfaceControlAddWindowT, mMockSurfaceControlAddWindowT,
x - mRelayoutResult.mDecorContainerOffsetX, x - mRelayoutResult.mDecorContainerOffsetX,
y - mRelayoutResult.mDecorContainerOffsetY, y - mRelayoutResult.mDecorContainerOffsetY,
width, height); width, height, 10);
return additionalWindow; return additionalWindow;
} }
} }