diff options
author | 2023-10-26 16:45:31 -0400 | |
---|---|---|
committer | 2024-01-04 14:51:46 -0500 | |
commit | 232a698770bdc5688015650a14d2f88ad94acf15 (patch) | |
tree | 785f67833fa0221f66ac01ede92c8700d7c9a2c9 | |
parent | af6e1e5b229d29c1ca3770ae0bbf09f0e24a4d3b (diff) |
Convert BubblePositionerTest to deviceless testing
This change introduces Robolectric to WMShell and converts BubblePositionerTest
to a bivalent test.
Flag: NONE
Bug: 308004028
Test: atest BubblePositionerTest
Change-Id: I0aa7ccd99d20fadc86429615f249124eb3f4f51b
9 files changed, 603 insertions, 602 deletions
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 5ad144d50b87..45540e0fbbb8 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -175,3 +175,74 @@ android_library { plugins: ["dagger2-compiler"], use_resource_processor: true, } + +android_app { + name: "WindowManagerShellRobolectric", + platform_apis: true, + static_libs: [ + "WindowManager-Shell", + ], + manifest: "multivalentTests/AndroidManifestRobolectric.xml", + use_resource_processor: true, +} + +android_robolectric_test { + name: "WMShellRobolectricTests", + instrumentation_for: "WindowManagerShellRobolectric", + upstream: true, + java_resource_dirs: [ + "multivalentTests/robolectric/config", + ], + srcs: [ + "multivalentTests/src/**/*.kt", + ], + static_libs: [ + "junit", + "androidx.test.runner", + "androidx.test.rules", + "androidx.test.ext.junit", + "mockito-robolectric-prebuilt", + "mockito-kotlin2", + "truth", + ], +} + +android_test { + name: "WMShellMultivalentTestsOnDevice", + srcs: [ + "multivalentTests/src/**/*.kt", + ], + static_libs: [ + "WindowManager-Shell", + "junit", + "androidx.test.runner", + "androidx.test.rules", + "androidx.test.ext.junit", + "frameworks-base-testutils", + "mockito-kotlin2", + "mockito-target-extended-minus-junit4", + "truth", + "platform-test-annotations", + "platform-test-rules", + ], + libs: [ + "android.test.base", + "android.test.runner", + ], + jni_libs: [ + "libdexmakerjvmtiagent", + "libstaticjvmtiagent", + ], + kotlincflags: ["-Xjvm-default=all"], + optimize: { + enabled: false, + }, + test_suites: ["device-tests"], + platform_apis: true, + certificate: "platform", + aaptflags: [ + "--extra-packages", + "com.android.wm.shell", + ], + manifest: "multivalentTests/AndroidManifest.xml", +} diff --git a/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml b/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml new file mode 100644 index 000000000000..f8f8338e5f04 --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTests/AndroidManifest.xml @@ -0,0 +1,13 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.wm.shell.multivalenttests"> + + <application android:debuggable="true" android:supportsRtl="true" > + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:label="Multivalent tests for WindowManager-Shell" + android:targetPackage="com.android.wm.shell.multivalenttests"> + </instrumentation> +</manifest> diff --git a/libs/WindowManager/Shell/multivalentTests/AndroidManifestRobolectric.xml b/libs/WindowManager/Shell/multivalentTests/AndroidManifestRobolectric.xml new file mode 100644 index 000000000000..ffcd7d46fbae --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTests/AndroidManifestRobolectric.xml @@ -0,0 +1,3 @@ +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.wm.shell.multivalenttests"> +</manifest> diff --git a/libs/WindowManager/Shell/multivalentTests/AndroidTest.xml b/libs/WindowManager/Shell/multivalentTests/AndroidTest.xml new file mode 100644 index 000000000000..36fe8ec3370d --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTests/AndroidTest.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 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. +--> +<configuration description="Runs Tests for WindowManagerShellLib"> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="install-arg" value="-t" /> + <option name="test-file-name" value="WMShellMultivalentTestsOnDevice.apk" /> + </target_preparer> + + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="framework-base-presubmit" /> + <option name="test-tag" value="WMShellMultivalentTestsOnDevice" /> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="com.android.wm.shell.multivalenttests" /> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration> diff --git a/libs/WindowManager/Shell/multivalentTests/robolectric/config/robolectric.properties b/libs/WindowManager/Shell/multivalentTests/robolectric/config/robolectric.properties new file mode 100644 index 000000000000..7a0527ccaafb --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTests/robolectric/config/robolectric.properties @@ -0,0 +1,2 @@ +sdk=NEWEST_SDK + diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt new file mode 100644 index 000000000000..ea7c6edd4b56 --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubblePositionerTest.kt @@ -0,0 +1,481 @@ +/* + * Copyright (C) 2023 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.bubbles + +import android.content.Context +import android.content.Intent +import android.content.pm.ShortcutInfo +import android.graphics.Insets +import android.graphics.PointF +import android.graphics.Rect +import android.os.UserHandle +import android.view.WindowManager +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.wm.shell.R +import com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT +import com.google.common.truth.Truth.assertThat +import com.google.common.util.concurrent.MoreExecutors.directExecutor +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +/** Tests operations and the resulting state managed by [BubblePositioner]. */ +@SmallTest +@RunWith(AndroidJUnit4::class) +class BubblePositionerTest { + + private lateinit var positioner: BubblePositioner + private val context = ApplicationProvider.getApplicationContext<Context>() + private val defaultDeviceConfig = + DeviceConfig( + windowBounds = Rect(0, 0, 1000, 2000), + isLargeScreen = false, + isSmallTablet = false, + isLandscape = false, + isRtl = false, + insets = Insets.of(0, 0, 0, 0) + ) + + @Before + fun setUp() { + val windowManager = context.getSystemService(WindowManager::class.java) + positioner = BubblePositioner(context, windowManager) + } + + @Test + fun testUpdate() { + val insets = Insets.of(10, 20, 5, 15) + val screenBounds = Rect(0, 0, 1000, 1200) + val availableRect = Rect(screenBounds) + availableRect.inset(insets) + positioner.update(defaultDeviceConfig.copy(insets = insets, windowBounds = screenBounds)) + assertThat(positioner.availableRect).isEqualTo(availableRect) + assertThat(positioner.isLandscape).isFalse() + assertThat(positioner.isLargeScreen).isFalse() + assertThat(positioner.insets).isEqualTo(insets) + } + + @Test + fun testShowBubblesVertically_phonePortrait() { + positioner.update(defaultDeviceConfig) + assertThat(positioner.showBubblesVertically()).isFalse() + } + + @Test + fun testShowBubblesVertically_phoneLandscape() { + positioner.update(defaultDeviceConfig.copy(isLandscape = true)) + assertThat(positioner.isLandscape).isTrue() + assertThat(positioner.showBubblesVertically()).isTrue() + } + + @Test + fun testShowBubblesVertically_tablet() { + positioner.update(defaultDeviceConfig.copy(isLargeScreen = true)) + assertThat(positioner.showBubblesVertically()).isTrue() + } + + /** If a resting position hasn't been set, calling it will return the default position. */ + @Test + fun testGetRestingPosition_returnsDefaultPosition() { + positioner.update(defaultDeviceConfig) + val restingPosition = positioner.getRestingPosition() + val defaultPosition = positioner.defaultStartPosition + assertThat(restingPosition).isEqualTo(defaultPosition) + } + + /** If a resting position has been set, it'll return that instead of the default position. */ + @Test + fun testGetRestingPosition_returnsRestingPosition() { + positioner.update(defaultDeviceConfig) + val restingPosition = PointF(100f, 100f) + positioner.restingPosition = restingPosition + assertThat(positioner.getRestingPosition()).isEqualTo(restingPosition) + } + + /** Test that the default resting position on phone is in upper left. */ + @Test + fun testGetRestingPosition_bubble_onPhone() { + positioner.update(defaultDeviceConfig) + val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */) + val restingPosition = positioner.getRestingPosition() + assertThat(restingPosition.x).isEqualTo(allowableStackRegion.left) + assertThat(restingPosition.y).isEqualTo(defaultYPosition) + } + + @Test + fun testGetRestingPosition_bubble_onPhone_RTL() { + positioner.update(defaultDeviceConfig.copy(isRtl = true)) + val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */) + val restingPosition = positioner.getRestingPosition() + assertThat(restingPosition.x).isEqualTo(allowableStackRegion.right) + assertThat(restingPosition.y).isEqualTo(defaultYPosition) + } + + /** Test that the default resting position on tablet is middle left. */ + @Test + fun testGetRestingPosition_chatBubble_onTablet() { + positioner.update(defaultDeviceConfig.copy(isLargeScreen = true)) + val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */) + val restingPosition = positioner.getRestingPosition() + assertThat(restingPosition.x).isEqualTo(allowableStackRegion.left) + assertThat(restingPosition.y).isEqualTo(defaultYPosition) + } + + @Test + fun testGetRestingPosition_chatBubble_onTablet_RTL() { + positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true)) + val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */) + val restingPosition = positioner.getRestingPosition() + assertThat(restingPosition.x).isEqualTo(allowableStackRegion.right) + assertThat(restingPosition.y).isEqualTo(defaultYPosition) + } + + /** Test that the default resting position on tablet is middle right. */ + @Test + fun testGetDefaultPosition_appBubble_onTablet() { + positioner.update(defaultDeviceConfig.copy(isLargeScreen = true)) + val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */) + val startPosition = positioner.getDefaultStartPosition(true /* isAppBubble */) + assertThat(startPosition.x).isEqualTo(allowableStackRegion.right) + assertThat(startPosition.y).isEqualTo(defaultYPosition) + } + + @Test + fun testGetRestingPosition_appBubble_onTablet_RTL() { + positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true)) + val allowableStackRegion = positioner.getAllowableStackPositionRegion(1 /* bubbleCount */) + val startPosition = positioner.getDefaultStartPosition(true /* isAppBubble */) + assertThat(startPosition.x).isEqualTo(allowableStackRegion.left) + assertThat(startPosition.y).isEqualTo(defaultYPosition) + } + + @Test + fun testHasUserModifiedDefaultPosition_false() { + positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true)) + assertThat(positioner.hasUserModifiedDefaultPosition()).isFalse() + positioner.restingPosition = positioner.defaultStartPosition + assertThat(positioner.hasUserModifiedDefaultPosition()).isFalse() + } + + @Test + fun testHasUserModifiedDefaultPosition_true() { + positioner.update(defaultDeviceConfig.copy(isLargeScreen = true, isRtl = true)) + assertThat(positioner.hasUserModifiedDefaultPosition()).isFalse() + positioner.restingPosition = PointF(0f, 100f) + assertThat(positioner.hasUserModifiedDefaultPosition()).isTrue() + } + + @Test + fun testGetExpandedViewHeight_max() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName) + val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor()) + + assertThat(positioner.getExpandedViewHeight(bubble)).isEqualTo(MAX_HEIGHT) + } + + @Test + fun testGetExpandedViewHeight_customHeight_valid() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + val minHeight = + context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_default_height) + val bubble = + Bubble( + "key", + ShortcutInfo.Builder(context, "id").build(), + minHeight + 100 /* desiredHeight */, + 0 /* desiredHeightResId */, + "title", + 0 /* taskId */, + null /* locus */, + true /* isDismissable */, + directExecutor()) {} + + // Ensure the height is the same as the desired value + assertThat(positioner.getExpandedViewHeight(bubble)) + .isEqualTo(bubble.getDesiredHeight(context)) + } + + @Test + fun testGetExpandedViewHeight_customHeight_tooSmall() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val bubble = + Bubble( + "key", + ShortcutInfo.Builder(context, "id").build(), + 10 /* desiredHeight */, + 0 /* desiredHeightResId */, + "title", + 0 /* taskId */, + null /* locus */, + true /* isDismissable */, + directExecutor()) {} + + // Ensure the height is the same as the desired value + val minHeight = + context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_default_height) + assertThat(positioner.getExpandedViewHeight(bubble)).isEqualTo(minHeight) + } + + @Test + fun testGetMaxExpandedViewHeight_onLargeTablet() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val manageButtonHeight = + context.resources.getDimensionPixelSize(R.dimen.bubble_manage_button_height) + val pointerWidth = context.resources.getDimensionPixelSize(R.dimen.bubble_pointer_width) + val expandedViewPadding = + context.resources.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding) + val expectedHeight = + 1800 - 2 * 20 - manageButtonHeight - pointerWidth - expandedViewPadding * 2 + assertThat(positioner.getMaxExpandedViewHeight(false /* isOverflow */)) + .isEqualTo(expectedHeight) + } + + @Test + fun testAreBubblesBottomAligned_largeScreen_true() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + assertThat(positioner.areBubblesBottomAligned()).isTrue() + } + + @Test + fun testAreBubblesBottomAligned_largeScreen_landscape_false() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + isLandscape = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + assertThat(positioner.areBubblesBottomAligned()).isFalse() + } + + @Test + fun testAreBubblesBottomAligned_smallTablet_false() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + isSmallTablet = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + assertThat(positioner.areBubblesBottomAligned()).isFalse() + } + + @Test + fun testAreBubblesBottomAligned_phone_false() { + val deviceConfig = + defaultDeviceConfig.copy( + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + assertThat(positioner.areBubblesBottomAligned()).isFalse() + } + + @Test + fun testExpandedViewY_phoneLandscape() { + val deviceConfig = + defaultDeviceConfig.copy( + isLandscape = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName) + val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor()) + + // This bubble will have max height so it'll always be top aligned + assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) + .isEqualTo(positioner.getExpandedViewYTopAligned()) + } + + @Test + fun testExpandedViewY_phonePortrait() { + val deviceConfig = + defaultDeviceConfig.copy( + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName) + val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor()) + + // Always top aligned in phone portrait + assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) + .isEqualTo(positioner.getExpandedViewYTopAligned()) + } + + @Test + fun testExpandedViewY_smallTabletLandscape() { + val deviceConfig = + defaultDeviceConfig.copy( + isSmallTablet = true, + isLandscape = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName) + val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor()) + + // This bubble will have max height which is always top aligned on small tablets + assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) + .isEqualTo(positioner.getExpandedViewYTopAligned()) + } + + @Test + fun testExpandedViewY_smallTabletPortrait() { + val deviceConfig = + defaultDeviceConfig.copy( + isSmallTablet = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName) + val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor()) + + // This bubble will have max height which is always top aligned on small tablets + assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) + .isEqualTo(positioner.getExpandedViewYTopAligned()) + } + + @Test + fun testExpandedViewY_largeScreenLandscape() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + isLandscape = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName) + val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor()) + + // This bubble will have max height which is always top aligned on landscape, large tablet + assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) + .isEqualTo(positioner.getExpandedViewYTopAligned()) + } + + @Test + fun testExpandedViewY_largeScreenPortrait() { + val deviceConfig = + defaultDeviceConfig.copy( + isLargeScreen = true, + insets = Insets.of(10, 20, 5, 15), + windowBounds = Rect(0, 0, 1800, 2600) + ) + positioner.update(deviceConfig) + + val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName) + val bubble = Bubble.createAppBubble(intent, UserHandle(1), null, directExecutor()) + + val manageButtonHeight = + context.resources.getDimensionPixelSize(R.dimen.bubble_manage_button_height) + val manageButtonPlusMargin = + manageButtonHeight + + 2 * context.resources.getDimensionPixelSize(R.dimen.bubble_manage_button_margin) + val pointerWidth = context.resources.getDimensionPixelSize(R.dimen.bubble_pointer_width) + + val expectedExpandedViewY = + positioner.availableRect.bottom - + manageButtonPlusMargin - + positioner.getExpandedViewHeightForLargeScreen() - + pointerWidth + + // Bubbles are bottom aligned on portrait, large tablet + assertThat(positioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) + .isEqualTo(expectedExpandedViewY) + } + + private val defaultYPosition: Float + /** + * Calculates the Y position bubbles should be placed based on the config. Based on the + * calculations in [BubblePositioner.getDefaultStartPosition] and + * [BubbleStackView.RelativeStackPosition]. + */ + get() { + val isTablet = positioner.isLargeScreen + + // On tablet the position is centered, on phone it is an offset from the top. + val desiredY = + if (isTablet) { + positioner.screenRect.height() / 2f - positioner.bubbleSize / 2f + } else { + context.resources + .getDimensionPixelOffset(R.dimen.bubble_stack_starting_offset_y) + .toFloat() + } + // Since we're visually centering the bubbles on tablet, use total screen height rather + // than the available height. + val height = + if (isTablet) { + positioner.screenRect.height() + } else { + positioner.availableRect.height() + } + val offsetPercent = (desiredY / height).coerceIn(0f, 1f) + val allowableStackRegion = + positioner.getAllowableStackPositionRegion(1 /* bubbleCount */) + return allowableStackRegion.top + allowableStackRegion.height() * offsetPercent + } +} diff --git a/libs/WindowManager/Shell/multivalentTestsForDevice b/libs/WindowManager/Shell/multivalentTestsForDevice new file mode 120000 index 000000000000..20ee34ada103 --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTestsForDevice @@ -0,0 +1 @@ +multivalentTests
\ No newline at end of file diff --git a/libs/WindowManager/Shell/multivalentTestsForDeviceless b/libs/WindowManager/Shell/multivalentTestsForDeviceless new file mode 120000 index 000000000000..20ee34ada103 --- /dev/null +++ b/libs/WindowManager/Shell/multivalentTestsForDeviceless @@ -0,0 +1 @@ +multivalentTests
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java deleted file mode 100644 index 6ebee730756e..000000000000 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblePositionerTest.java +++ /dev/null @@ -1,602 +0,0 @@ -/* - * Copyright (C) 2023 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.bubbles; - -import static com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT; - -import static com.google.common.truth.Truth.assertThat; -import static com.google.common.util.concurrent.MoreExecutors.directExecutor; - -import static org.mockito.Mockito.mock; - -import android.content.Intent; -import android.content.pm.ShortcutInfo; -import android.graphics.Insets; -import android.graphics.PointF; -import android.graphics.Rect; -import android.graphics.RectF; -import android.os.UserHandle; -import android.testing.AndroidTestingRunner; -import android.view.WindowManager; - -import androidx.test.filters.SmallTest; - -import com.android.wm.shell.R; -import com.android.wm.shell.ShellTestCase; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Tests operations and the resulting state managed by {@link BubblePositioner}. - */ -@SmallTest -@RunWith(AndroidTestingRunner.class) -public class BubblePositionerTest extends ShellTestCase { - - private BubblePositioner mPositioner; - - @Before - public void setUp() { - WindowManager windowManager = mContext.getSystemService(WindowManager.class); - mPositioner = new BubblePositioner(mContext, windowManager); - } - - @Test - public void testUpdate() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1000, 1200); - Rect availableRect = new Rect(screenBounds); - availableRect.inset(insets); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.getAvailableRect()).isEqualTo(availableRect); - assertThat(mPositioner.isLandscape()).isFalse(); - assertThat(mPositioner.isLargeScreen()).isFalse(); - assertThat(mPositioner.getInsets()).isEqualTo(insets); - } - - @Test - public void testShowBubblesVertically_phonePortrait() { - DeviceConfig deviceConfig = new ConfigBuilder().build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.showBubblesVertically()).isFalse(); - } - - @Test - public void testShowBubblesVertically_phoneLandscape() { - DeviceConfig deviceConfig = new ConfigBuilder().setLandscape().build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.isLandscape()).isTrue(); - assertThat(mPositioner.showBubblesVertically()).isTrue(); - } - - @Test - public void testShowBubblesVertically_tablet() { - DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.showBubblesVertically()).isTrue(); - } - - /** If a resting position hasn't been set, calling it will return the default position. */ - @Test - public void testGetRestingPosition_returnsDefaultPosition() { - DeviceConfig deviceConfig = new ConfigBuilder().build(); - mPositioner.update(deviceConfig); - - PointF restingPosition = mPositioner.getRestingPosition(); - PointF defaultPosition = mPositioner.getDefaultStartPosition(); - - assertThat(restingPosition).isEqualTo(defaultPosition); - } - - /** If a resting position has been set, it'll return that instead of the default position. */ - @Test - public void testGetRestingPosition_returnsRestingPosition() { - DeviceConfig deviceConfig = new ConfigBuilder().build(); - mPositioner.update(deviceConfig); - - PointF restingPosition = new PointF(100, 100); - mPositioner.setRestingPosition(restingPosition); - - assertThat(mPositioner.getRestingPosition()).isEqualTo(restingPosition); - } - - /** Test that the default resting position on phone is in upper left. */ - @Test - public void testGetRestingPosition_bubble_onPhone() { - DeviceConfig deviceConfig = new ConfigBuilder().build(); - mPositioner.update(deviceConfig); - - RectF allowableStackRegion = - mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */); - PointF restingPosition = mPositioner.getRestingPosition(); - - assertThat(restingPosition.x).isEqualTo(allowableStackRegion.left); - assertThat(restingPosition.y).isEqualTo(getDefaultYPosition()); - } - - @Test - public void testGetRestingPosition_bubble_onPhone_RTL() { - DeviceConfig deviceConfig = new ConfigBuilder().setRtl().build(); - mPositioner.update(deviceConfig); - - RectF allowableStackRegion = - mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */); - PointF restingPosition = mPositioner.getRestingPosition(); - - assertThat(restingPosition.x).isEqualTo(allowableStackRegion.right); - assertThat(restingPosition.y).isEqualTo(getDefaultYPosition()); - } - - /** Test that the default resting position on tablet is middle left. */ - @Test - public void testGetRestingPosition_chatBubble_onTablet() { - DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().build(); - mPositioner.update(deviceConfig); - - RectF allowableStackRegion = - mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */); - PointF restingPosition = mPositioner.getRestingPosition(); - - assertThat(restingPosition.x).isEqualTo(allowableStackRegion.left); - assertThat(restingPosition.y).isEqualTo(getDefaultYPosition()); - } - - @Test - public void testGetRestingPosition_chatBubble_onTablet_RTL() { - DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().setRtl().build(); - mPositioner.update(deviceConfig); - - RectF allowableStackRegion = - mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */); - PointF restingPosition = mPositioner.getRestingPosition(); - - assertThat(restingPosition.x).isEqualTo(allowableStackRegion.right); - assertThat(restingPosition.y).isEqualTo(getDefaultYPosition()); - } - - /** Test that the default resting position on tablet is middle right. */ - @Test - public void testGetDefaultPosition_appBubble_onTablet() { - DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().build(); - mPositioner.update(deviceConfig); - - RectF allowableStackRegion = - mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */); - PointF startPosition = mPositioner.getDefaultStartPosition(true /* isAppBubble */); - - assertThat(startPosition.x).isEqualTo(allowableStackRegion.right); - assertThat(startPosition.y).isEqualTo(getDefaultYPosition()); - } - - @Test - public void testGetRestingPosition_appBubble_onTablet_RTL() { - DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().setRtl().build(); - mPositioner.update(deviceConfig); - - RectF allowableStackRegion = - mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */); - PointF startPosition = mPositioner.getDefaultStartPosition(true /* isAppBubble */); - - assertThat(startPosition.x).isEqualTo(allowableStackRegion.left); - assertThat(startPosition.y).isEqualTo(getDefaultYPosition()); - } - - @Test - public void testHasUserModifiedDefaultPosition_false() { - DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().setRtl().build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.hasUserModifiedDefaultPosition()).isFalse(); - - mPositioner.setRestingPosition(mPositioner.getDefaultStartPosition()); - - assertThat(mPositioner.hasUserModifiedDefaultPosition()).isFalse(); - } - - @Test - public void testHasUserModifiedDefaultPosition_true() { - DeviceConfig deviceConfig = new ConfigBuilder().setLargeScreen().setRtl().build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.hasUserModifiedDefaultPosition()).isFalse(); - - mPositioner.setRestingPosition(new PointF(0, 100)); - - assertThat(mPositioner.hasUserModifiedDefaultPosition()).isTrue(); - } - - @Test - public void testGetExpandedViewHeight_max() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName()); - Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor()); - - assertThat(mPositioner.getExpandedViewHeight(bubble)).isEqualTo(MAX_HEIGHT); - } - - @Test - public void testGetExpandedViewHeight_customHeight_valid() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - final int minHeight = mContext.getResources().getDimensionPixelSize( - R.dimen.bubble_expanded_default_height); - Bubble bubble = new Bubble("key", - mock(ShortcutInfo.class), - minHeight + 100 /* desiredHeight */, - 0 /* desiredHeightResId */, - "title", - 0 /* taskId */, - null /* locus */, - true /* isDismissable */, - directExecutor(), - mock(Bubbles.BubbleMetadataFlagListener.class)); - - // Ensure the height is the same as the desired value - assertThat(mPositioner.getExpandedViewHeight(bubble)).isEqualTo( - bubble.getDesiredHeight(mContext)); - } - - - @Test - public void testGetExpandedViewHeight_customHeight_tooSmall() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Bubble bubble = new Bubble("key", - mock(ShortcutInfo.class), - 10 /* desiredHeight */, - 0 /* desiredHeightResId */, - "title", - 0 /* taskId */, - null /* locus */, - true /* isDismissable */, - directExecutor(), - mock(Bubbles.BubbleMetadataFlagListener.class)); - - // Ensure the height is the same as the minimum value - final int minHeight = mContext.getResources().getDimensionPixelSize( - R.dimen.bubble_expanded_default_height); - assertThat(mPositioner.getExpandedViewHeight(bubble)).isEqualTo(minHeight); - } - - @Test - public void testGetMaxExpandedViewHeight_onLargeTablet() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - int manageButtonHeight = - mContext.getResources().getDimensionPixelSize(R.dimen.bubble_manage_button_height); - int pointerWidth = mContext.getResources().getDimensionPixelSize( - R.dimen.bubble_pointer_width); - int expandedViewPadding = mContext.getResources().getDimensionPixelSize(R - .dimen.bubble_expanded_view_padding); - float expectedHeight = 1800 - 2 * 20 - manageButtonHeight - pointerWidth - - expandedViewPadding * 2; - assertThat(((float) mPositioner.getMaxExpandedViewHeight(false /* isOverflow */))) - .isWithin(0.1f).of(expectedHeight); - } - - @Test - public void testAreBubblesBottomAligned_largeScreen_true() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.areBubblesBottomAligned()).isTrue(); - } - - @Test - public void testAreBubblesBottomAligned_largeScreen_false() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setLandscape() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.areBubblesBottomAligned()).isFalse(); - } - - @Test - public void testAreBubblesBottomAligned_smallTablet_false() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setSmallTablet() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.areBubblesBottomAligned()).isFalse(); - } - - @Test - public void testAreBubblesBottomAligned_phone_false() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - assertThat(mPositioner.areBubblesBottomAligned()).isFalse(); - } - - @Test - public void testExpandedViewY_phoneLandscape() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLandscape() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName()); - Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor()); - - // This bubble will have max height so it'll always be top aligned - assertThat(mPositioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) - .isEqualTo(mPositioner.getExpandedViewYTopAligned()); - } - - @Test - public void testExpandedViewY_phonePortrait() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName()); - Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor()); - - // Always top aligned in phone portrait - assertThat(mPositioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) - .isEqualTo(mPositioner.getExpandedViewYTopAligned()); - } - - @Test - public void testExpandedViewY_smallTabletLandscape() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setSmallTablet() - .setLandscape() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName()); - Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor()); - - // This bubble will have max height which is always top aligned on small tablets - assertThat(mPositioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) - .isEqualTo(mPositioner.getExpandedViewYTopAligned()); - } - - @Test - public void testExpandedViewY_smallTabletPortrait() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setSmallTablet() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName()); - Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor()); - - // This bubble will have max height which is always top aligned on small tablets - assertThat(mPositioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) - .isEqualTo(mPositioner.getExpandedViewYTopAligned()); - } - - @Test - public void testExpandedViewY_largeScreenLandscape() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setLandscape() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName()); - Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor()); - - // This bubble will have max height which is always top aligned on landscape, large tablet - assertThat(mPositioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) - .isEqualTo(mPositioner.getExpandedViewYTopAligned()); - } - - @Test - public void testExpandedViewY_largeScreenPortrait() { - Insets insets = Insets.of(10, 20, 5, 15); - Rect screenBounds = new Rect(0, 0, 1800, 2600); - - DeviceConfig deviceConfig = new ConfigBuilder() - .setLargeScreen() - .setInsets(insets) - .setScreenBounds(screenBounds) - .build(); - mPositioner.update(deviceConfig); - - Intent intent = new Intent(Intent.ACTION_VIEW).setPackage(mContext.getPackageName()); - Bubble bubble = Bubble.createAppBubble(intent, new UserHandle(1), null, directExecutor()); - - int manageButtonHeight = - mContext.getResources().getDimensionPixelSize(R.dimen.bubble_manage_button_height); - int manageButtonPlusMargin = manageButtonHeight + 2 - * mContext.getResources().getDimensionPixelSize( - R.dimen.bubble_manage_button_margin); - int pointerWidth = mContext.getResources().getDimensionPixelSize( - R.dimen.bubble_pointer_width); - - final float expectedExpandedViewY = mPositioner.getAvailableRect().bottom - - manageButtonPlusMargin - - mPositioner.getExpandedViewHeightForLargeScreen() - - pointerWidth; - - // Bubbles are bottom aligned on portrait, large tablet - assertThat(mPositioner.getExpandedViewY(bubble, 0f /* bubblePosition */)) - .isEqualTo(expectedExpandedViewY); - } - - /** - * Calculates the Y position bubbles should be placed based on the config. Based on - * the calculations in {@link BubblePositioner#getDefaultStartPosition()} and - * {@link BubbleStackView.RelativeStackPosition}. - */ - private float getDefaultYPosition() { - final boolean isTablet = mPositioner.isLargeScreen(); - - // On tablet the position is centered, on phone it is an offset from the top. - final float desiredY = isTablet - ? mPositioner.getScreenRect().height() / 2f - (mPositioner.getBubbleSize() / 2f) - : mContext.getResources().getDimensionPixelOffset( - R.dimen.bubble_stack_starting_offset_y); - // Since we're visually centering the bubbles on tablet, use total screen height rather - // than the available height. - final float height = isTablet - ? mPositioner.getScreenRect().height() - : mPositioner.getAvailableRect().height(); - float offsetPercent = desiredY / height; - offsetPercent = Math.max(0f, Math.min(1f, offsetPercent)); - final RectF allowableStackRegion = - mPositioner.getAllowableStackPositionRegion(1 /* bubbleCount */); - return allowableStackRegion.top + allowableStackRegion.height() * offsetPercent; - } - - /** - * Sets up window manager to return config values based on what you need for the test. - * By default it sets up a portrait phone without any insets. - */ - private static class ConfigBuilder { - private Rect mScreenBounds = new Rect(0, 0, 1000, 2000); - private boolean mIsLargeScreen = false; - private boolean mIsSmallTablet = false; - private boolean mIsLandscape = false; - private boolean mIsRtl = false; - private Insets mInsets = Insets.of(0, 0, 0, 0); - - public ConfigBuilder setScreenBounds(Rect screenBounds) { - mScreenBounds = screenBounds; - return this; - } - - public ConfigBuilder setLargeScreen() { - mIsLargeScreen = true; - return this; - } - - public ConfigBuilder setSmallTablet() { - mIsSmallTablet = true; - return this; - } - - public ConfigBuilder setLandscape() { - mIsLandscape = true; - return this; - } - - public ConfigBuilder setRtl() { - mIsRtl = true; - return this; - } - - public ConfigBuilder setInsets(Insets insets) { - mInsets = insets; - return this; - } - - private DeviceConfig build() { - return new DeviceConfig(mIsLargeScreen, mIsSmallTablet, mIsLandscape, mIsRtl, - mScreenBounds, mInsets); - } - } -} |