Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 93fa1b04 authored by Matt Sziklay's avatar Matt Sziklay Committed by Android (Google) Code Review
Browse files

Merge "Add handle menu to TYPE_STATUS_BAR_ADDITIONAL layer." into main

parents 736591ce ba86a2a0
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -439,7 +439,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
            } else if (id == R.id.caption_handle || id == R.id.open_menu_button) {
                if (!decoration.isHandleMenuActive()) {
                    moveTaskToFront(decoration.mTaskInfo);
                    decoration.createHandleMenu();
                    decoration.createHandleMenu(mSplitScreenController);
                } else {
                    decoration.closeHandleMenu();
                }
+13 −2
Original line number Diff line number Diff line
@@ -68,6 +68,7 @@ import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.shared.DesktopModeStatus;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
import com.android.wm.shell.windowdecor.viewholder.AppHandleViewHolder;
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
@@ -650,7 +651,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
    /**
     * Create and display handle menu window.
     */
    void createHandleMenu() {
    void createHandleMenu(SplitScreenController splitScreenController) {
        loadAppInfoIfNeeded();
        mHandleMenu = new HandleMenu.Builder(this)
                .setAppIcon(mAppIconBitmap)
@@ -660,6 +661,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
                .setLayoutId(mRelayoutParams.mLayoutResId)
                .setWindowingButtonsVisible(DesktopModeStatus.canEnterDesktopMode(mContext))
                .setCaptionHeight(mResult.mCaptionHeight)
                .setDisplayController(mDisplayController)
                .setSplitScreenController(splitScreenController)
                .build();
        mWindowDecorViewHolder.onHandleMenuOpened();
        mHandleMenu.show();
@@ -815,11 +818,15 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
        // We want handle to remain pressed if the pointer moves outside of it during a drag.
        handle.setPressed((inHandle && action == ACTION_DOWN)
                || (handle.isPressed() && action != ACTION_UP && action != ACTION_CANCEL));
        if (isHandleMenuActive()) {
        if (isHandleMenuActive() && !isMenuAboveStatusBar()) {
            mHandleMenu.checkMotionEvent(ev);
        }
    }

    private boolean isMenuAboveStatusBar() {
        return Flags.enableAdditionalWindowsAboveStatusBar() && !mTaskInfo.isFreeform();
    }

    private boolean pointInView(View v, float x, float y) {
        return v != null && v.getLeft() <= x && v.getRight() >= x
                && v.getTop() <= y && v.getBottom() >= y;
@@ -868,6 +875,10 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
        return exclusionRegion;
    }

    int getCaptionX() {
        return mResult.mCaptionX;
    }

    @Override
    int getCaptionHeightId(@WindowingMode int windowingMode) {
        return getCaptionHeightIdStatic(windowingMode);
+97 −35
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_UP;

import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
@@ -34,6 +36,7 @@ import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.PointF;
import android.graphics.Rect;
import android.view.MotionEvent;
import android.view.SurfaceControl;
import android.view.View;
@@ -42,7 +45,15 @@ import android.widget.ImageView;
import android.widget.TextView;
import android.window.SurfaceSyncGroup;

import androidx.annotation.VisibleForTesting;

import com.android.window.flags.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer;
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewContainer;

/**
 * Handle menu opened when the appropriate button is clicked on.
@@ -56,15 +67,19 @@ class HandleMenu {
    private static final String TAG = "HandleMenu";
    private static final boolean SHOULD_SHOW_MORE_ACTIONS_PILL = false;
    private final Context mContext;
    private final WindowDecoration mParentDecor;
    private WindowDecoration.AdditionalWindow mHandleMenuWindow;
    private final PointF mHandleMenuPosition = new PointF();
    private final DesktopModeWindowDecoration mParentDecor;
    @VisibleForTesting
    AdditionalViewContainer mHandleMenuViewContainer;
    @VisibleForTesting
    final PointF mHandleMenuPosition = new PointF();
    private final boolean mShouldShowWindowingPill;
    private final Bitmap mAppIconBitmap;
    private final CharSequence mAppName;
    private final View.OnClickListener mOnClickListener;
    private final View.OnTouchListener mOnTouchListener;
    private final RunningTaskInfo mTaskInfo;
    private final DisplayController mDisplayController;
    private final SplitScreenController mSplitScreenController;
    private final int mLayoutResId;
    private int mMarginMenuTop;
    private int mMarginMenuStart;
@@ -74,12 +89,16 @@ class HandleMenu {
    private HandleMenuAnimator mHandleMenuAnimator;


    HandleMenu(WindowDecoration parentDecor, int layoutResId, View.OnClickListener onClickListener,
            View.OnTouchListener onTouchListener, Bitmap appIcon, CharSequence appName,
            boolean shouldShowWindowingPill, int captionHeight) {
    HandleMenu(DesktopModeWindowDecoration parentDecor, int layoutResId,
            View.OnClickListener onClickListener, View.OnTouchListener onTouchListener,
            Bitmap appIcon, CharSequence appName, DisplayController displayController,
            SplitScreenController splitScreenController, boolean shouldShowWindowingPill,
            int captionHeight) {
        mParentDecor = parentDecor;
        mContext = mParentDecor.mDecorWindowContext;
        mTaskInfo = mParentDecor.mTaskInfo;
        mDisplayController = displayController;
        mSplitScreenController = splitScreenController;
        mLayoutResId = layoutResId;
        mOnClickListener = onClickListener;
        mOnTouchListener = onTouchListener;
@@ -95,20 +114,27 @@ class HandleMenu {
        final SurfaceSyncGroup ssg = new SurfaceSyncGroup(TAG);
        SurfaceControl.Transaction t = new SurfaceControl.Transaction();

        createHandleMenuWindow(t, ssg);
        createHandleMenuViewContainer(t, ssg);
        ssg.addTransaction(t);
        ssg.markSyncReady();
        setupHandleMenu();
        animateHandleMenu();
    }

    private void createHandleMenuWindow(SurfaceControl.Transaction t, SurfaceSyncGroup ssg) {
    private void createHandleMenuViewContainer(SurfaceControl.Transaction t,
            SurfaceSyncGroup ssg) {
        final int x = (int) mHandleMenuPosition.x;
        final int y = (int) mHandleMenuPosition.y;
        mHandleMenuWindow = mParentDecor.addWindow(
        if (!mTaskInfo.isFreeform() && Flags.enableAdditionalWindowsAboveStatusBar()) {
            mHandleMenuViewContainer = new AdditionalSystemViewContainer(mContext,
                    R.layout.desktop_mode_window_decor_handle_menu, mTaskInfo.taskId,
                    x, y, mMenuWidth, mMenuHeight);
        } else {
            mHandleMenuViewContainer = mParentDecor.addWindow(
                    R.layout.desktop_mode_window_decor_handle_menu, "Handle Menu",
                    t, ssg, x, y, mMenuWidth, mMenuHeight);
        final View handleMenuView = mHandleMenuWindow.mWindowViewHost.getView();
        }
        final View handleMenuView = mHandleMenuViewContainer.getView();
        mHandleMenuAnimator = new HandleMenuAnimator(handleMenuView, mMenuWidth, mCaptionHeight);
    }

@@ -129,7 +155,7 @@ class HandleMenu {
     * pill.
     */
    private void setupHandleMenu() {
        final View handleMenu = mHandleMenuWindow.mWindowViewHost.getView();
        final View handleMenu = mHandleMenuViewContainer.getView();
        handleMenu.setOnTouchListener(mOnTouchListener);
        setupAppInfoPill(handleMenu);
        if (mShouldShowWindowingPill) {
@@ -147,6 +173,7 @@ class HandleMenu {
        final ImageView appIcon = handleMenu.findViewById(R.id.application_icon);
        final TextView appName = handleMenu.findViewById(R.id.application_name);
        collapseBtn.setOnClickListener(mOnClickListener);
        collapseBtn.setTaskInfo(mTaskInfo);
        appIcon.setImageBitmap(mAppIconBitmap);
        appName.setText(mAppName);
    }
@@ -215,32 +242,55 @@ class HandleMenu {
     * Updates handle menu's position variables to reflect its next position.
     */
    private void updateHandleMenuPillPositions() {
        final int menuX, menuY;
        final int captionWidth = mTaskInfo.getConfiguration()
                .windowConfiguration.getBounds().width();
        int menuX;
        final int menuY;
        if (mLayoutResId == R.layout.desktop_mode_app_header) {
            // Align the handle menu to the left of the caption.
            // Align the handle menu to the left side of the caption.
            menuX = mMarginMenuStart;
            menuY = mMarginMenuTop;
        } else {
            // Position the handle menu at the center of the caption.
            final int handleWidth = loadDimensionPixelSize(mContext.getResources(),
                    R.dimen.desktop_mode_fullscreen_decor_caption_width);
            final int handleOffset = (mMenuWidth / 2) - (handleWidth / 2);
            final int captionX = mParentDecor.getCaptionX();
            // TODO(b/343561161): This needs to be calculated differently if the task is in
            //  top/bottom split.
            if (Flags.enableAdditionalWindowsAboveStatusBar()) {
                final Rect leftOrTopStageBounds = new Rect();
                if (mSplitScreenController.getSplitPosition(mTaskInfo.taskId)
                        == SPLIT_POSITION_BOTTOM_OR_RIGHT) {
                    mSplitScreenController.getStageBounds(leftOrTopStageBounds, new Rect());
                }
                // In a focused decor, we use global coordinates for handle menu. Therefore we
                // need to account for other factors like split stage and menu/handle width to
                // center the menu.
                final DisplayLayout layout = mDisplayController
                        .getDisplayLayout(mTaskInfo.displayId);
                menuX = captionX + handleOffset - (layout.width() / 2);
                if (mSplitScreenController.getSplitPosition(mTaskInfo.taskId)
                        == SPLIT_POSITION_BOTTOM_OR_RIGHT && layout.isLandscape()) {
                    // If this task in the right stage, we need to offset by left stage's width
                    menuX += leftOrTopStageBounds.width();
                }
                menuY = mMarginMenuStart - ((layout.height() - mMenuHeight) / 2);
            } else {
                final int captionWidth = mTaskInfo.getConfiguration()
                        .windowConfiguration.getBounds().width();
                menuX = (captionWidth / 2) - (mMenuWidth / 2);
            menuY = mMarginMenuStart;
                menuY = mMarginMenuTop;
            }
        }

        // Handle Menu position setup.
        mHandleMenuPosition.set(menuX, menuY);

    }

    /**
     * Update pill layout, in case task changes have caused positioning to change.
     */
    void relayout(SurfaceControl.Transaction t) {
        if (mHandleMenuWindow != null) {
        if (mHandleMenuViewContainer != null) {
            updateHandleMenuPillPositions();
            t.setPosition(mHandleMenuWindow.mWindowSurface,
                    mHandleMenuPosition.x, mHandleMenuPosition.y);
            mHandleMenuViewContainer.setPosition(t, mHandleMenuPosition.x, mHandleMenuPosition.y);
        }
    }

@@ -252,7 +302,7 @@ class HandleMenu {
     * @param ev the MotionEvent to compare against.
     */
    void checkMotionEvent(MotionEvent ev) {
        final View handleMenu = mHandleMenuWindow.mWindowViewHost.getView();
        final View handleMenu = mHandleMenuViewContainer.getView();
        final HandleMenuImageButton collapse = handleMenu.findViewById(R.id.collapse_menu_button);
        final PointF inputPoint = translateInputToLocalSpace(ev);
        final boolean inputInCollapseButton = pointInView(collapse, inputPoint.x, inputPoint.y);
@@ -280,7 +330,7 @@ class HandleMenu {
    boolean isValidMenuInput(PointF inputPoint) {
        if (!viewsLaidOut()) return true;
        return pointInView(
                mHandleMenuWindow.mWindowViewHost.getView(),
                mHandleMenuViewContainer.getView(),
                inputPoint.x - mHandleMenuPosition.x,
                inputPoint.y - mHandleMenuPosition.y);
    }
@@ -294,7 +344,7 @@ class HandleMenu {
     * Check if the views for handle menu can be seen.
     */
    private boolean viewsLaidOut() {
        return mHandleMenuWindow.mWindowViewHost.getView().isLaidOut();
        return mHandleMenuViewContainer.getView().isLaidOut();
    }

    private void loadHandleMenuDimensions() {
@@ -333,8 +383,8 @@ class HandleMenu {

    void close() {
        final Runnable after = () -> {
            mHandleMenuWindow.releaseView();
            mHandleMenuWindow = null;
            mHandleMenuViewContainer.releaseView();
            mHandleMenuViewContainer = null;
        };
        if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
                || mTaskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) {
@@ -345,7 +395,7 @@ class HandleMenu {
    }

    static final class Builder {
        private final WindowDecoration mParent;
        private final DesktopModeWindowDecoration mParent;
        private CharSequence mName;
        private Bitmap mAppIcon;
        private View.OnClickListener mOnClickListener;
@@ -353,9 +403,10 @@ class HandleMenu {
        private int mLayoutId;
        private boolean mShowWindowingPill;
        private int mCaptionHeight;
        private DisplayController mDisplayController;
        private SplitScreenController mSplitScreenController;


        Builder(@NonNull WindowDecoration parent) {
        Builder(@NonNull DesktopModeWindowDecoration parent) {
            mParent = parent;
        }

@@ -394,9 +445,20 @@ class HandleMenu {
            return this;
        }

        Builder setDisplayController(DisplayController displayController) {
            mDisplayController = displayController;
            return this;
        }

        Builder setSplitScreenController(SplitScreenController splitScreenController) {
            mSplitScreenController = splitScreenController;
            return this;
        }

        HandleMenu build() {
            return new HandleMenu(mParent, mLayoutId, mOnClickListener, mOnTouchListener,
                    mAppIcon, mName, mShowWindowingPill, mCaptionHeight);
            return new HandleMenu(mParent, mLayoutId, mOnClickListener,
                    mOnTouchListener, mAppIcon, mName, mDisplayController, mSplitScreenController,
                    mShowWindowingPill, mCaptionHeight);
        }
    }
}
+17 −3
Original line number Diff line number Diff line
@@ -15,6 +15,10 @@
 */
package com.android.wm.shell.windowdecor

import android.app.ActivityManager.RunningTaskInfo
import com.android.window.flags.Flags
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer

import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
@@ -25,10 +29,20 @@ import android.widget.ImageButton
 * This is due to the hover events being handled by [DesktopModeWindowDecorViewModel]
 * in order to take the status bar layer into account. Handling it in both classes results in a
 * flicker when the hover moves from outside to inside status bar layer.
 * TODO(b/342229481): Remove this and all uses of it once [AdditionalSystemViewContainer] is no longer
 *  guarded by a flag.
 */
class HandleMenuImageButton(context: Context?, attrs: AttributeSet?) :
    ImageButton(context, attrs) {
class HandleMenuImageButton(
    context: Context?,
    attrs: AttributeSet?
) : ImageButton(context, attrs) {
    lateinit var taskInfo: RunningTaskInfo

    override fun onHoverEvent(motionEvent: MotionEvent): Boolean {
        if (Flags.enableAdditionalWindowsAboveStatusBar() || taskInfo.isFreeform) {
            return super.onHoverEvent(motionEvent)
        } else {
            return false
        }
    }
}
+7 −7
Original line number Diff line number Diff line
@@ -50,7 +50,7 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.animation.Interpolators.EMPHASIZED_DECELERATE
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.windowdecor.WindowDecoration.AdditionalWindow
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer
import java.util.function.Supplier


@@ -70,7 +70,7 @@ class MaximizeMenu(
        private val menuPosition: PointF,
        private val transactionSupplier: Supplier<Transaction> = Supplier { Transaction() }
) {
    private var maximizeMenu: AdditionalWindow? = null
    private var maximizeMenu: AdditionalViewHostViewContainer? = null
    private lateinit var viewHost: SurfaceControlViewHost
    private lateinit var leash: SurfaceControl
    private val openMenuAnimatorSet = AnimatorSet()
@@ -145,7 +145,8 @@ class MaximizeMenu(
                .setPosition(leash, menuPosition.x, menuPosition.y)
                .setCornerRadius(leash, cornerRadius)
                .show(leash)
        maximizeMenu = AdditionalWindow(leash, viewHost, transactionSupplier)
        maximizeMenu =
            AdditionalViewHostViewContainer(leash, viewHost, transactionSupplier)

        syncQueue.runInSync { transaction ->
            transaction.merge(t)
@@ -154,8 +155,7 @@ class MaximizeMenu(
    }

    private fun animateOpenMenu() {
        val viewHost = maximizeMenu?.mWindowViewHost
        val maximizeMenuView = viewHost?.view ?: return
        val maximizeMenuView = maximizeMenu?.view ?: return
        val maximizeWindowText = maximizeMenuView.requireViewById<TextView>(
                R.id.maximize_menu_maximize_window_text)
        val snapWindowText = maximizeMenuView.requireViewById<TextView>(
@@ -233,7 +233,7 @@ class MaximizeMenu(
    }

    private fun setupMaximizeMenu() {
        val maximizeMenuView = maximizeMenu?.mWindowViewHost?.view ?: return
        val maximizeMenuView = maximizeMenu?.view ?: return

        maximizeMenuView.setOnGenericMotionListener(onGenericMotionListener)
        maximizeMenuView.setOnTouchListener(onTouchListener)
@@ -275,7 +275,7 @@ class MaximizeMenu(
     * Check if the views for maximize menu can be seen.
     */
    private fun viewsLaidOut(): Boolean {
        return maximizeMenu?.mWindowViewHost?.view?.isLaidOut ?: false
        return maximizeMenu?.view?.isLaidOut ?: false
    }

    fun onMaximizeMenuHoverEnter(viewId: Int, ev: MotionEvent) {
Loading