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

Commit 696eb14f authored by Jorge Gil's avatar Jorge Gil
Browse files

Update HandleMenu coordinates and tests

Updates HandleMenu position calculation so that:
1) AdditionalSystemViewContainer can take a global x,y position, whereas
   before it needed pre-adjusted coordinates to adjust to default
   Gravity.CENTER gravity of the LayoutParams. By changing the params'
   gravity to TOP-LEFT, callers can send global (x,y) position without
   additional adjustments.
2) Removed marginStart from x value of the HandleMenu position when in
   fullscreen/split - as that is only intended for the freeform's menu
   (to provide separation from the task edge). In fullscreen and split
   this cause the menu to be slightly off-center.
3) Updated HandleMenuTests so:
  - As a result of (1), expected position values use coordinates relative
  to the display bounds, which is consistent with the test comments. So
  there won't be expected negative values.
  - Added resource overrides for width/height/margins, so that changing
    the real resources don't cause the test to fail, nor changing the
    device's density, which would resolve the real values (in dp) to
    different than expected px.
  - Updated flagged tests to use @EnableFlags, otherwise flagged tests
    may not run in presubmit if the flag is disabled and are prone to
    breaking.

Bug: 349464246
Test: atest HandleMenuTests
Flag: com.android.window.flags.enable_additional_windows_above_status_bar
Change-Id: Iffde19fa49cf0db097b9dd4abf504289f2af823d
parent 76d684ea
Loading
Loading
Loading
Loading
+5 −8
Original line number Diff line number Diff line
@@ -53,7 +53,6 @@ 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;
@@ -276,10 +275,8 @@ class HandleMenu {
                // 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 = mGlobalMenuPosition.x + ((mMenuWidth - layout.width()) / 2);
                menuY = mGlobalMenuPosition.y + ((mMenuHeight - layout.height()) / 2);
                menuX = mGlobalMenuPosition.x;
                menuY = mGlobalMenuPosition.y;
            } else {
                menuX = (taskBounds.width() / 2) - (mMenuWidth / 2);
                menuY = mMarginMenuTop;
@@ -295,7 +292,7 @@ class HandleMenu {
                    taskBounds.top + mMarginMenuTop);
        } else if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
            mGlobalMenuPosition.set(
                    (taskBounds.width() / 2) - (mMenuWidth / 2) + mMarginMenuStart,
                    (taskBounds.width() / 2) - (mMenuWidth / 2),
                    mMarginMenuTop
            );
        } else if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) {
@@ -309,11 +306,11 @@ class HandleMenu {
            if (splitPosition == SPLIT_POSITION_BOTTOM_OR_RIGHT) {
                mGlobalMenuPosition.set(leftOrTopStageBounds.width()
                                + (rightOrBottomStageBounds.width() / 2)
                                - (mMenuWidth / 2) + mMarginMenuStart,
                                - (mMenuWidth / 2),
                        mMarginMenuTop);
            } else if (splitPosition == SPLIT_POSITION_TOP_OR_LEFT) {
                mGlobalMenuPosition.set((leftOrTopStageBounds.width() / 2)
                                - (mMenuWidth / 2) + mMarginMenuStart,
                                - (mMenuWidth / 2),
                        mMarginMenuTop);
            }
        }
+6 −3
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.wm.shell.windowdecor.additionalviewcontainer

import android.content.Context
import android.graphics.PixelFormat
import android.view.Gravity
import android.view.LayoutInflater
import android.view.SurfaceControl
import android.view.View
@@ -45,9 +46,11 @@ class AdditionalSystemViewContainer(
            WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL,
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
            PixelFormat.TRANSPARENT
        )
        lp.title = "Additional view container of Task=$taskId"
        lp.setTrustedOverlay()
        ).apply {
            title = "Additional view container of Task=$taskId"
            gravity = Gravity.LEFT or Gravity.TOP
            setTrustedOverlay()
        }
        val wm: WindowManager? = context.getSystemService(WindowManager::class.java)
        wm?.addView(view, lp)
    }
+34 −22
Original line number Diff line number Diff line
@@ -22,10 +22,10 @@ import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.Point
import android.graphics.Rect
import android.platform.test.annotations.RequiresFlagsEnabled
import android.platform.test.flag.junit.CheckFlagsRule
import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.Display
@@ -33,6 +33,7 @@ import android.view.LayoutInflater
import android.view.SurfaceControl
import android.view.SurfaceControlViewHost
import android.view.View
import androidx.core.graphics.toPointF
import androidx.test.filters.SmallTest
import com.android.window.flags.Flags
import com.android.wm.shell.R
@@ -46,6 +47,7 @@ import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UND
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Rule
@@ -69,7 +71,7 @@ import org.mockito.kotlin.whenever
class HandleMenuTest : ShellTestCase() {
    @JvmField
    @Rule
    val mCheckFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
    val setFlagsRule: SetFlagsRule = SetFlagsRule()

    @Mock
    private lateinit var mockDesktopWindowDecoration: DesktopModeWindowDecoration
@@ -100,7 +102,7 @@ class HandleMenuTest : ShellTestCase() {
        ) {
            SurfaceControl.Transaction()
        }
        val menuView = LayoutInflater.from(context).inflate(
        val menuView = LayoutInflater.from(mContext).inflate(
            R.layout.desktop_mode_window_decor_handle_menu, null)
        whenever(mockDesktopWindowDecoration.addWindow(
            anyInt(), any(), any(), any(), anyInt(), anyInt(), anyInt(), anyInt())
@@ -110,50 +112,60 @@ class HandleMenuTest : ShellTestCase() {
        whenever(displayLayout.width()).thenReturn(DISPLAY_BOUNDS.width())
        whenever(displayLayout.height()).thenReturn(DISPLAY_BOUNDS.height())
        whenever(displayLayout.isLandscape).thenReturn(true)
        mockDesktopWindowDecoration.mDecorWindowContext = context
        mContext.orCreateTestableResources.apply {
            addOverride(R.dimen.desktop_mode_handle_menu_width, MENU_WIDTH)
            addOverride(R.dimen.desktop_mode_handle_menu_height, MENU_HEIGHT)
            addOverride(R.dimen.desktop_mode_handle_menu_margin_top, MENU_TOP_MARGIN)
            addOverride(R.dimen.desktop_mode_handle_menu_margin_start, MENU_START_MARGIN)
        }
        mockDesktopWindowDecoration.mDecorWindowContext = mContext
    }

    @Test
    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
    @EnableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
    fun testFullscreenMenuUsesSystemViewContainer() {
        createTaskInfo(WINDOWING_MODE_FULLSCREEN, SPLIT_POSITION_UNDEFINED)
        val handleMenu = createAndShowHandleMenu()
        assertTrue(handleMenu.mHandleMenuViewContainer is AdditionalSystemViewContainer)
        // Verify menu is created at coordinates that, when added to WindowManager,
        // show at the top-center of display.
        assertTrue(handleMenu.mHandleMenuPosition.equals(16f, -512f))
        val expected = Point(DISPLAY_BOUNDS.centerX() - MENU_WIDTH / 2, MENU_TOP_MARGIN)
        assertEquals(expected.toPointF(), handleMenu.mHandleMenuPosition)
    }

    @Test
    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
    @EnableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
    fun testFreeformMenu_usesViewHostViewContainer() {
        createTaskInfo(WINDOWING_MODE_FREEFORM, SPLIT_POSITION_UNDEFINED)
        handleMenu = createAndShowHandleMenu()
        assertTrue(handleMenu.mHandleMenuViewContainer is AdditionalViewHostViewContainer)
        // Verify menu is created near top-left of task.
        assertTrue(handleMenu.mHandleMenuPosition.equals(12f, 8f))
        val expected = Point(MENU_START_MARGIN, MENU_TOP_MARGIN)
        assertEquals(expected.toPointF(), handleMenu.mHandleMenuPosition)
    }

    @Test
    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
    @EnableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
    fun testSplitLeftMenu_usesSystemViewContainer() {
        createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, SPLIT_POSITION_TOP_OR_LEFT)
        handleMenu = createAndShowHandleMenu()
        assertTrue(handleMenu.mHandleMenuViewContainer is AdditionalSystemViewContainer)
        // Verify menu is created at coordinates that, when added to WindowManager,
        // show at the top of split left task.
        assertTrue(handleMenu.mHandleMenuPosition.equals(-624f, -512f))
        // show at the top-center of split left task.
        val expected = Point(SPLIT_LEFT_BOUNDS.centerX() - MENU_WIDTH / 2, MENU_TOP_MARGIN)
        assertEquals(expected.toPointF(), handleMenu.mHandleMenuPosition)
    }

    @Test
    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
    @EnableFlags(Flags.FLAG_ENABLE_ADDITIONAL_WINDOWS_ABOVE_STATUS_BAR)
    fun testSplitRightMenu_usesSystemViewContainer() {
        createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, SPLIT_POSITION_BOTTOM_OR_RIGHT)
        handleMenu = createAndShowHandleMenu()
        assertTrue(handleMenu.mHandleMenuViewContainer is AdditionalSystemViewContainer)
        // Verify menu is created at coordinates that, when added to WindowManager,
        // show at the top of split right task.
        assertTrue(handleMenu.mHandleMenuPosition.equals(656f, -512f))
        // show at the top-center of split right task.
        val expected = Point(SPLIT_RIGHT_BOUNDS.centerX() - MENU_WIDTH / 2, MENU_TOP_MARGIN)
        assertEquals(expected.toPointF(), handleMenu.mHandleMenuPosition)
    }

    private fun createTaskInfo(windowingMode: Int, splitPosition: Int) {
@@ -178,14 +190,10 @@ class HandleMenuTest : ShellTestCase() {
            .setBounds(bounds)
            .setVisible(true)
            .build()
        // Calculate captionX similar to how WindowDecoration calculates it.
        whenever(mockDesktopWindowDecoration.captionX).thenReturn(
            (mockDesktopWindowDecoration.mTaskInfo.configuration.windowConfiguration
                .bounds.width() - context.resources.getDimensionPixelSize(
                R.dimen.desktop_mode_fullscreen_decor_caption_width)) / 2)
        whenever(splitScreenController.getSplitPosition(any())).thenReturn(splitPosition)
        whenever(splitScreenController.getStageBounds(any(), any())).thenAnswer {
            (it.arguments.first() as Rect).set(SPLIT_LEFT_BOUNDS)
            (it.arguments[1] as Rect).set(SPLIT_RIGHT_BOUNDS)
        }
    }

@@ -193,7 +201,7 @@ class HandleMenuTest : ShellTestCase() {
        val layoutId = if (mockDesktopWindowDecoration.mTaskInfo.isFreeform) {
            R.layout.desktop_mode_app_header
        } else {
            R.layout.desktop_mode_app_header
            R.layout.desktop_mode_app_handle
        }
        val handleMenu = HandleMenu(mockDesktopWindowDecoration, layoutId,
                onClickListener, onTouchListener, appIcon, appName, displayController,
@@ -208,5 +216,9 @@ class HandleMenuTest : ShellTestCase() {
        private val FREEFORM_BOUNDS = Rect(500, 500, 2000, 1200)
        private val SPLIT_LEFT_BOUNDS = Rect(0, 0, 1280, 1600)
        private val SPLIT_RIGHT_BOUNDS = Rect(1280, 0, 2560, 1600)
        private const val MENU_WIDTH = 200
        private const val MENU_HEIGHT = 400
        private const val MENU_TOP_MARGIN = 10
        private const val MENU_START_MARGIN = 20
    }
}