Delegate handle menu operations to a separate class.

Moves the logic for handle menu creation/updating/removal to a separate
class out of DesktopModeWindowDecoration.

Bug: 276364188
Test: Manual; confirm no regressions with opening/closing/interacting
with handle menu.

Change-Id: Ie97a32a2a3257bd37d395995e7af75bfc6931add
This commit is contained in:
mattsziklay
2023-04-12 10:45:33 -07:00
parent 1e84c915b9
commit 2e39619556
3 changed files with 422 additions and 215 deletions

View File

@@ -17,17 +17,12 @@
package com.android.wm.shell.windowdecor;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Region;
@@ -39,11 +34,6 @@ import android.view.MotionEvent;
import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.window.SurfaceSyncGroup;
import android.window.WindowContainerTransaction;
import com.android.launcher3.icons.IconProvider;
@@ -90,6 +80,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private AdditionalWindow mHandleMenuAppInfoPill;
private AdditionalWindow mHandleMenuWindowingPill;
private AdditionalWindow mHandleMenuMoreActionsPill;
private HandleMenu mHandleMenu;
private Drawable mAppIcon;
private CharSequence mAppName;
@@ -122,29 +113,6 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mSyncQueue = syncQueue;
loadAppInfo();
loadHandleMenuDimensions();
}
private void loadHandleMenuDimensions() {
final Resources resources = mDecorWindowContext.getResources();
mMenuWidth = loadDimensionPixelSize(resources,
R.dimen.desktop_mode_handle_menu_width);
mMarginMenuTop = loadDimensionPixelSize(resources,
R.dimen.desktop_mode_handle_menu_margin_top);
mMarginMenuStart = loadDimensionPixelSize(resources,
R.dimen.desktop_mode_handle_menu_margin_start);
mMarginMenuSpacing = loadDimensionPixelSize(resources,
R.dimen.desktop_mode_handle_menu_pill_spacing_margin);
mAppInfoPillHeight = loadDimensionPixelSize(resources,
R.dimen.desktop_mode_handle_menu_app_info_pill_height);
mWindowingPillHeight = loadDimensionPixelSize(resources,
R.dimen.desktop_mode_handle_menu_windowing_pill_height);
mShadowRadius = loadDimensionPixelSize(resources,
R.dimen.desktop_mode_handle_menu_shadow_radius);
mCornerRadius = loadDimensionPixelSize(resources,
R.dimen.desktop_mode_handle_menu_corner_radius);
mMoreActionsPillHeight = loadDimensionPixelSize(resources,
R.dimen.desktop_mode_handle_menu_more_actions_pill_height);
}
@Override
@@ -197,20 +165,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM;
final boolean isDragResizeable = isFreeform && taskInfo.isResizeable;
if (mHandleMenuAppInfoPill != null) {
updateHandleMenuPillPositions();
startT.setPosition(mHandleMenuAppInfoPill.mWindowSurface,
mHandleMenuAppInfoPillPosition.x, mHandleMenuAppInfoPillPosition.y);
// Only show windowing buttons in proto2. Proto1 uses a system-level mode only.
final boolean shouldShowWindowingPill = DesktopModeStatus.isProto2Enabled();
if (shouldShowWindowingPill) {
startT.setPosition(mHandleMenuWindowingPill.mWindowSurface,
mHandleMenuWindowingPillPosition.x, mHandleMenuWindowingPillPosition.y);
}
startT.setPosition(mHandleMenuMoreActionsPill.mWindowSurface,
mHandleMenuMoreActionsPillPosition.x, mHandleMenuMoreActionsPillPosition.y);
if (isHandleMenuActive()) {
mHandleMenu.relayout(startT);
}
final WindowDecorLinearLayout oldRootView = mResult.mRootView;
@@ -297,7 +253,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
}
boolean isHandleMenuActive() {
return mHandleMenuAppInfoPill != null;
return mHandleMenu != null;
}
private void loadAppInfo() {
@@ -327,136 +283,16 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
* Create and display handle menu window
*/
void createHandleMenu() {
final SurfaceSyncGroup ssg = new SurfaceSyncGroup(TAG);
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
updateHandleMenuPillPositions();
createAppInfoPill(t, ssg);
// Only show windowing buttons in proto2. Proto1 uses a system-level mode only.
final boolean shouldShowWindowingPill = DesktopModeStatus.isProto2Enabled();
if (shouldShowWindowingPill) {
createWindowingPill(t, ssg);
}
createMoreActionsPill(t, ssg);
ssg.addTransaction(t);
ssg.markSyncReady();
setupHandleMenu(shouldShowWindowingPill);
}
private void createAppInfoPill(SurfaceControl.Transaction t, SurfaceSyncGroup ssg) {
final int x = (int) mHandleMenuAppInfoPillPosition.x;
final int y = (int) mHandleMenuAppInfoPillPosition.y;
mHandleMenuAppInfoPill = addWindow(
R.layout.desktop_mode_window_decor_handle_menu_app_info_pill,
"Menu's app info pill",
t, ssg, x, y, mMenuWidth, mAppInfoPillHeight, mShadowRadius, mCornerRadius);
}
private void createWindowingPill(SurfaceControl.Transaction t, SurfaceSyncGroup ssg) {
final int x = (int) mHandleMenuWindowingPillPosition.x;
final int y = (int) mHandleMenuWindowingPillPosition.y;
mHandleMenuWindowingPill = addWindow(
R.layout.desktop_mode_window_decor_handle_menu_windowing_pill,
"Menu's windowing pill",
t, ssg, x, y, mMenuWidth, mWindowingPillHeight, mShadowRadius, mCornerRadius);
}
private void createMoreActionsPill(SurfaceControl.Transaction t, SurfaceSyncGroup ssg) {
final int x = (int) mHandleMenuMoreActionsPillPosition.x;
final int y = (int) mHandleMenuMoreActionsPillPosition.y;
mHandleMenuMoreActionsPill = addWindow(
R.layout.desktop_mode_window_decor_handle_menu_more_actions_pill,
"Menu's more actions pill",
t, ssg, x, y, mMenuWidth, mMoreActionsPillHeight, mShadowRadius, mCornerRadius);
}
private void setupHandleMenu(boolean windowingPillShown) {
// App Info pill setup.
final View appInfoPillView = mHandleMenuAppInfoPill.mWindowViewHost.getView();
final ImageButton collapseBtn = appInfoPillView.findViewById(R.id.collapse_menu_button);
final ImageView appIcon = appInfoPillView.findViewById(R.id.application_icon);
final TextView appName = appInfoPillView.findViewById(R.id.application_name);
collapseBtn.setOnClickListener(mOnCaptionButtonClickListener);
appInfoPillView.setOnTouchListener(mOnCaptionTouchListener);
appIcon.setImageDrawable(mAppIcon);
appName.setText(mAppName);
// Windowing pill setup.
if (windowingPillShown) {
final View windowingPillView = mHandleMenuWindowingPill.mWindowViewHost.getView();
final ImageButton fullscreenBtn = windowingPillView.findViewById(
R.id.fullscreen_button);
final ImageButton splitscreenBtn = windowingPillView.findViewById(
R.id.split_screen_button);
final ImageButton floatingBtn = windowingPillView.findViewById(R.id.floating_button);
final ImageButton desktopBtn = windowingPillView.findViewById(R.id.desktop_button);
fullscreenBtn.setOnClickListener(mOnCaptionButtonClickListener);
splitscreenBtn.setOnClickListener(mOnCaptionButtonClickListener);
floatingBtn.setOnClickListener(mOnCaptionButtonClickListener);
desktopBtn.setOnClickListener(mOnCaptionButtonClickListener);
// The button corresponding to the windowing mode that the task is currently in uses a
// different color than the others.
final ColorStateList activeColorStateList = ColorStateList.valueOf(
mContext.getColor(R.color.desktop_mode_caption_menu_buttons_color_active));
final ColorStateList inActiveColorStateList = ColorStateList.valueOf(
mContext.getColor(R.color.desktop_mode_caption_menu_buttons_color_inactive));
fullscreenBtn.setImageTintList(
mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
? activeColorStateList : inActiveColorStateList);
splitscreenBtn.setImageTintList(
mTaskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW
? activeColorStateList : inActiveColorStateList);
floatingBtn.setImageTintList(mTaskInfo.getWindowingMode() == WINDOWING_MODE_PINNED
? activeColorStateList : inActiveColorStateList);
desktopBtn.setImageTintList(mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
? activeColorStateList : inActiveColorStateList);
}
// More Actions pill setup.
final View moreActionsPillView = mHandleMenuMoreActionsPill.mWindowViewHost.getView();
final Button closeBtn = moreActionsPillView.findViewById(R.id.close_button);
closeBtn.setOnClickListener(mOnCaptionButtonClickListener);
}
/**
* Updates the handle menu pills' position variables to reflect their next positions
*/
private void updateHandleMenuPillPositions() {
final int menuX, menuY;
final int captionWidth = mTaskInfo.getConfiguration()
.windowConfiguration.getBounds().width();
if (mRelayoutParams.mLayoutResId
== R.layout.desktop_mode_app_controls_window_decor) {
// Align the handle menu to the left of the caption.
menuX = mRelayoutParams.mCaptionX + mMarginMenuStart;
menuY = mRelayoutParams.mCaptionY + mMarginMenuTop;
} else {
// Position the handle menu at the center of the caption.
menuX = mRelayoutParams.mCaptionX + (captionWidth / 2) - (mMenuWidth / 2);
menuY = mRelayoutParams.mCaptionY + mMarginMenuStart;
}
// App Info pill setup.
final int appInfoPillY = menuY;
mHandleMenuAppInfoPillPosition.set(menuX, appInfoPillY);
// Only show windowing buttons in proto2. Proto1 uses a system-level mode only.
final boolean shouldShowWindowingPill = DesktopModeStatus.isProto2Enabled();
final int windowingPillY, moreActionsPillY;
if (shouldShowWindowingPill) {
windowingPillY = appInfoPillY + mAppInfoPillHeight + mMarginMenuSpacing;
mHandleMenuWindowingPillPosition.set(menuX, windowingPillY);
moreActionsPillY = windowingPillY + mWindowingPillHeight + mMarginMenuSpacing;
mHandleMenuMoreActionsPillPosition.set(menuX, moreActionsPillY);
} else {
// Just start after the end of the app info pill + margins.
moreActionsPillY = appInfoPillY + mAppInfoPillHeight + mMarginMenuSpacing;
mHandleMenuMoreActionsPillPosition.set(menuX, moreActionsPillY);
}
mHandleMenu = new HandleMenu.Builder(this)
.setAppIcon(mAppIcon)
.setAppName(mAppName)
.setOnClickListener(mOnCaptionButtonClickListener)
.setOnTouchListener(mOnCaptionTouchListener)
.setLayoutId(mRelayoutParams.mLayoutResId)
.setCaptionPosition(mRelayoutParams.mCaptionX, mRelayoutParams.mCaptionY)
.setWindowingButtonsVisible(DesktopModeStatus.isProto2Enabled())
.build();
mHandleMenu.show();
}
/**
@@ -464,14 +300,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
*/
void closeHandleMenu() {
if (!isHandleMenuActive()) return;
mHandleMenuAppInfoPill.releaseView();
mHandleMenuAppInfoPill = null;
if (mHandleMenuWindowingPill != null) {
mHandleMenuWindowingPill.releaseView();
mHandleMenuWindowingPill = null;
}
mHandleMenuMoreActionsPill.releaseView();
mHandleMenuMoreActionsPill = null;
mHandleMenu.close();
mHandleMenu = null;
}
@Override
@@ -488,10 +318,6 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
void closeHandleMenuIfNeeded(MotionEvent ev) {
if (!isHandleMenuActive()) return;
// 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 (mHandleMenuAppInfoPill.mWindowViewHost.getView().getWidth() == 0) return;
PointF inputPoint = offsetCaptionLocation(ev);
// If this is called before open_menu_button's onClick, we don't want to close
@@ -501,22 +327,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
inputPoint.x,
inputPoint.y);
final boolean pointInAppInfoPill = pointInView(
mHandleMenuAppInfoPill.mWindowViewHost.getView(),
inputPoint.x - mHandleMenuAppInfoPillPosition.x,
inputPoint.y - mHandleMenuAppInfoPillPosition.y);
boolean pointInWindowingPill = false;
if (mHandleMenuWindowingPill != null) {
pointInWindowingPill = pointInView(mHandleMenuWindowingPill.mWindowViewHost.getView(),
inputPoint.x - mHandleMenuWindowingPillPosition.x,
inputPoint.y - mHandleMenuWindowingPillPosition.y);
}
final boolean pointInMoreActionsPill = pointInView(
mHandleMenuMoreActionsPill.mWindowViewHost.getView(),
inputPoint.x - mHandleMenuMoreActionsPillPosition.x,
inputPoint.y - mHandleMenuMoreActionsPillPosition.y);
if (!pointInAppInfoPill && !pointInWindowingPill
&& !pointInMoreActionsPill && !pointInOpenMenuButton) {
if (!mHandleMenu.isValidMenuInput(inputPoint) && !pointInOpenMenuButton) {
closeHandleMenu();
}
}
@@ -573,13 +384,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
final View handle = caption.findViewById(R.id.caption_handle);
clickIfPointInView(new PointF(ev.getX(), ev.getY()), handle);
} else {
final View appInfoPill = mHandleMenuAppInfoPill.mWindowViewHost.getView();
final ImageButton collapse = appInfoPill.findViewById(R.id.collapse_menu_button);
// Translate the input point from display coordinates to the same space as the collapse
// button, meaning its parent (app info pill view).
final PointF inputPoint = new PointF(ev.getX() - mHandleMenuAppInfoPillPosition.x,
ev.getY() - mHandleMenuAppInfoPillPosition.y);
clickIfPointInView(inputPoint, collapse);
mHandleMenu.checkClickEvent(ev);
}
}
@@ -591,7 +396,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
return false;
}
private boolean pointInView(View v, float x, float y) {
boolean pointInView(View v, float x, float y) {
return v != null && v.getLeft() <= x && v.getRight() >= x
&& v.getTop() <= y && v.getBottom() >= y;
}

View File

@@ -0,0 +1,402 @@
/*
* 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.
*/
package com.android.wm.shell.windowdecor;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.PointF;
import android.graphics.drawable.Drawable;
import android.view.MotionEvent;
import android.view.SurfaceControl;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.window.SurfaceSyncGroup;
import com.android.wm.shell.R;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
/**
* Handle menu opened when the appropriate button is clicked on.
*
* Displays up to 3 pills that show the following:
* App Info: App name, app icon, and collapse button to close the menu.
* Windowing Options(Proto 2 only): Buttons to change windowing modes.
* Additional Options: Miscellaneous functions including screenshot and closing task.
*/
class HandleMenu {
private static final String TAG = "HandleMenu";
private final Context mContext;
private final WindowDecoration mParentDecor;
private WindowDecoration.AdditionalWindow mAppInfoPill;
private WindowDecoration.AdditionalWindow mWindowingPill;
private WindowDecoration.AdditionalWindow mMoreActionsPill;
private final PointF mAppInfoPillPosition = new PointF();
private final PointF mWindowingPillPosition = new PointF();
private final PointF mMoreActionsPillPosition = new PointF();
private final boolean mShouldShowWindowingPill;
private final Drawable mAppIcon;
private final CharSequence mAppName;
private final View.OnClickListener mOnClickListener;
private final View.OnTouchListener mOnTouchListener;
private final RunningTaskInfo mTaskInfo;
private final int mLayoutResId;
private final int mCaptionX;
private final int mCaptionY;
private int mMarginMenuTop;
private int mMarginMenuStart;
private int mMarginMenuSpacing;
private int mMenuWidth;
private int mAppInfoPillHeight;
private int mWindowingPillHeight;
private int mMoreActionsPillHeight;
private int mShadowRadius;
private int mCornerRadius;
HandleMenu(WindowDecoration parentDecor, int layoutResId, int captionX, int captionY,
View.OnClickListener onClickListener, View.OnTouchListener onTouchListener,
Drawable appIcon, CharSequence appName, boolean shouldShowWindowingPill) {
mParentDecor = parentDecor;
mContext = mParentDecor.mDecorWindowContext;
mTaskInfo = mParentDecor.mTaskInfo;
mLayoutResId = layoutResId;
mCaptionX = captionX;
mCaptionY = captionY;
mOnClickListener = onClickListener;
mOnTouchListener = onTouchListener;
mAppIcon = appIcon;
mAppName = appName;
mShouldShowWindowingPill = shouldShowWindowingPill;
loadHandleMenuDimensions();
updateHandleMenuPillPositions();
}
void show() {
final SurfaceSyncGroup ssg = new SurfaceSyncGroup(TAG);
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
createAppInfoPill(t, ssg);
if (mShouldShowWindowingPill) {
createWindowingPill(t, ssg);
}
createMoreActionsPill(t, ssg);
ssg.addTransaction(t);
ssg.markSyncReady();
setupHandleMenu();
}
private void createAppInfoPill(SurfaceControl.Transaction t, SurfaceSyncGroup ssg) {
final int x = (int) mAppInfoPillPosition.x;
final int y = (int) mAppInfoPillPosition.y;
mAppInfoPill = mParentDecor.addWindow(
R.layout.desktop_mode_window_decor_handle_menu_app_info_pill,
"Menu's app info pill",
t, ssg, x, y, mMenuWidth, mAppInfoPillHeight, mShadowRadius, mCornerRadius);
}
private void createWindowingPill(SurfaceControl.Transaction t, SurfaceSyncGroup ssg) {
final int x = (int) mWindowingPillPosition.x;
final int y = (int) mWindowingPillPosition.y;
mWindowingPill = mParentDecor.addWindow(
R.layout.desktop_mode_window_decor_handle_menu_windowing_pill,
"Menu's windowing pill",
t, ssg, x, y, mMenuWidth, mWindowingPillHeight, mShadowRadius, mCornerRadius);
}
private void createMoreActionsPill(SurfaceControl.Transaction t, SurfaceSyncGroup ssg) {
final int x = (int) mMoreActionsPillPosition.x;
final int y = (int) mMoreActionsPillPosition.y;
mMoreActionsPill = mParentDecor.addWindow(
R.layout.desktop_mode_window_decor_handle_menu_more_actions_pill,
"Menu's more actions pill",
t, ssg, x, y, mMenuWidth, mMoreActionsPillHeight, mShadowRadius, mCornerRadius);
}
/**
* Set up interactive elements and color of this handle menu
*/
private void setupHandleMenu() {
// App Info pill setup.
final View appInfoPillView = mAppInfoPill.mWindowViewHost.getView();
final ImageButton collapseBtn = appInfoPillView.findViewById(R.id.collapse_menu_button);
final ImageView appIcon = appInfoPillView.findViewById(R.id.application_icon);
final TextView appName = appInfoPillView.findViewById(R.id.application_name);
collapseBtn.setOnClickListener(mOnClickListener);
appInfoPillView.setOnTouchListener(mOnTouchListener);
appIcon.setImageDrawable(mAppIcon);
appName.setText(mAppName);
// Windowing pill setup.
if (mShouldShowWindowingPill) {
final View windowingPillView = mWindowingPill.mWindowViewHost.getView();
final ImageButton fullscreenBtn = windowingPillView.findViewById(
R.id.fullscreen_button);
final ImageButton splitscreenBtn = windowingPillView.findViewById(
R.id.split_screen_button);
final ImageButton floatingBtn = windowingPillView.findViewById(R.id.floating_button);
final ImageButton desktopBtn = windowingPillView.findViewById(R.id.desktop_button);
fullscreenBtn.setOnClickListener(mOnClickListener);
splitscreenBtn.setOnClickListener(mOnClickListener);
floatingBtn.setOnClickListener(mOnClickListener);
desktopBtn.setOnClickListener(mOnClickListener);
// The button corresponding to the windowing mode that the task is currently in uses a
// different color than the others.
final ColorStateList activeColorStateList = ColorStateList.valueOf(
mContext.getColor(R.color.desktop_mode_caption_menu_buttons_color_active));
final ColorStateList inActiveColorStateList = ColorStateList.valueOf(
mContext.getColor(R.color.desktop_mode_caption_menu_buttons_color_inactive));
fullscreenBtn.setImageTintList(
mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
? activeColorStateList : inActiveColorStateList);
splitscreenBtn.setImageTintList(
mTaskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW
? activeColorStateList : inActiveColorStateList);
floatingBtn.setImageTintList(mTaskInfo.getWindowingMode() == WINDOWING_MODE_PINNED
? activeColorStateList : inActiveColorStateList);
desktopBtn.setImageTintList(mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
? activeColorStateList : inActiveColorStateList);
}
// More Actions pill setup.
final View moreActionsPillView = mMoreActionsPill.mWindowViewHost.getView();
final Button closeBtn = moreActionsPillView.findViewById(R.id.close_button);
closeBtn.setOnClickListener(mOnClickListener);
}
/**
* Updates the handle menu pills' position variables to reflect their next positions
*/
private void updateHandleMenuPillPositions() {
final int menuX, menuY;
final int captionWidth = mTaskInfo.getConfiguration()
.windowConfiguration.getBounds().width();
if (mLayoutResId
== R.layout.desktop_mode_app_controls_window_decor) {
// Align the handle menu to the left of the caption.
menuX = mCaptionX + mMarginMenuStart;
menuY = mCaptionY + mMarginMenuTop;
} else {
// Position the handle menu at the center of the caption.
menuX = mCaptionX + (captionWidth / 2) - (mMenuWidth / 2);
menuY = mCaptionY + mMarginMenuStart;
}
// App Info pill setup.
final int appInfoPillY = menuY;
mAppInfoPillPosition.set(menuX, appInfoPillY);
final int windowingPillY, moreActionsPillY;
if (mShouldShowWindowingPill) {
windowingPillY = appInfoPillY + mAppInfoPillHeight + mMarginMenuSpacing;
mWindowingPillPosition.set(menuX, windowingPillY);
moreActionsPillY = windowingPillY + mWindowingPillHeight + mMarginMenuSpacing;
mMoreActionsPillPosition.set(menuX, moreActionsPillY);
} else {
// Just start after the end of the app info pill + margins.
moreActionsPillY = appInfoPillY + mAppInfoPillHeight + mMarginMenuSpacing;
mMoreActionsPillPosition.set(menuX, moreActionsPillY);
}
}
/**
* Update pill layout, in case task changes have caused positioning to change.
* @param t
*/
void relayout(SurfaceControl.Transaction t) {
if (mAppInfoPill != null) {
updateHandleMenuPillPositions();
t.setPosition(mAppInfoPill.mWindowSurface,
mAppInfoPillPosition.x, mAppInfoPillPosition.y);
// Only show windowing buttons in proto2. Proto1 uses a system-level mode only.
final boolean shouldShowWindowingPill = DesktopModeStatus.isProto2Enabled();
if (shouldShowWindowingPill) {
t.setPosition(mWindowingPill.mWindowSurface,
mWindowingPillPosition.x, mWindowingPillPosition.y);
}
t.setPosition(mMoreActionsPill.mWindowSurface,
mMoreActionsPillPosition.x, mMoreActionsPillPosition.y);
}
}
/**
* Check a passed MotionEvent if a click has occurred on any button on this caption
* Note this should only be called when a regular onClick is not possible
* (i.e. the button was clicked through status bar layer)
* @param ev the MotionEvent to compare against.
*/
void checkClickEvent(MotionEvent ev) {
final View appInfoPill = mAppInfoPill.mWindowViewHost.getView();
final ImageButton collapse = appInfoPill.findViewById(R.id.collapse_menu_button);
// Translate the input point from display coordinates to the same space as the collapse
// button, meaning its parent (app info pill view).
final PointF inputPoint = new PointF(ev.getX() - mAppInfoPillPosition.x,
ev.getY() - mAppInfoPillPosition.y);
if (pointInView(collapse, inputPoint.x, inputPoint.y)) {
mOnClickListener.onClick(collapse);
}
}
/**
* A valid menu input is one of the following:
* An input that happens in the menu views.
* Any input before the views have been laid out.
* @param inputPoint the input to compare against.
*/
boolean isValidMenuInput(PointF inputPoint) {
if (!viewsLaidOut()) return true;
final boolean pointInAppInfoPill = pointInView(
mAppInfoPill.mWindowViewHost.getView(),
inputPoint.x - mAppInfoPillPosition.x,
inputPoint.y - mAppInfoPillPosition.y);
boolean pointInWindowingPill = false;
if (mWindowingPill != null) {
pointInWindowingPill = pointInView(
mWindowingPill.mWindowViewHost.getView(),
inputPoint.x - mWindowingPillPosition.x,
inputPoint.y - mWindowingPillPosition.y);
}
final boolean pointInMoreActionsPill = pointInView(
mMoreActionsPill.mWindowViewHost.getView(),
inputPoint.x - mMoreActionsPillPosition.x,
inputPoint.y - mMoreActionsPillPosition.y);
return pointInAppInfoPill || pointInWindowingPill || pointInMoreActionsPill;
}
private boolean pointInView(View v, float x, float y) {
return v != null && v.getLeft() <= x && v.getRight() >= x
&& v.getTop() <= y && v.getBottom() >= y;
}
/**
* Check if the views for handle menu can be seen.
* @return
*/
private boolean viewsLaidOut() {
return mAppInfoPill.mWindowViewHost.getView().isLaidOut();
}
private void loadHandleMenuDimensions() {
final Resources resources = mContext.getResources();
mMenuWidth = loadDimensionPixelSize(resources,
R.dimen.desktop_mode_handle_menu_width);
mMarginMenuTop = loadDimensionPixelSize(resources,
R.dimen.desktop_mode_handle_menu_margin_top);
mMarginMenuStart = loadDimensionPixelSize(resources,
R.dimen.desktop_mode_handle_menu_margin_start);
mMarginMenuSpacing = loadDimensionPixelSize(resources,
R.dimen.desktop_mode_handle_menu_pill_spacing_margin);
mAppInfoPillHeight = loadDimensionPixelSize(resources,
R.dimen.desktop_mode_handle_menu_app_info_pill_height);
mWindowingPillHeight = loadDimensionPixelSize(resources,
R.dimen.desktop_mode_handle_menu_windowing_pill_height);
mMoreActionsPillHeight = loadDimensionPixelSize(resources,
R.dimen.desktop_mode_handle_menu_more_actions_pill_height);
mShadowRadius = loadDimensionPixelSize(resources,
R.dimen.desktop_mode_handle_menu_shadow_radius);
mCornerRadius = loadDimensionPixelSize(resources,
R.dimen.desktop_mode_handle_menu_corner_radius);
}
private int loadDimensionPixelSize(Resources resources, int resourceId) {
if (resourceId == Resources.ID_NULL) {
return 0;
}
return resources.getDimensionPixelSize(resourceId);
}
void close() {
mAppInfoPill.releaseView();
mAppInfoPill = null;
if (mWindowingPill != null) {
mWindowingPill.releaseView();
mWindowingPill = null;
}
mMoreActionsPill.releaseView();
mMoreActionsPill = null;
}
static final class Builder {
private final WindowDecoration mParent;
private CharSequence mName;
private Drawable mAppIcon;
private View.OnClickListener mOnClickListener;
private View.OnTouchListener mOnTouchListener;
private int mLayoutId;
private int mCaptionX;
private int mCaptionY;
private boolean mShowWindowingPill;
Builder(@NonNull WindowDecoration parent) {
mParent = parent;
}
Builder setAppName(@Nullable CharSequence name) {
mName = name;
return this;
}
Builder setAppIcon(@Nullable Drawable appIcon) {
mAppIcon = appIcon;
return this;
}
Builder setOnClickListener(@Nullable View.OnClickListener onClickListener) {
mOnClickListener = onClickListener;
return this;
}
Builder setOnTouchListener(@Nullable View.OnTouchListener onTouchListener) {
mOnTouchListener = onTouchListener;
return this;
}
Builder setLayoutId(int layoutId) {
mLayoutId = layoutId;
return this;
}
Builder setCaptionPosition(int captionX, int captionY) {
mCaptionX = captionX;
mCaptionY = captionY;
return this;
}
Builder setWindowingButtonsVisible(boolean windowingButtonsVisible) {
mShowWindowingPill = windowingButtonsVisible;
return this;
}
HandleMenu build() {
return new HandleMenu(mParent, mLayoutId, mCaptionX, mCaptionY, mOnClickListener,
mOnTouchListener, mAppIcon, mName, mShowWindowingPill);
}
}
}

View File

@@ -458,7 +458,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
SurfaceControlViewHost mWindowViewHost;
Supplier<SurfaceControl.Transaction> mTransactionSupplier;
private AdditionalWindow(SurfaceControl surfaceControl,
AdditionalWindow(SurfaceControl surfaceControl,
SurfaceControlViewHost surfaceControlViewHost,
Supplier<SurfaceControl.Transaction> transactionSupplier) {
mWindowSurface = surfaceControl;