diff options
3 files changed, 422 insertions, 215 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java index 619e963aaea6..f9fdd831a42c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java @@ -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; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java new file mode 100644 index 000000000000..ed3cca078084 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java @@ -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); + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java index 9a1b4ffbd50c..e772fc25f8cf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java @@ -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; |