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

Commit 033c630e authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Handle maximizing non-resizable activities in desktop mode" into main

parents b2ea8ff7 27c5aa0f
Loading
Loading
Loading
Loading
+44 −3
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@
package com.android.wm.shell.desktopmode

import android.app.ActivityManager.RunningTaskInfo
import android.app.TaskInfo
import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
import android.content.pm.ActivityInfo.isFixedOrientationLandscape
import android.content.pm.ActivityInfo.isFixedOrientationPortrait
import android.content.res.Configuration.ORIENTATION_LANDSCAPE
@@ -105,7 +107,7 @@ fun calculateInitialBounds(
 * Calculates the largest size that can fit in a given area while maintaining a specific aspect
 * ratio.
 */
private fun maximumSizeMaintainingAspectRatio(
fun maximumSizeMaintainingAspectRatio(
    taskInfo: RunningTaskInfo,
    targetArea: Size,
    aspectRatio: Float
@@ -114,7 +116,8 @@ private fun maximumSizeMaintainingAspectRatio(
    val targetWidth = targetArea.width
    val finalHeight: Int
    val finalWidth: Int
    if (isFixedOrientationPortrait(taskInfo.topActivityInfo!!.screenOrientation)) {
    // Get orientation either through top activity or task's orientation
    if (taskInfo.hasPortraitTopActivity()) {
        val tempWidth = (targetHeight / aspectRatio).toInt()
        if (tempWidth <= targetWidth) {
            finalHeight = targetHeight
@@ -137,7 +140,7 @@ private fun maximumSizeMaintainingAspectRatio(
}

/** Calculates the aspect ratio of an activity from its fullscreen bounds. */
private fun calculateAspectRatio(taskInfo: RunningTaskInfo): Float {
fun calculateAspectRatio(taskInfo: RunningTaskInfo): Float {
    if (taskInfo.appCompatTaskInfo.topActivityBoundsLetterboxed) {
        val appLetterboxWidth = taskInfo.appCompatTaskInfo.topActivityLetterboxWidth
        val appLetterboxHeight = taskInfo.appCompatTaskInfo.topActivityLetterboxHeight
@@ -171,3 +174,41 @@ private fun positionInScreen(desiredSize: Size, screenBounds: Rect): Rect {
        desiredSize.height + heightOffset
    )
}

/**
 * Adjusts bounds to be positioned in the middle of the area provided, not necessarily the
 * entire screen, as area can be offset by left and top start.
 */
fun centerInArea(desiredSize: Size, areaBounds: Rect, leftStart: Int, topStart: Int): Rect {
    val heightOffset = (areaBounds.height() - desiredSize.height) / 2
    val widthOffset = (areaBounds.width() - desiredSize.width) / 2

    val newLeft = leftStart + widthOffset
    val newTop = topStart + heightOffset
    val newRight = newLeft + desiredSize.width
    val newBottom = newTop + desiredSize.height

    return Rect(newLeft, newTop, newRight, newBottom)
}

fun TaskInfo.hasPortraitTopActivity(): Boolean {
    val topActivityScreenOrientation =
        topActivityInfo?.screenOrientation ?: SCREEN_ORIENTATION_UNSPECIFIED
    val appBounds = configuration.windowConfiguration.appBounds

    return when {
        // First check if activity has portrait screen orientation
        topActivityScreenOrientation != SCREEN_ORIENTATION_UNSPECIFIED -> {
            isFixedOrientationPortrait(topActivityScreenOrientation)
        }

        // Then check if the activity is portrait when letterboxed
        appCompatTaskInfo.topActivityBoundsLetterboxed -> appCompatTaskInfo.isTopActivityPillarboxed

        // Then check if the activity is portrait
        appBounds != null -> appBounds.height() > appBounds.width()

        // Otherwise just take the orientation of the task
        else -> isFixedOrientationPortrait(configuration.orientation)
    }
}
+29 −9
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import android.graphics.Rect
import android.graphics.Region
import android.os.IBinder
import android.os.SystemProperties
import android.util.Size
import android.view.Display.DEFAULT_DISPLAY
import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_CHANGE
@@ -649,13 +650,21 @@ class DesktopTasksController(
    fun toggleDesktopTaskSize(taskInfo: RunningTaskInfo) {
        val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return

        val stableBounds = Rect()
        displayLayout.getStableBounds(stableBounds)
        val stableBounds = Rect().apply { displayLayout.getStableBounds(this) }
        val currentTaskBounds = taskInfo.configuration.windowConfiguration.bounds
        val destinationBounds = Rect()
        if (taskInfo.configuration.windowConfiguration.bounds == stableBounds) {
            // The desktop task is currently occupying the whole stable bounds. If the bounds
            // before the task was toggled to stable bounds were saved, toggle the task to those
            // bounds. Otherwise, toggle to the default bounds.

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

        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.
            // Otherwise, toggle to the default bounds.
            val taskBoundsBeforeMaximize =
                desktopModeTaskRepository.removeBoundsBeforeMaximize(taskInfo.taskId)
            if (taskBoundsBeforeMaximize != null) {
@@ -670,9 +679,20 @@ class DesktopTasksController(
        } else {
            // Save current bounds so that task can be restored back to original bounds if necessary
            // and toggle to the stable bounds.
            val taskBounds = taskInfo.configuration.windowConfiguration.bounds
            desktopModeTaskRepository.saveBoundsBeforeMaximize(taskInfo.taskId, taskBounds)
            desktopModeTaskRepository.saveBoundsBeforeMaximize(taskInfo.taskId, currentTaskBounds)

            if (taskInfo.isResizeable) {
                // if resizable then expand to entire stable bounds (full display minus insets)
                destinationBounds.set(stableBounds)
            } else {
                // if non-resizable then calculate max bounds according to aspect ratio
                val activityAspectRatio = calculateAspectRatio(taskInfo)
                val newSize = maximumSizeMaintainingAspectRatio(taskInfo,
                    Size(stableBounds.width(), stableBounds.height()), activityAspectRatio)
                val newBounds = centerInArea(
                    newSize, stableBounds, stableBounds.left, stableBounds.top)
                destinationBounds.set(newBounds)
            }
        }

        val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds)
+60 −0
Original line number Diff line number Diff line
@@ -1915,6 +1915,26 @@ class DesktopTasksControllerTest : ShellTestCase() {
    assertThat(findBoundsChange(wct, task)).isEqualTo(STABLE_BOUNDS)
  }

  @Test
  fun toggleBounds_togglesToCalculatedBoundsForNonResizable() {
    val bounds = Rect(0, 0, 200, 100)
    val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds).apply {
      topActivityInfo = ActivityInfo().apply {
        screenOrientation = SCREEN_ORIENTATION_LANDSCAPE
        configuration.windowConfiguration.appBounds = bounds
      }
      isResizeable = false
    }

    // Bounds should be 1000 x 500, vertically centered in the 1000 x 1000 stable bounds
    val expectedBounds = Rect(STABLE_BOUNDS.left, 250, STABLE_BOUNDS.right, 750)

    controller.toggleDesktopTaskSize(task)
    // Assert bounds set to stable bounds
    val wct = getLatestToggleResizeDesktopTaskWct()
    assertThat(findBoundsChange(wct, task)).isEqualTo(expectedBounds)
  }

  @Test
  fun toggleBounds_lastBoundsBeforeMaximizeSaved() {
    val bounds = Rect(0, 0, 100, 100)
@@ -1941,6 +1961,46 @@ class DesktopTasksControllerTest : ShellTestCase() {
    assertThat(findBoundsChange(wct, task)).isEqualTo(boundsBeforeMaximize)
  }

  @Test
  fun toggleBounds_togglesFromStableBoundsToLastBoundsBeforeMaximize_nonResizeableEqualWidth() {
    val boundsBeforeMaximize = Rect(0, 0, 100, 100)
    val task = setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize).apply {
      isResizeable = false
    }

    // Maximize
    controller.toggleDesktopTaskSize(task)
    task.configuration.windowConfiguration.bounds.set(STABLE_BOUNDS.left,
      boundsBeforeMaximize.top, STABLE_BOUNDS.right, boundsBeforeMaximize.bottom)

    // Restore
    controller.toggleDesktopTaskSize(task)

    // Assert bounds set to last bounds before maximize
    val wct = getLatestToggleResizeDesktopTaskWct()
    assertThat(findBoundsChange(wct, task)).isEqualTo(boundsBeforeMaximize)
  }

  @Test
  fun toggleBounds_togglesFromStableBoundsToLastBoundsBeforeMaximize_nonResizeableEqualHeight() {
    val boundsBeforeMaximize = Rect(0, 0, 100, 100)
    val task = setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize).apply {
      isResizeable = false
    }

    // Maximize
    controller.toggleDesktopTaskSize(task)
    task.configuration.windowConfiguration.bounds.set(boundsBeforeMaximize.left,
      STABLE_BOUNDS.top, boundsBeforeMaximize.right, STABLE_BOUNDS.bottom)

    // Restore
    controller.toggleDesktopTaskSize(task)

    // Assert bounds set to last bounds before maximize
    val wct = getLatestToggleResizeDesktopTaskWct()
    assertThat(findBoundsChange(wct, task)).isEqualTo(boundsBeforeMaximize)
  }

  @Test
  fun toggleBounds_removesLastBoundsBeforeMaximizeAfterRestoringBounds() {
    val boundsBeforeMaximize = Rect(0, 0, 100, 100)