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

Commit 6017894b authored by Bartosz Chominski's avatar Bartosz Chominski
Browse files

Make maximize menu's dimensions flexible

Maximize menu's labels should not be truncated or ellipsized but instead
the menu itself should resize accordingly.

UI spec: https://www.figma.com/design/8GROgIkVJuUcSOnScz1HWx/V-%E2%80%A2-Windowing-POR?node-id=5718-45454&t=L228ojIJ6SpRnf5W-0

Bug: 356212470
Test: manually verified that labels are legible and that the layout is
up to the spec with small/large text and English/Polish language
options.
Flag: com.android.window.flags.enable_desktop_windowing_mode

Change-Id: I3c7d3ec14a77e85766916270cd73f1e11aeadb41
parent 1b0b0490
Loading
Loading
Loading
Loading
+35 −19
Original line number Diff line number Diff line
@@ -18,23 +18,28 @@
    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
    android:id="@+id/maximize_menu"
    android:layout_width="wrap_content"
    android:layout_height="@dimen/desktop_mode_maximize_menu_height"
    android:layout_height="wrap_content"
    android:background="@drawable/desktop_mode_maximize_menu_background"
    android:elevation="1dp">

    <LinearLayout
        android:id="@+id/container"
        android:layout_width="wrap_content"
        android:layout_height="@dimen/desktop_mode_maximize_menu_height"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:padding="16dp"
        android:paddingHorizontal="12dp"
        android:paddingVertical="16dp"
        android:measureWithLargestChild="true"
        android:gravity="center">

        <LinearLayout
            android:id="@+id/maximize_menu_immersive_toggle_container"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical">
            android:layout_weight="1"
            android:orientation="vertical"
            android:layout_marginStart="4dp"
            android:layout_marginEnd="4dp">

            <Button
                android:layout_width="94dp"
@@ -44,21 +49,22 @@
                android:stateListAnimator="@null"
                android:importantForAccessibility="yes"
                android:contentDescription="@string/desktop_mode_maximize_menu_immersive_button_text"
                android:layout_marginEnd="8dp"
                android:layout_marginBottom="4dp"
                android:alpha="0"/>

            <TextView
                android:id="@+id/maximize_menu_immersive_toggle_button_text"
                android:layout_width="94dp"
                android:layout_height="18dp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="11sp"
                android:layout_marginBottom="76dp"
                android:lineHeight="16sp"
                android:gravity="center"
                android:fontFamily="google-sans-text"
                android:textFontWeight="500"
                android:importantForAccessibility="no"
                android:text="@string/desktop_mode_maximize_menu_immersive_button_text"
                android:textColor="@androidprv:color/materialColorOnSurface"
                android:singleLine="true"
                android:alpha="0"/>
        </LinearLayout>

@@ -66,7 +72,11 @@
            android:id="@+id/maximize_menu_size_toggle_container"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical">
            android:layout_weight="1"
            android:orientation="vertical"
            android:gravity="center_horizontal"
            android:layout_marginStart="4dp"
            android:layout_marginEnd="4dp">

            <Button
                android:layout_width="94dp"
@@ -81,15 +91,17 @@

            <TextView
                android:id="@+id/maximize_menu_size_toggle_button_text"
                android:layout_width="94dp"
                android:layout_height="18dp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="11sp"
                android:layout_marginBottom="76dp"
                android:lineHeight="16sp"
                android:gravity="center"
                android:fontFamily="google-sans-text"
                android:textFontWeight="500"
                android:importantForAccessibility="no"
                android:text="@string/desktop_mode_maximize_menu_maximize_text"
                android:textColor="@androidprv:color/materialColorOnSurface"
                android:singleLine="true"
                android:alpha="0"/>
        </LinearLayout>

@@ -97,7 +109,11 @@
            android:id="@+id/maximize_menu_snap_container"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical">
            android:layout_weight="1"
            android:orientation="vertical"
            android:gravity="center_horizontal"
            android:layout_marginStart="4dp"
            android:layout_marginEnd="4dp">
            <LinearLayout
                android:id="@+id/maximize_menu_snap_menu_layout"
                android:layout_width="wrap_content"
@@ -106,7 +122,6 @@
                android:padding="4dp"
                android:background="@drawable/desktop_mode_maximize_menu_layout_background"
                android:layout_marginBottom="4dp"
                android:layout_marginStart="8dp"
                android:alpha="0">
                <Button
                    android:id="@+id/maximize_menu_snap_left_button"
@@ -131,16 +146,17 @@
            </LinearLayout>
            <TextView
                android:id="@+id/maximize_menu_snap_window_text"
                android:layout_width="94dp"
                android:layout_height="18dp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="11sp"
                android:layout_marginBottom="76dp"
                android:layout_gravity="center"
                android:lineHeight="16sp"
                android:gravity="center"
                android:importantForAccessibility="no"
                android:fontFamily="google-sans-text"
                android:textFontWeight="500"
                android:text="@string/desktop_mode_maximize_menu_snap_text"
                android:textColor="@androidprv:color/materialColorOnSurface"
                android:singleLine="true"
                android:alpha="0"/>
        </LinearLayout>
    </LinearLayout>
@@ -150,6 +166,6 @@
    <View
        android:id="@+id/maximize_menu_overlay"
        android:layout_width="match_parent"
        android:layout_height="@dimen/desktop_mode_maximize_menu_height"/>
        android:layout_height="match_parent"/>
</FrameLayout>
+0 −8
Original line number Diff line number Diff line
@@ -487,14 +487,6 @@
    <!-- The default minimum allowed window height when resizing a window in desktop mode. -->
    <dimen name="desktop_mode_minimum_window_height">352dp</dimen>

    <!-- The width of the maximize menu in desktop mode, depending on the number of options -->
    <dimen name="desktop_mode_maximize_menu_width_one_options">126dp</dimen>
    <dimen name="desktop_mode_maximize_menu_width_two_options">228dp</dimen>
    <dimen name="desktop_mode_maximize_menu_width_three_options">330dp</dimen>

    <!-- The height of the maximize menu in desktop mode. -->
    <dimen name="desktop_mode_maximize_menu_height">114dp</dimen>

    <!-- The padding of the maximize menu in desktop mode. -->
    <dimen name="desktop_mode_menu_padding">16dp</dimen>

+4 −29
Original line number Diff line number Diff line
@@ -803,8 +803,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
        if (!mTaskInfo.isVisible()) {
            closeMaximizeMenu();
        } else {
            final int menuWidth = calculateMaximizeMenuWidth();
            mMaximizeMenu.positionMenu(calculateMaximizeMenuPosition(menuWidth), startT);
            mMaximizeMenu.positionMenu(startT);
        }
    }

@@ -1069,27 +1068,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
        return Resources.ID_NULL;
    }

    private int calculateMaximizeMenuWidth() {
        final boolean showImmersive = DesktopModeFlags.ENABLE_FULLY_IMMERSIVE_IN_DESKTOP.isTrue()
                && TaskInfoKt.getRequestingImmersive(mTaskInfo);
        final boolean showMaximize = true;
        final boolean showSnaps = mTaskInfo.isResizeable;
        int showCount = 0;
        if (showImmersive) showCount++;
        if (showMaximize) showCount++;
        if (showSnaps) showCount++;
        return switch (showCount) {
            case 1 -> loadDimensionPixelSize(mContext.getResources(),
                    R.dimen.desktop_mode_maximize_menu_width_one_options);
            case 2 -> loadDimensionPixelSize(mContext.getResources(),
                    R.dimen.desktop_mode_maximize_menu_width_two_options);
            case 3 -> loadDimensionPixelSize(mContext.getResources(),
                    R.dimen.desktop_mode_maximize_menu_width_three_options);
            default -> throw new IllegalArgumentException("");
        };
    }

    private PointF calculateMaximizeMenuPosition(int menuWidth) {
    private PointF calculateMaximizeMenuPosition(int menuWidth, int menuHeight) {
        final PointF position = new PointF();
        final Resources resources = mContext.getResources();
        final DisplayLayout displayLayout =
@@ -1105,9 +1084,6 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
        final int[] maximizeButtonLocation = new int[2];
        maximizeWindowButton.getLocationInWindow(maximizeButtonLocation);

        final int menuHeight = loadDimensionPixelSize(
                resources, R.dimen.desktop_mode_maximize_menu_height);

        float menuLeft = (mPositionInParent.x + maximizeButtonLocation[0] - ((float) (menuWidth
                - maximizeWindowButton.getWidth()) / 2));
        float menuTop = (mPositionInParent.y + captionHeight);
@@ -1294,17 +1270,16 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
     * Create and display maximize menu window
     */
    void createMaximizeMenu() {
        final int menuWidth = calculateMaximizeMenuWidth();
        mMaximizeMenu = mMaximizeMenuFactory.create(mSyncQueue, mRootTaskDisplayAreaOrganizer,
                mDisplayController, mTaskInfo, mContext,
                calculateMaximizeMenuPosition(menuWidth), mSurfaceControlTransactionSupplier);
                (width, height) -> calculateMaximizeMenuPosition(width, height),
                mSurfaceControlTransactionSupplier);

        mMaximizeMenu.show(
                /* isTaskInImmersiveMode= */
                DesktopModeFlags.ENABLE_FULLY_IMMERSIVE_IN_DESKTOP.isTrue()
                        && mDesktopUserRepositories.getProfile(mTaskInfo.userId)
                            .isTaskInFullImmersiveState(mTaskInfo.taskId),
                /* menuWidth= */ menuWidth,
                /* showImmersiveOption= */
                DesktopModeFlags.ENABLE_FULLY_IMMERSIVE_IN_DESKTOP.isTrue()
                        && TaskInfoKt.getRequestingImmersive(mTaskInfo),
+37 −26
Original line number Diff line number Diff line
@@ -90,7 +90,7 @@ class MaximizeMenu(
        private val displayController: DisplayController,
        private val taskInfo: RunningTaskInfo,
        private val decorWindowContext: Context,
        private val menuPosition: PointF,
        private val positionSupplier: (Int, Int) -> PointF,
        private val transactionSupplier: Supplier<Transaction> = Supplier { Transaction() }
) {
    private var maximizeMenu: AdditionalViewHostViewContainer? = null
@@ -100,19 +100,19 @@ class MaximizeMenu(
    private val cornerRadius = loadDimensionPixelSize(
            R.dimen.desktop_mode_maximize_menu_corner_radius
    ).toFloat()
    private val menuHeight = loadDimensionPixelSize(R.dimen.desktop_mode_maximize_menu_height)
    private lateinit var menuPosition: PointF
    private val menuPadding = loadDimensionPixelSize(R.dimen.desktop_mode_menu_padding)

    /** Position the menu relative to the caption's position. */
    fun positionMenu(position: PointF, t: Transaction) {
        menuPosition.set(position)
    fun positionMenu(t: Transaction) {
        menuPosition = positionSupplier(maximizeMenuView?.measureWidth() ?: 0,
                                        maximizeMenuView?.measureHeight() ?: 0)
        t.setPosition(leash, menuPosition.x, menuPosition.y)
    }

    /** Creates and shows the maximize window. */
    fun show(
        isTaskInImmersiveMode: Boolean,
        menuWidth: Int,
        showImmersiveOption: Boolean,
        showSnapOptions: Boolean,
        onMaximizeOrRestoreClickListener: () -> Unit,
@@ -125,7 +125,6 @@ class MaximizeMenu(
        if (maximizeMenu != null) return
        createMaximizeMenu(
            isTaskInImmersiveMode = isTaskInImmersiveMode,
            menuWidth = menuWidth,
            showImmersiveOption = showImmersiveOption,
            showSnapOptions = showSnapOptions,
            onMaximizeClickListener = onMaximizeOrRestoreClickListener,
@@ -161,7 +160,6 @@ class MaximizeMenu(
    /** Create a maximize menu that is attached to the display area. */
    private fun createMaximizeMenu(
        isTaskInImmersiveMode: Boolean,
        menuWidth: Int,
        showImmersiveOption: Boolean,
        showSnapOptions: Boolean,
        onMaximizeClickListener: () -> Unit,
@@ -178,16 +176,6 @@ class MaximizeMenu(
                .setName("Maximize Menu")
                .setContainerLayer()
                .build()
        val lp = WindowManager.LayoutParams(
                menuWidth,
                menuHeight,
                WindowManager.LayoutParams.TYPE_APPLICATION,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                        or WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
                PixelFormat.TRANSPARENT
        )
        lp.title = "Maximize Menu for Task=" + taskInfo.taskId
        lp.setTrustedOverlay()
        val windowManager = WindowlessWindowManager(
                taskInfo.configuration,
                leash,
@@ -207,7 +195,6 @@ class MaximizeMenu(
                MaximizeMenuView.ImmersiveConfig.Hidden
            },
            showSnapOptions = showSnapOptions,
            menuHeight = menuHeight,
            menuPadding = menuPadding,
        ).also { menuView ->
            menuView.bind(taskInfo)
@@ -217,6 +204,19 @@ class MaximizeMenu(
            menuView.onRightSnapClickListener = onRightSnapClickListener
            menuView.onMenuHoverListener = onHoverListener
            menuView.onOutsideTouchListener = onOutsideTouchListener
            val menuWidth = menuView.measureWidth()
            val menuHeight = menuView.measureHeight()
            menuPosition = positionSupplier(menuWidth, menuHeight)
            val lp = WindowManager.LayoutParams(
                    menuWidth.toInt(),
                    menuHeight.toInt(),
                    WindowManager.LayoutParams.TYPE_APPLICATION,
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                            or WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
                    PixelFormat.TRANSPARENT
            )
            lp.title = "Maximize Menu for Task=" + taskInfo.taskId
            lp.setTrustedOverlay()
            viewHost.setView(menuView.rootView, lp)
        }

@@ -268,7 +268,6 @@ class MaximizeMenu(
        private val sizeToggleDirection: SizeToggleDirection,
        immersiveConfig: ImmersiveConfig,
        showSnapOptions: Boolean,
        private val menuHeight: Int,
        private val menuPadding: Int
    ) {
        val rootView = LayoutInflater.from(context)
@@ -583,7 +582,7 @@ class MaximizeMenu(
                            // the menu.
                            val value = animatedValue as Float
                            val topPadding = menuPadding -
                                    ((1 - value) * menuHeight).toInt()
                                    ((1 - value) * measureHeight()).toInt()
                            container.setPadding(menuPadding, topPadding,
                                menuPadding, menuPadding)
                        }
@@ -604,7 +603,7 @@ class MaximizeMenu(
                    }
                },
                ObjectAnimator.ofFloat(rootView, TRANSLATION_Y,
                    (STARTING_MENU_HEIGHT_SCALE - 1) * menuHeight, 0f).apply {
                    (STARTING_MENU_HEIGHT_SCALE - 1) * measureHeight(), 0f).apply {
                    duration = OPEN_MENU_HEIGHT_ANIMATION_DURATION_MS
                    interpolator = EMPHASIZED_DECELERATE
                },
@@ -667,7 +666,7 @@ class MaximizeMenu(
                                    // the menu.
                                    val value = animatedValue as Float
                                    val topPadding = menuPadding -
                                            ((1 - value) * menuHeight).toInt()
                                            ((1 - value) * measureHeight()).toInt()
                                    container.setPadding(menuPadding, topPadding,
                                            menuPadding, menuPadding)
                                }
@@ -688,7 +687,7 @@ class MaximizeMenu(
                        }
                    },
                    ObjectAnimator.ofFloat(rootView, TRANSLATION_Y,
                            0f, (STARTING_MENU_HEIGHT_SCALE - 1) * menuHeight).apply {
                            0f, (STARTING_MENU_HEIGHT_SCALE - 1) * measureHeight()).apply {
                        duration = CLOSE_MENU_HEIGHT_ANIMATION_DURATION_MS
                        interpolator = FAST_OUT_LINEAR_IN
                    },
@@ -792,6 +791,18 @@ class MaximizeMenu(
            )
        }

        /** Measure width of the root view of this menu. */
        fun measureWidth() : Int {
            rootView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
            return rootView.getMeasuredWidth()
        }

        /** Measure height of the root view of this menu. */
        fun measureHeight() : Int {
            rootView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
            return rootView.getMeasuredHeight()
        }

        private fun deactivateSnapOptions() {
            // TODO(b/346440693): the background/colorStateList set on these buttons is overridden
            //  to a static resource & color on manually tracked hover events, which defeats the
@@ -1036,7 +1047,7 @@ interface MaximizeMenuFactory {
        displayController: DisplayController,
        taskInfo: RunningTaskInfo,
        decorWindowContext: Context,
        menuPosition: PointF,
        positionSupplier: (Int, Int) -> PointF,
        transactionSupplier: Supplier<Transaction>
    ): MaximizeMenu
}
@@ -1049,7 +1060,7 @@ object DefaultMaximizeMenuFactory : MaximizeMenuFactory {
        displayController: DisplayController,
        taskInfo: RunningTaskInfo,
        decorWindowContext: Context,
        menuPosition: PointF,
        positionSupplier: (Int, Int) -> PointF,
        transactionSupplier: Supplier<Transaction>
    ): MaximizeMenu {
        return MaximizeMenu(
@@ -1058,7 +1069,7 @@ object DefaultMaximizeMenuFactory : MaximizeMenuFactory {
            displayController,
            taskInfo,
            decorWindowContext,
            menuPosition,
            positionSupplier,
            transactionSupplier
        )
    }
+12 −13
Original line number Diff line number Diff line
@@ -120,6 +120,7 @@ import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder;
import kotlin.Unit;
import kotlin.jvm.functions.Function0;
import kotlin.jvm.functions.Function1;
import kotlin.jvm.functions.Function2;

import kotlinx.coroutines.CoroutineScope;
import kotlinx.coroutines.MainCoroutineDispatcher;
@@ -998,8 +999,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {

        createMaximizeMenu(decoration);

        verify(menu).show(anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), any(), any(), any(),
                any(), mOnMaxMenuHoverChangeListener.capture(), any());
        verify(menu).show(anyBoolean(), anyBoolean(), anyBoolean(), any(), any(), any(), any(),
                mOnMaxMenuHoverChangeListener.capture(), any());
        assertTrue(decoration.isMaximizeMenuActive());
    }

@@ -1011,8 +1012,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
                new FakeMaximizeMenuFactory(menu));
        decoration.setAppHeaderMaximizeButtonHovered(false);
        createMaximizeMenu(decoration);
        verify(menu).show(anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), any(), any(), any(),
                any(), mOnMaxMenuHoverChangeListener.capture(), any());
        verify(menu).show(anyBoolean(), anyBoolean(), anyBoolean(), any(), any(), any(), any(),
                mOnMaxMenuHoverChangeListener.capture(), any());

        mOnMaxMenuHoverChangeListener.getValue().invoke(false);

@@ -1050,8 +1051,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
        final DesktopModeWindowDecoration decoration = createWindowDecoration(taskInfo,
                new FakeMaximizeMenuFactory(menu));
        createMaximizeMenu(decoration);
        verify(menu).show(anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), any(), any(), any(),
                any(), mOnMaxMenuHoverChangeListener.capture(), any());
        verify(menu).show(anyBoolean(), anyBoolean(), anyBoolean(), any(), any(), any(), any(),
                mOnMaxMenuHoverChangeListener.capture(), any());

        mOnMaxMenuHoverChangeListener.getValue().invoke(true);

@@ -1065,8 +1066,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
        final DesktopModeWindowDecoration decoration = createWindowDecoration(taskInfo,
                new FakeMaximizeMenuFactory(menu));
        createMaximizeMenu(decoration);
        verify(menu).show(anyBoolean(), anyInt(), anyBoolean(), anyBoolean(), any(), any(), any(),
                any(), mOnMaxMenuHoverChangeListener.capture(), any());
        verify(menu).show(anyBoolean(), anyBoolean(), anyBoolean(), any(), any(), any(), any(),
                mOnMaxMenuHoverChangeListener.capture(), any());

        decoration.setAppHeaderMaximizeButtonHovered(true);

@@ -1086,7 +1087,6 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {

        verify(menu).show(
                anyBoolean(),
                anyInt(),
                /* showImmersiveOption= */ eq(true),
                anyBoolean(),
                any(),
@@ -1111,7 +1111,6 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {

        verify(menu).show(
                anyBoolean(),
                anyInt(),
                /* showImmersiveOption= */ eq(false),
                anyBoolean(),
                any(),
@@ -1136,7 +1135,6 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {

        verify(menu).show(
                anyBoolean(),
                anyInt(),
                anyBoolean(),
                /* showSnapOptions= */ eq(true),
                any(),
@@ -1161,7 +1159,6 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {

        verify(menu).show(
                anyBoolean(),
                anyInt(),
                anyBoolean(),
                /* showSnapOptions= */ eq(false),
                any(),
@@ -1766,7 +1763,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
                @NonNull RootTaskDisplayAreaOrganizer rootTdaOrganizer,
                @NonNull DisplayController displayController,
                @NonNull ActivityManager.RunningTaskInfo taskInfo,
                @NonNull Context decorWindowContext, @NonNull PointF menuPosition,
                @NonNull Context decorWindowContext,
                @NonNull Function2<? super Integer,? super Integer,? extends PointF>
                    positionSupplier,
                @NonNull Supplier<SurfaceControl.Transaction> transactionSupplier) {
            return mMaximizeMenu;
        }