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

Commit 54229fee authored by Gustav Sennton's avatar Gustav Sennton
Browse files

Avoid throwing exception for unexpected ToggleResize transition

Transitions sometimes contain unexpected changes or lack necessary
changes, we should not crash when that happens - instead just let
another handler (not ToggleResizeDesktopTaskTransitionHandler) handle
the transition.

Bug: 442956351
Flag: EXEMPT minor bug fix
Test: manual

Change-Id: I17de965ce2cf4336973342a33f755dadb9842270
parent 0e3df2fe
Loading
Loading
Loading
Loading
+11 −5
Original line number Diff line number Diff line
@@ -30,7 +30,9 @@ import android.window.WindowContainerTransaction
import androidx.core.animation.addListener
import com.android.internal.jank.Cuj
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.windowdecor.OnTaskResizeAnimationListener
import java.util.function.Supplier
@@ -83,7 +85,7 @@ class ToggleResizeDesktopTaskTransitionHandler(
        finishTransaction: SurfaceControl.Transaction,
        finishCallback: Transitions.TransitionFinishCallback,
    ): Boolean {
        val change = findRelevantChange(info)
        val change = findRelevantChange(info) ?: return false
        val leash = change.leash
        val taskId = checkNotNull(change.taskInfo).taskId
        val startBounds = initialBounds ?: change.startAbsBounds
@@ -151,15 +153,14 @@ class ToggleResizeDesktopTaskTransitionHandler(
        return null
    }

    private fun findRelevantChange(info: TransitionInfo): TransitionInfo.Change {
    private fun findRelevantChange(info: TransitionInfo): TransitionInfo.Change? {
        val matchingChanges =
            info.changes.filter { c ->
                !isWallpaper(c) && isValidTaskChange(c) && c.mode == TRANSIT_CHANGE
            }
        if (matchingChanges.size != 1) {
            throw IllegalStateException(
                "Expected 1 relevant change but found: ${matchingChanges.size}"
            )
            logE("Expected 1 relevant change but found: %d", matchingChanges.size)
            return null
        }
        return matchingChanges.first()
    }
@@ -170,7 +171,12 @@ class ToggleResizeDesktopTaskTransitionHandler(
    private fun isValidTaskChange(change: TransitionInfo.Change): Boolean =
        change.taskInfo != null && change.taskInfo?.taskId != -1

    private fun logE(msg: String, vararg arguments: Any?) {
        ProtoLog.e(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
    }

    companion object {
        private const val RESIZE_DURATION_MS = 300L
        private const val TAG = "ToggleResizeDesktopTaskTransitionHandler"
    }
}
+107 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.os.IBinder
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_CHANGE
import android.window.TransitionInfo
import android.window.WindowContainerToken
import androidx.test.filters.SmallTest
import com.android.internal.jank.InteractionJankMonitor
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestRunningTaskInfoBuilder
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE
import com.android.wm.shell.transition.TransitionInfoBuilder
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.windowdecor.OnTaskResizeAnimationListener
import com.google.common.truth.Truth.assertThat
import java.util.function.Supplier
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever

@SmallTest
@TestableLooper.RunWithLooper
@RunWith(AndroidTestingRunner::class)
class ToggleResizeDesktopTaskTransitionHandlerTest : ShellTestCase() {

    private val transitions = mock<Transitions>()
    private val transaction = mock<SurfaceControl.Transaction>()
    private val transactionSupplier = mock<Supplier<SurfaceControl.Transaction>>()
    private val interactionJankMonitor = mock<InteractionJankMonitor>()
    private val transitionHandler =
        ToggleResizeDesktopTaskTransitionHandler(
            transitions,
            transactionSupplier,
            interactionJankMonitor,
        )

    @Before
    fun setUp() {
        transitionHandler.setOnTaskResizeAnimationListener(mock<OnTaskResizeAnimationListener>())
        whenever(transactionSupplier.get()).thenReturn(transaction)
        whenever(transaction.setPosition(any(), any(), any())).thenReturn(transaction)
        whenever(transaction.setWindowCrop(any(), any(), any())).thenReturn(transaction)
        whenever(transaction.show(any())).thenReturn(transaction)
    }

    @Test
    fun startAnimation_noRelevantChanges_returnsFalse() {
        val info = TransitionInfoBuilder(TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE).build()
        assertThat(startAnimation(info)).isFalse()
    }

    @Test
    fun startAnimation_twoRelevantChanges_returnsFalse() {
        val info =
            TransitionInfoBuilder(TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE)
                .addChange(createTaskChange())
                .addChange(createTaskChange())
                .build()
        assertThat(startAnimation(info)).isFalse()
    }

    @Test
    fun startAnimation_oneRelevantChange_returnsTrue() {
        val info =
            TransitionInfoBuilder(TRANSIT_DESKTOP_MODE_TOGGLE_RESIZE)
                .addChange(createTaskChange())
                .build()
        assertThat(startAnimation(info)).isTrue()
    }

    private fun startAnimation(info: TransitionInfo) =
        transitionHandler.startAnimation(
            mock<IBinder>(),
            info,
            transaction,
            transaction,
            mock<Transitions.TransitionFinishCallback>(),
        )

    private fun createTaskChange() =
        TransitionInfo.Change(mock<WindowContainerToken>(), mock<SurfaceControl>()).apply {
            setMode(TRANSIT_CHANGE)
            setTaskInfo(TestRunningTaskInfoBuilder().build())
        }
}