diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java index 51645356b8..33fe83e1d0 100644 --- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java +++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java @@ -419,6 +419,20 @@ public class RecentsOrientedState implements LauncherPrefChangeListener { | FLAG_HOME_ROTATION_FORCE_ENABLED_FOR_TESTING)) != 0; } + /** + * Returns if we should show the action buttons on the recent view based on the orientation + * state. + */ + public boolean shouldHideActionButtons() { + // In fixed landscape always show the action buttons + return !isLauncherFixedLandscape() + // When not in fixed landscape, do not show actions buttons when using + // fake landscape which happens when the rotation is not ROTATION_0 unless + // rotation is allowed + && (getTouchRotation() != ROTATION_0 || getRecentsActivityRotation() != ROTATION_0) + && !isRecentsActivityRotationAllowed(); + } + /** * Enables or disables the rotation watcher for listening to rotation callbacks */ diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 43d662d9c7..6eb2f70a80 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -19,7 +19,6 @@ package com.android.quickstep.views; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.os.Trace.traceBegin; import static android.os.Trace.traceEnd; -import static android.view.Surface.ROTATION_0; import static android.view.View.MeasureSpec.EXACTLY; import static android.view.View.MeasureSpec.makeMeasureSpec; @@ -2346,10 +2345,10 @@ public abstract class RecentsView< } } - boolean isInLandscape = mOrientationState.getTouchRotation() != ROTATION_0 - || mOrientationState.getRecentsActivityRotation() != ROTATION_0; - mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION, - !mOrientationState.isRecentsActivityRotationAllowed() && isInLandscape); + mActionsView.updateHiddenFlags( + HIDDEN_NON_ZERO_ROTATION, + mOrientationState.shouldHideActionButtons() + ); // Recalculate DeviceProfile dependent layout. updateSizeAndPadding(); @@ -6600,9 +6599,10 @@ public abstract class RecentsView< getCurrentPageTaskView().setModalness(modalness); } // Only show actions view when it's modal for in-place landscape mode. - boolean inPlaceLandscape = !mOrientationState.isRecentsActivityRotationAllowed() - && mOrientationState.getTouchRotation() != ROTATION_0; - mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION, modalness < 1 && inPlaceLandscape); + mActionsView.updateHiddenFlags( + HIDDEN_NON_ZERO_ROTATION, modalness < 1 + && mOrientationState.shouldHideActionButtons() + ); } @Nullable diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/RecentOrientedStateHandlerTests.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/RecentOrientedStateHandlerTests.kt deleted file mode 100644 index 3cdf608059..0000000000 --- a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/RecentOrientedStateHandlerTests.kt +++ /dev/null @@ -1,166 +0,0 @@ -/* - * 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.quickstep.util - -import android.view.Surface -import android.view.Surface.ROTATION_0 -import android.view.Surface.ROTATION_180 -import android.view.Surface.ROTATION_90 -import androidx.test.core.app.ApplicationProvider -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.quickstep.FallbackActivityInterface -import com.android.quickstep.orientation.RecentsPagedOrientationHandler -import com.android.quickstep.orientation.RecentsPagedOrientationHandler.Companion.PORTRAIT -import com.google.common.truth.Truth.assertWithMessage -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.kotlin.spy -import org.mockito.kotlin.whenever - -/** - * Test all possible inputs to RecentsOrientedState.updateHandler. It tests all possible - * combinations of rotations and relevant methods (two methods that return boolean values) but it - * only provides the expected result when the final rotation is different from ROTATION_0 for - * simplicity. So any case not shown in resultMap you can assume results in ROTATION_0. - */ -@SmallTest -@RunWith(AndroidJUnit4::class) -class RecentOrientedStateHandlerTests { - - data class TestCase( - val recentsRotation: Int, - val displayRotation: Int, - val touchRotation: Int, - val isRotationAllowed: Boolean, - val isFixedLandscape: Boolean, - ) { - override fun toString(): String { - return "TestCase(recentsRotation=${Surface.rotationToString(recentsRotation)}, " + - "displayRotation=${Surface.rotationToString(displayRotation)}, " + - "touchRotation=${Surface.rotationToString(touchRotation)}, " + - "isRotationAllowed=$isRotationAllowed, " + - "isFixedLandscape=$isFixedLandscape)" - } - } - - private fun runTestCase(testCase: TestCase, expectedHandler: RecentsPagedOrientationHandler) { - val recentOrientedState = - spy( - RecentsOrientedState( - ApplicationProvider.getApplicationContext(), - FallbackActivityInterface.INSTANCE, - ) {} - ) - whenever(recentOrientedState.isRecentsActivityRotationAllowed).thenAnswer { - testCase.isRotationAllowed - } - whenever(recentOrientedState.isLauncherFixedLandscape).thenAnswer { - testCase.isFixedLandscape - } - - recentOrientedState.update(testCase.displayRotation, testCase.touchRotation) - val rotation = recentOrientedState.orientationHandler.rotation - assertWithMessage("$testCase to ${Surface.rotationToString(rotation)},") - .that(rotation) - .isEqualTo(expectedHandler.rotation) - } - - @Test - fun `test fixed landscape when device is portrait`() { - runTestCase( - TestCase( - recentsRotation = ROTATION_0, - displayRotation = -1, - touchRotation = ROTATION_0, - isRotationAllowed = false, - isFixedLandscape = true, - ), - PORTRAIT, - ) - } - - @Test - fun `test fixed landscape when device is landscape`() { - runTestCase( - TestCase( - recentsRotation = ROTATION_90, - displayRotation = -1, - touchRotation = ROTATION_0, - isRotationAllowed = false, - isFixedLandscape = true, - ), - PORTRAIT, - ) - } - - @Test - fun `test fixed landscape when device is seascape`() { - runTestCase( - TestCase( - recentsRotation = ROTATION_180, - displayRotation = -1, - touchRotation = ROTATION_0, - isRotationAllowed = false, - isFixedLandscape = true, - ), - PORTRAIT, - ) - } - - @Test - fun `test fixed landscape when device is portrait and display rotation is portrait`() { - runTestCase( - TestCase( - recentsRotation = ROTATION_0, - displayRotation = ROTATION_0, - touchRotation = ROTATION_0, - isRotationAllowed = false, - isFixedLandscape = true, - ), - PORTRAIT, - ) - } - - @Test - fun `test fixed landscape when device is landscape and display rotation is landscape `() { - runTestCase( - TestCase( - recentsRotation = ROTATION_90, - displayRotation = ROTATION_90, - touchRotation = ROTATION_0, - isRotationAllowed = false, - isFixedLandscape = true, - ), - PORTRAIT, - ) - } - - @Test - fun `test fixed landscape when device is seascape and display rotation is seascape`() { - runTestCase( - TestCase( - recentsRotation = ROTATION_180, - displayRotation = ROTATION_180, - touchRotation = ROTATION_0, - isRotationAllowed = false, - isFixedLandscape = true, - ), - PORTRAIT, - ) - } -} diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/RecentOrientedStateTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/RecentOrientedStateTest.kt new file mode 100644 index 0000000000..137603f79b --- /dev/null +++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/RecentOrientedStateTest.kt @@ -0,0 +1,325 @@ +/* + * 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.quickstep.util + +import android.view.Surface +import android.view.Surface.ROTATION_0 +import android.view.Surface.ROTATION_180 +import android.view.Surface.ROTATION_90 +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.quickstep.FallbackActivityInterface +import com.android.quickstep.orientation.RecentsPagedOrientationHandler +import com.android.quickstep.orientation.RecentsPagedOrientationHandler.Companion.PORTRAIT +import com.google.common.truth.Truth.assertWithMessage +import junit.framework.TestCase.assertEquals +import org.junit.Assert.assertNotEquals +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.spy +import org.mockito.kotlin.whenever + +data class HideActionsTestCase( + val recentsRotation: Int, + val touchRotation: Int, + val isRotationAllowed: Boolean, + val isFixedLandscape: Boolean, +) + +data class RotationHandlerTestCase( + val displayRotation: Int, + val touchRotation: Int, + val isRotationAllowed: Boolean, + val isFixedLandscape: Boolean, +) { + override fun toString(): String { + return "TestCase(displayRotation=${Surface.rotationToString(displayRotation)}, " + + "touchRotation=${Surface.rotationToString(touchRotation)}, " + + "isRotationAllowed=$isRotationAllowed, " + + "isFixedLandscape=$isFixedLandscape)" + } +} + +/** + * Test all possible inputs to RecentsOrientedState.updateHandler. It tests all possible + * combinations of rotations and relevant methods (two methods that return boolean values) but it + * only provides the expected result when the final rotation is different from ROTATION_0 for + * simplicity. So any case not shown in resultMap you can assume results in ROTATION_0. + */ +@SmallTest +@RunWith(AndroidJUnit4::class) +class RecentOrientedStateTest { + + companion object { + const val INVALID_ROTATION = -1 + } + + private fun createRecentOrientedState() = + spy( + RecentsOrientedState( + ApplicationProvider.getApplicationContext(), + FallbackActivityInterface.INSTANCE, + ) {} + ) + + private fun rotationHandlerTest( + testCase: RotationHandlerTestCase, + expectedHandler: RecentsPagedOrientationHandler, + ) { + val recentOrientedState = createRecentOrientedState() + whenever(recentOrientedState.isRecentsActivityRotationAllowed) + .thenReturn(testCase.isRotationAllowed) + whenever(recentOrientedState.isLauncherFixedLandscape).thenReturn(testCase.isFixedLandscape) + + recentOrientedState.update(testCase.displayRotation, testCase.touchRotation) + val rotation = recentOrientedState.orientationHandler.rotation + assertWithMessage("$testCase to ${Surface.rotationToString(rotation)},") + .that(rotation) + .isEqualTo(expectedHandler.rotation) + } + + private fun shouldHideActionButtonTest( + testCase: HideActionsTestCase, + hideActionButtonsExpected: Boolean, + ) { + val recentOrientedState = createRecentOrientedState() + whenever(recentOrientedState.recentsActivityRotation).thenReturn(testCase.recentsRotation) + whenever(recentOrientedState.touchRotation).thenReturn(testCase.touchRotation) + whenever(recentOrientedState.isRecentsActivityRotationAllowed) + .thenReturn(testCase.isRotationAllowed) + whenever(recentOrientedState.isLauncherFixedLandscape).thenReturn(testCase.isFixedLandscape) + val res = recentOrientedState.shouldHideActionButtons() + assertWithMessage( + "Test case $testCase generated $res but should be $hideActionButtonsExpected" + ) + .that(res) + .isEqualTo(hideActionButtonsExpected) + } + + @Test + fun `stateId changes with flags`() { + val recentOrientedState1 = createRecentOrientedState() + val recentOrientedState2 = createRecentOrientedState() + assertEquals(recentOrientedState1.stateId, recentOrientedState2.stateId) + + recentOrientedState1.setGestureActive(true) + recentOrientedState2.setGestureActive(false) + assertNotEquals(recentOrientedState1.stateId, recentOrientedState2.stateId) + + recentOrientedState2.setGestureActive(true) + assertEquals(recentOrientedState1.stateId, recentOrientedState2.stateId) + } + + @Test + fun `stateId changes with recents rotation`() { + val recentOrientedState1 = createRecentOrientedState() + val recentOrientedState2 = createRecentOrientedState() + recentOrientedState1.setRecentsRotation(ROTATION_90) + recentOrientedState2.setRecentsRotation(ROTATION_180) + assertNotEquals(recentOrientedState1.stateId, recentOrientedState2.stateId) + + recentOrientedState2.setRecentsRotation(ROTATION_90) + assertEquals(recentOrientedState1.stateId, recentOrientedState2.stateId) + } + + @Test + fun `stateId changes with display rotation`() { + val recentOrientedState1 = createRecentOrientedState() + val recentOrientedState2 = createRecentOrientedState() + recentOrientedState1.update(ROTATION_0, ROTATION_90) + recentOrientedState2.update(ROTATION_0, ROTATION_180) + assertNotEquals(recentOrientedState1.stateId, recentOrientedState2.stateId) + + recentOrientedState2.update(ROTATION_90, ROTATION_90) + assertNotEquals(recentOrientedState1.stateId, recentOrientedState2.stateId) + + recentOrientedState2.update(ROTATION_90, ROTATION_0) + assertNotEquals(recentOrientedState1.stateId, recentOrientedState2.stateId) + + recentOrientedState2.update(ROTATION_0, ROTATION_90) + assertEquals(recentOrientedState1.stateId, recentOrientedState2.stateId) + } + + @Test + fun `rotation handler test fixed landscape when device is portrait`() { + rotationHandlerTest( + testCase = + RotationHandlerTestCase( + displayRotation = INVALID_ROTATION, + touchRotation = ROTATION_0, + isRotationAllowed = false, + isFixedLandscape = true, + ), + expectedHandler = PORTRAIT, + ) + } + + @Test + fun `rotation handler test fixed landscape when device is landscape`() { + rotationHandlerTest( + testCase = + RotationHandlerTestCase( + displayRotation = INVALID_ROTATION, + touchRotation = ROTATION_0, + isRotationAllowed = false, + isFixedLandscape = true, + ), + expectedHandler = PORTRAIT, + ) + } + + @Test + fun `rotation handler test fixed landscape when device is seascape`() { + rotationHandlerTest( + testCase = + RotationHandlerTestCase( + displayRotation = INVALID_ROTATION, + touchRotation = ROTATION_0, + isRotationAllowed = false, + isFixedLandscape = true, + ), + expectedHandler = PORTRAIT, + ) + } + + @Test + fun `rotation handler test fixed landscape when device is portrait and display rotation is portrait`() { + rotationHandlerTest( + testCase = + RotationHandlerTestCase( + displayRotation = ROTATION_0, + touchRotation = ROTATION_0, + isRotationAllowed = false, + isFixedLandscape = true, + ), + expectedHandler = PORTRAIT, + ) + } + + @Test + fun `rotation handler test fixed landscape when device is landscape and display rotation is landscape `() { + rotationHandlerTest( + testCase = + RotationHandlerTestCase( + displayRotation = ROTATION_90, + touchRotation = ROTATION_0, + isRotationAllowed = false, + isFixedLandscape = true, + ), + expectedHandler = PORTRAIT, + ) + } + + @Test + fun `rotation handler test fixed landscape when device is seascape and display rotation is seascape`() { + rotationHandlerTest( + testCase = + RotationHandlerTestCase( + displayRotation = ROTATION_180, + touchRotation = ROTATION_0, + isRotationAllowed = false, + isFixedLandscape = true, + ), + expectedHandler = PORTRAIT, + ) + } + + @Test + fun `should hide actions fixed landscape no rotation`() { + shouldHideActionButtonTest( + testCase = + HideActionsTestCase( + recentsRotation = ROTATION_0, + touchRotation = ROTATION_0, + isRotationAllowed = false, + isFixedLandscape = true, + ), + hideActionButtonsExpected = false, + ) + } + + @Test + fun `should hide actions fixed landscape rotation 90`() { + shouldHideActionButtonTest( + testCase = + HideActionsTestCase( + recentsRotation = ROTATION_90, + touchRotation = ROTATION_0, + isRotationAllowed = false, + isFixedLandscape = true, + ), + hideActionButtonsExpected = false, + ) + } + + @Test + fun `should hide actions recent rotation 180`() { + shouldHideActionButtonTest( + testCase = + HideActionsTestCase( + recentsRotation = ROTATION_180, + touchRotation = ROTATION_0, + isRotationAllowed = false, + isFixedLandscape = false, + ), + hideActionButtonsExpected = true, + ) + } + + @Test + fun `should hide actions touch rotation 180`() { + shouldHideActionButtonTest( + testCase = + HideActionsTestCase( + recentsRotation = ROTATION_0, + touchRotation = ROTATION_180, + isRotationAllowed = false, + isFixedLandscape = false, + ), + hideActionButtonsExpected = true, + ) + } + + @Test + fun `should hide actions rotation allowed`() { + shouldHideActionButtonTest( + testCase = + HideActionsTestCase( + recentsRotation = ROTATION_90, + touchRotation = ROTATION_180, + isRotationAllowed = true, + isFixedLandscape = false, + ), + hideActionButtonsExpected = false, + ) + } + + @Test + fun `should hide actions fixed landscape rotations and not allowed`() { + shouldHideActionButtonTest( + testCase = + HideActionsTestCase( + recentsRotation = ROTATION_180, + touchRotation = ROTATION_90, + isRotationAllowed = false, + isFixedLandscape = true, + ), + hideActionButtonsExpected = false, + ) + } +} diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/RecentsOrientedStateTest.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/util/RecentsOrientedStateTest.java deleted file mode 100644 index 47ef13b42f..0000000000 --- a/quickstep/tests/multivalentTests/src/com/android/quickstep/util/RecentsOrientedStateTest.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2020 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.quickstep.util; - -import static android.view.Surface.ROTATION_0; -import static android.view.Surface.ROTATION_180; -import static android.view.Surface.ROTATION_90; - -import static androidx.test.core.app.ApplicationProvider.getApplicationContext; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; - -import android.content.Context; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import com.android.quickstep.FallbackActivityInterface; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Tests for {@link RecentsOrientedState} - */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class RecentsOrientedStateTest { - - private RecentsOrientedState mR1, mR2; - - @Before - public void setup() { - Context context = getApplicationContext(); - mR1 = new RecentsOrientedState(context, FallbackActivityInterface.INSTANCE, i -> { }); - mR2 = new RecentsOrientedState(context, FallbackActivityInterface.INSTANCE, i -> { }); - assertEquals(mR1.getStateId(), mR2.getStateId()); - } - - @Test - public void stateId_changesWithFlags() { - mR1.setGestureActive(true); - mR2.setGestureActive(false); - assertNotEquals(mR1.getStateId(), mR2.getStateId()); - - mR2.setGestureActive(true); - assertEquals(mR1.getStateId(), mR2.getStateId()); - } - - @Test - public void stateId_changesWithRecentsRotation() { - mR1.setRecentsRotation(ROTATION_90); - mR2.setRecentsRotation(ROTATION_180); - assertNotEquals(mR1.getStateId(), mR2.getStateId()); - - mR2.setRecentsRotation(ROTATION_90); - assertEquals(mR1.getStateId(), mR2.getStateId()); - } - - @Test - public void stateId_changesWithDisplayRotation() { - mR1.update(ROTATION_0, ROTATION_90); - mR2.update(ROTATION_0, ROTATION_180); - assertNotEquals(mR1.getStateId(), mR2.getStateId()); - - mR2.update(ROTATION_90, ROTATION_90); - assertNotEquals(mR1.getStateId(), mR2.getStateId()); - - mR2.update(ROTATION_90, ROTATION_0); - assertNotEquals(mR1.getStateId(), mR2.getStateId()); - - mR2.update(ROTATION_0, ROTATION_90); - assertEquals(mR1.getStateId(), mR2.getStateId()); - } -}