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

Commit b613d982 authored by mattsziklay's avatar mattsziklay
Browse files

Add "Manage Windows" option to Handle Menu.

When 2 or more app instances are open, handle menu will show a "Manage
Windows" button. This button will open a sub menu showing thumbnails of
all open instances of the app that aren't the same instance that
opened the menu.

Clicking a thumbnail closes the menu and opens the instance, following
similar rules to the Add Window button:

-In freeform, open the instance as a new freeform window (or move it
to the front if already in freeform).
-In fullscreen, open the instance in splitscreen.
-In splitscreen, replace the opposite split stage with the requested
instance.

Still to do:
- Thumbnails of freeform tasks do not use the snapshot cache.
- Animation of menu
- Polish of menu and icons
- Max height/scrollability for menus with > 6 instances.

Video: http://recall/-/g32sLZz1OjS8b4rbGPPq6V/g7RFA4hRn5LFHUcc4Dvpqx
Bug: 336289597
Bug: 337903443
Flag: com.android.window.flags.enable_desktop_windowing_multi_instance_features
Test: manual
Change-Id: I0e8880a35c210df6ec272c23eb7c685e47174a09
parent 2ca7c59b
Loading
Loading
Loading
Loading
+19 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?><!--
  ~ Copyright (C) 2024 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.
  -->

<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="960" android:viewportHeight="960" android:tint="?attr/colorControlNormal">
    <path android:fillColor="@android:color/black" android:pathData="M160,880Q127,880 103.5,856.5Q80,833 80,800L80,440Q80,407 103.5,383.5Q127,360 160,360L240,360L240,160Q240,127 263.5,103.5Q287,80 320,80L800,80Q833,80 856.5,103.5Q880,127 880,160L880,520Q880,553 856.5,576.5Q833,600 800,600L720,600L720,800Q720,833 696.5,856.5Q673,880 640,880L160,880ZM160,800L640,800Q640,800 640,800Q640,800 640,800L640,520L160,520L160,800Q160,800 160,800Q160,800 160,800ZM720,520L800,520Q800,520 800,520Q800,520 800,520L800,240L320,240L320,360L640,360Q673,360 696.5,383.5Q720,407 720,440L720,520Z"/>
</vector>
+8 −0
Original line number Diff line number Diff line
@@ -147,6 +147,14 @@
            android:drawableStart="@drawable/desktop_mode_ic_handle_menu_new_window"
            android:drawableTint="?androidprv:attr/materialColorOnSurface"
            style="@style/DesktopModeHandleMenuActionButton" />

        <Button
            android:id="@+id/manage_windows_button"
            android:contentDescription="@string/manage_windows_text"
            android:text="@string/manage_windows_text"
            android:drawableStart="@drawable/desktop_mode_ic_handle_menu_manage_windows"
            android:drawableTint="?androidprv:attr/materialColorOnSurface"
            style="@style/DesktopModeHandleMenuActionButton" />
    </LinearLayout>

    <LinearLayout
+6 −3
Original line number Diff line number Diff line
@@ -504,9 +504,9 @@
    <!-- The width of the handle menu in desktop mode.  -->
    <dimen name="desktop_mode_handle_menu_width">216dp</dimen>

    <!-- The maximum height of the handle menu in desktop mode. Four pills (52dp each) plus 2dp
        spacing between them plus 4dp top padding. -->
    <dimen name="desktop_mode_handle_menu_height">270dp</dimen>
    <!-- The maximum height of the handle menu in desktop mode. Three pills at 52dp each,
         additional actions pill 156dp, plus 2dp spacing between them plus 4dp top padding. -->
    <dimen name="desktop_mode_handle_menu_height">322dp</dimen>

    <!-- The elevation set on the handle menu pills. -->
    <dimen name="desktop_mode_handle_menu_pill_elevation">1dp</dimen>
@@ -520,6 +520,9 @@
    <!-- The maximum height of the handle menu's "New Window" button in desktop mode. -->
    <dimen name="desktop_mode_handle_menu_new_window_height">52dp</dimen>

    <!-- The maximum height of the handle menu's "Manage Windows" button in desktop mode. -->
    <dimen name="desktop_mode_handle_menu_manage_windows_height">52dp</dimen>

    <!-- The maximum height of the handle menu's "Screenshot" button in desktop mode. -->
    <dimen name="desktop_mode_handle_menu_screenshot_height">52dp</dimen>

+2 −0
Original line number Diff line number Diff line
@@ -294,6 +294,8 @@
    <string name="open_in_browser_text">Open in browser</string>
    <!-- Accessibility text for the handle menu new window button [CHAR LIMIT=NONE] -->
    <string name="new_window_text">New Window</string>
    <!-- Accessibility text for the handle menu new window button [CHAR LIMIT=NONE] -->
    <string name="manage_windows_text">Manage Windows</string>
    <!-- Accessibility text for the handle menu close button [CHAR LIMIT=NONE] -->
    <string name="close_text">Close</string>
    <!-- Accessibility text for the handle menu close menu button [CHAR LIMIT=NONE] -->
+43 −22
Original line number Diff line number Diff line
@@ -1077,45 +1077,47 @@ class DesktopTasksController(
            request.triggerTask != null
    }

    fun openNewWindow(
        taskInfo: RunningTaskInfo
    /** Open an existing instance of an app. */
    fun openInstance(
        callingTask: RunningTaskInfo,
        requestedTaskId: Int
    ) {
        // TODO(b/337915660): Add a transition handler for these; animations
        //  need updates in some cases.
        val newTaskWindowingMode = when {
            taskInfo.isFreeform -> {
                WINDOWING_MODE_FREEFORM
            }
            taskInfo.isFullscreen || taskInfo.isMultiWindow -> {
                WINDOWING_MODE_MULTI_WINDOW
            }
            else -> {
                error("Invalid windowing mode: ${taskInfo.windowingMode}")
        val wct = WindowContainerTransaction()
        val options = createNewWindowOptions(callingTask)
        if (options.launchWindowingMode == WINDOWING_MODE_FREEFORM) {
            wct.startTask(requestedTaskId, options.toBundle())
            transitions.startTransition(TRANSIT_OPEN, wct, null)
        } else {
            val splitPosition = splitScreenController.determineNewInstancePosition(callingTask)
            splitScreenController.startTask(requestedTaskId, splitPosition,
                options.toBundle(), null /* hideTaskToken */)
        }
    }

        val baseActivity = taskInfo.baseActivity ?: return
    /** Create an Intent to open a new window of a task. */
    fun openNewWindow(
        callingTaskInfo: RunningTaskInfo
    ) {
        // TODO(b/337915660): Add a transition handler for these; animations
        //  need updates in some cases.
        val baseActivity = callingTaskInfo.baseActivity ?: return
        val fillIn: Intent = context.packageManager
            .getLaunchIntentForPackage(
                baseActivity.packageName
            ) ?: return
        fillIn
            .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
        val options =
            ActivityOptions.makeBasic().apply {
                launchWindowingMode = newTaskWindowingMode
                pendingIntentBackgroundActivityStartMode =
                    ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
            }
        val launchIntent = PendingIntent.getActivity(
            context,
            /* requestCode= */ 0,
            fillIn,
            PendingIntent.FLAG_IMMUTABLE
        )
        when (newTaskWindowingMode) {
        val options = createNewWindowOptions(callingTaskInfo)
        when (options.launchWindowingMode) {
            WINDOWING_MODE_MULTI_WINDOW -> {
                val splitPosition = splitScreenController.determineNewInstancePosition(taskInfo)
                val splitPosition = splitScreenController
                    .determineNewInstancePosition(callingTaskInfo)
                splitScreenController.startIntent(
                    launchIntent, context.userId, fillIn, splitPosition,
                    options.toBundle(), null /* hideTaskToken */
@@ -1130,6 +1132,25 @@ class DesktopTasksController(
        }
    }

    private fun createNewWindowOptions(callingTask: RunningTaskInfo): ActivityOptions {
        val newTaskWindowingMode = when {
            callingTask.isFreeform -> {
                WINDOWING_MODE_FREEFORM
            }
            callingTask.isFullscreen || callingTask.isMultiWindow -> {
                WINDOWING_MODE_MULTI_WINDOW
            }
            else -> {
                error("Invalid windowing mode: ${callingTask.windowingMode}")
            }
        }
        return ActivityOptions.makeBasic().apply {
            launchWindowingMode = newTaskWindowingMode
            pendingIntentBackgroundActivityStartMode =
                ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
        }
    }

    /**
     * Handles the case where a freeform task is launched from recents.
     *
Loading