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

Commit f2767e98 authored by Jagrut Desai's avatar Jagrut Desai Committed by Android (Google) Code Review
Browse files

Merge "Extend DesktopTaskListener Functionality" into main

parents 7a28974d 822bacd5
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -171,6 +171,18 @@ fun calculateAspectRatio(taskInfo: RunningTaskInfo): Float {
        minOf(appBounds.height(), appBounds.width()).toFloat()
}

/** Returns true if task's width or height is maximized else returns false. */
fun isTaskWidthOrHeightEqual(taskBounds: Rect, stableBounds: Rect): Boolean {
    return taskBounds.width() == stableBounds.width() ||
            taskBounds.height() == stableBounds.height()
}

/** Returns true if task bound is equal to stable bounds else returns false. */
fun isTaskBoundsEqual(taskBounds: Rect, stableBounds: Rect): Boolean {
    return taskBounds.width() == stableBounds.width() &&
            taskBounds.height() == stableBounds.height()
}

/**
 * Calculates the desired initial bounds for applications in desktop windowing. This is done as a
 * scale of the screen bounds.
+128 −13
Original line number Diff line number Diff line
@@ -169,6 +169,9 @@ class DesktopTasksController(
            }
        }

    @VisibleForTesting
    var taskbarDesktopTaskListener: TaskbarDesktopTaskListener? = null

    /** Task id of the task currently being dragged from fullscreen/split. */
    val draggingTaskId
        get() = dragToDesktopTransitionHandler.draggingTaskId
@@ -610,13 +613,10 @@ class DesktopTasksController(
        val currentTaskBounds = taskInfo.configuration.windowConfiguration.bounds
        val destinationBounds = Rect()

        val isMaximized = if (taskInfo.isResizeable) {
            currentTaskBounds == stableBounds
        } else {
            currentTaskBounds.width() == stableBounds.width()
                    || currentTaskBounds.height() == stableBounds.height()
        }

        val isMaximized = isTaskMaximized(taskInfo, stableBounds)
        // If the task is currently maximized, we will toggle it not to be and vice versa. This is
        // helpful to eliminate the current task from logic to calculate taskbar corner rounding.
        val willMaximize = !isMaximized
        if (isMaximized) {
            // The desktop task is at the maximized width and/or height of the stable bounds.
            // If the task's pre-maximize stable bounds were saved, toggle the task to those bounds.
@@ -651,6 +651,18 @@ class DesktopTasksController(
            }
        }



        val shouldRestoreToSnap =
            isMaximized && isTaskSnappedToHalfScreen(taskInfo, destinationBounds)

        logD("willMaximize = %s", willMaximize)
        logD("shouldRestoreToSnap = %s", shouldRestoreToSnap)

        val doesAnyTaskRequireTaskbarRounding = willMaximize || shouldRestoreToSnap ||
                doesAnyTaskRequireTaskbarRounding(taskInfo.displayId, taskInfo.taskId)

        taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding)
        val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds)
        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
            toggleResizeDesktopTaskTransitionHandler.startTransition(wct)
@@ -659,6 +671,65 @@ class DesktopTasksController(
        }
    }

    private fun isTaskMaximized(
        taskInfo: RunningTaskInfo,
        stableBounds: Rect
    ): Boolean {
        val currentTaskBounds = taskInfo.configuration.windowConfiguration.bounds

        return if (taskInfo.isResizeable) {
            isTaskBoundsEqual(currentTaskBounds, stableBounds)
        } else {
            isTaskWidthOrHeightEqual(currentTaskBounds, stableBounds)
        }
    }

    private fun isMaximizedToStableBoundsEdges(
        taskInfo: RunningTaskInfo,
        stableBounds: Rect
    ): Boolean {
        val currentTaskBounds = taskInfo.configuration.windowConfiguration.bounds
        return isTaskBoundsEqual(currentTaskBounds, stableBounds)
    }

    /** Returns if current task bound is snapped to half screen */
    private fun isTaskSnappedToHalfScreen(
        taskInfo: RunningTaskInfo,
        taskBounds: Rect = taskInfo.configuration.windowConfiguration.bounds
    ): Boolean =
        getSnapBounds(taskInfo, SnapPosition.LEFT) == taskBounds ||
                getSnapBounds(taskInfo, SnapPosition.RIGHT) == taskBounds

    @VisibleForTesting
    fun doesAnyTaskRequireTaskbarRounding(
        displayId: Int,
        excludeTaskId: Int? = null,
    ): Boolean {
        val doesAnyTaskRequireTaskbarRounding =
            taskRepository.getActiveNonMinimizedOrderedTasks(displayId)
                // exclude current task since maximize/restore transition has not taken place yet.
                .filterNot { taskId -> taskId == excludeTaskId }
                .any { taskId ->
                    val taskInfo = shellTaskOrganizer.getRunningTaskInfo(taskId)!!
                    val displayLayout = displayController.getDisplayLayout(taskInfo.displayId)
                    val stableBounds = Rect().apply { displayLayout?.getStableBounds(this) }
                    logD("taskInfo = %s", taskInfo)
                    logD(
                        "isTaskSnappedToHalfScreen(taskInfo) = %s",
                        isTaskSnappedToHalfScreen(taskInfo)
                    )
                    logD(
                        "isMaximizedToStableBoundsEdges(taskInfo, stableBounds) = %s",
                        isMaximizedToStableBoundsEdges(taskInfo, stableBounds)
                    )
                    isTaskSnappedToHalfScreen(taskInfo)
                            || isMaximizedToStableBoundsEdges(taskInfo, stableBounds)
                }

        logD("doesAnyTaskRequireTaskbarRounding = %s", doesAnyTaskRequireTaskbarRounding)
        return doesAnyTaskRequireTaskbarRounding
    }

    /**
     * Quick-resize to the right or left half of the stable bounds.
     *
@@ -673,9 +744,9 @@ class DesktopTasksController(
        position: SnapPosition
    ) {
        val destinationBounds = getSnapBounds(taskInfo, position)

        if (destinationBounds == taskInfo.configuration.windowConfiguration.bounds) return

        taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(true)
        val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds)
        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
            toggleResizeDesktopTaskTransitionHandler.startTransition(wct, currentDragBounds)
@@ -806,6 +877,10 @@ class DesktopTasksController(
            .mapNotNull { taskId -> shellTaskOrganizer.getRunningTaskInfo(taskId) }
            .reversed() // Start from the back so the front task is brought forward last
            .forEach { task -> wct.reorder(task.token, /* onTop= */ true) }

        taskbarDesktopTaskListener?.
            onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding(displayId))

        return taskToMinimize
    }

@@ -1156,6 +1231,12 @@ class DesktopTasksController(
        ) {
            wct.removeTask(task.token)
        }
        taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(
            doesAnyTaskRequireTaskbarRounding(
                task.displayId,
                task.id
            )
        )
        return if (wct.isEmpty) null else wct
    }

@@ -1450,6 +1531,8 @@ class DesktopTasksController(
        }
        // A freeform drag-move ended, remove the indicator immediately.
        releaseVisualIndicator()
        taskbarDesktopTaskListener
            ?.onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding(taskInfo.displayId))
    }

    /**
@@ -1686,17 +1769,39 @@ class DesktopTasksController(
                }
            }

        private val mTaskbarDesktopTaskListener: TaskbarDesktopTaskListener =
                object : TaskbarDesktopTaskListener {
                    override fun onTaskbarCornerRoundingUpdate(
                        hasTasksRequiringTaskbarRounding: Boolean) {
                        ProtoLog.v(
                                WM_SHELL_DESKTOP_MODE,
                                "IDesktopModeImpl: onTaskbarCornerRoundingUpdate " +
                                        "doesAnyTaskRequireTaskbarRounding=%s",
                                hasTasksRequiringTaskbarRounding
                        )

                        remoteListener.call { l ->
                            l.onTaskbarCornerRoundingUpdate(hasTasksRequiringTaskbarRounding)
                        }
                    }
                }

        init {
            remoteListener =
                SingleInstanceRemoteListener<DesktopTasksController, IDesktopTaskListener>(
                    controller,
                    { c ->
                        c.taskRepository.addVisibleTasksListener(
                            listener,
                            c.mainExecutor
                        )
                        run {
                            c.taskRepository.addVisibleTasksListener(listener, c.mainExecutor)
                            c.taskbarDesktopTaskListener = mTaskbarDesktopTaskListener
                        }
                    },
                    { c -> c.taskRepository.removeVisibleTasksListener(listener) }
                    { c ->
                        run {
                            c.taskRepository.removeVisibleTasksListener(listener)
                            c.taskbarDesktopTaskListener = null
                        }
                    }
                )
        }

@@ -1779,6 +1884,16 @@ class DesktopTasksController(
        private const val TAG = "DesktopTasksController"
    }

    /** Defines interface for classes that can listen to changes for task resize. */
    // TODO(b/343931111): Migrate to using TransitionObservers when ready
    interface TaskbarDesktopTaskListener {
        /**
         * [hasTasksRequiringTaskbarRounding] is true when a task is either maximized or snapped
         * left/right and rounded corners are enabled.
         */
        fun onTaskbarCornerRoundingUpdate(hasTasksRequiringTaskbarRounding: Boolean)
    }

    /** The positions on a screen that a task can snap to. */
    enum class SnapPosition {
        RIGHT,
+6 −0
Original line number Diff line number Diff line
@@ -27,4 +27,10 @@ interface IDesktopTaskListener {

    /** @deprecated this is no longer supported. */
    oneway void onStashedChanged(int displayId, boolean stashed);

    /**
     * Shows taskbar corner radius when running desktop tasks are updated if
     * [hasTasksRequiringTaskbarRounding] is true.
     */
    oneway void onTaskbarCornerRoundingUpdate(boolean hasTasksRequiringTaskbarRounding);
}
 No newline at end of file
+66 −0
Original line number Diff line number Diff line
/*
 * 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.
 */

package com.android.wm.shell.desktopmode

import android.graphics.Rect
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidTestingRunner::class)
class DesktopModeUtilsTest {
    @Test
    fun isTaskBoundsEqual_stableBoundsAreEqual_returnTrue() {
        assertThat(isTaskBoundsEqual(task2Bounds, stableBounds)).isTrue()
    }

    @Test
    fun isTaskBoundsEqual_stableBoundsAreNotEqual_returnFalse() {
        assertThat(isTaskBoundsEqual(task4Bounds, stableBounds)).isFalse()
    }

    @Test
    fun isTaskWidthOrHeightEqual_stableBoundsAreEqual_returnTrue() {
        assertThat(isTaskWidthOrHeightEqual(task2Bounds, stableBounds)).isTrue()
    }

    @Test
    fun isTaskWidthOrHeightEqual_stableBoundWidthIsEquals_returnTrue() {
        assertThat(isTaskWidthOrHeightEqual(task3Bounds, stableBounds)).isTrue()
    }

    @Test
    fun isTaskWidthOrHeightEqual_stableBoundHeightIsEquals_returnTrue() {
        assertThat(isTaskWidthOrHeightEqual(task3Bounds, stableBounds)).isTrue()
    }

    @Test
    fun isTaskWidthOrHeightEqual_stableBoundsWidthOrHeightAreNotEquals_returnFalse() {
        assertThat(isTaskWidthOrHeightEqual(task1Bounds, stableBounds)).isTrue()
    }

    private companion object {
        val task1Bounds = Rect(0, 0, 0, 0)
        val task2Bounds = Rect(1, 1, 1, 1)
        val task3Bounds = Rect(0, 1, 0, 1)
        val task4Bounds = Rect(1, 2, 2, 1)
        val stableBounds = Rect(1, 1, 1, 1)
    }
}
+50 −0
Original line number Diff line number Diff line
@@ -84,6 +84,7 @@ import com.android.wm.shell.common.MultiInstanceHelper
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition
import com.android.wm.shell.desktopmode.DesktopTasksController.TaskbarDesktopTaskListener
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createHomeTask
@@ -175,6 +176,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
  @Mock
  private lateinit var mockInteractionJankMonitor: InteractionJankMonitor
  @Mock private lateinit var mockSurface: SurfaceControl
  @Mock private lateinit var taskbarDesktopTaskListener: TaskbarDesktopTaskListener

  private lateinit var mockitoSession: StaticMockitoSession
  private lateinit var controller: DesktopTasksController
@@ -237,6 +239,8 @@ class DesktopTasksControllerTest : ShellTestCase() {
    val captor = ArgumentCaptor.forClass(RecentsTransitionStateListener::class.java)
    verify(recentsTransitionHandler).addTransitionStateListener(captor.capture())
    recentsTransitionStateListener = captor.value

    controller.taskbarDesktopTaskListener = taskbarDesktopTaskListener
  }

  private fun createController(): DesktopTasksController {
@@ -281,6 +285,52 @@ class DesktopTasksControllerTest : ShellTestCase() {
    verify(shellInit).addInitCallback(any(), any<DesktopTasksController>())
  }

  @Test
  fun doesAnyTaskRequireTaskbarRounding_onlyFreeFormTaskIsRunning_returnFalse() {
    setUpFreeformTask()

    assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isFalse()
  }

  @Test
  fun doesAnyTaskRequireTaskbarRounding_toggleResizeOfFreeFormTask_returnTrue() {
    val task1 = setUpFreeformTask()

    val argumentCaptor = ArgumentCaptor.forClass(Boolean::class.java)
    controller.toggleDesktopTaskSize(task1)
    verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture())

    assertThat(argumentCaptor.value).isTrue()
  }

  @Test
  fun doesAnyTaskRequireTaskbarRounding_fullScreenTaskIsRunning_returnTrue() {
    val stableBounds = Rect().apply { displayLayout.getStableBounds(this) }
    setUpFreeformTask(bounds = stableBounds, active = true)
    assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isTrue()
  }

  @Test
  fun doesAnyTaskRequireTaskbarRounding_toggleResizeOfFullScreenTask_returnFalse() {
    val stableBounds = Rect().apply { displayLayout.getStableBounds(this) }
    val task1 = setUpFreeformTask(bounds = stableBounds, active = true)

    val argumentCaptor = ArgumentCaptor.forClass(Boolean::class.java)
    controller.toggleDesktopTaskSize(task1)
    verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture())

    assertThat(argumentCaptor.value).isFalse()
  }

  @Test
  fun doesAnyTaskRequireTaskbarRounding_splitScreenTaskIsRunning_returnTrue() {
    val stableBounds = Rect().apply { displayLayout.getStableBounds(this) }
    setUpFreeformTask(bounds = Rect(stableBounds.left, stableBounds.top, 500, stableBounds.bottom))

    assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isTrue()
  }


  @Test
  fun instantiate_cannotEnterDesktopMode_doNotAddInitCallback() {
    whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(false)