summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Matt Sziklay <mattsziklay@google.com> 2025-02-11 22:34:11 -0800
committer Android (Google) Code Review <android-gerrit@google.com> 2025-02-11 22:34:11 -0800
commitfe8af78f4b99265fa8efc0dc4868d35f2bf8a795 (patch)
tree07841d87ede28e51d18ac33bb4b846681682471f
parent7d2ddd33d9715f791ec4fa930a2c2aa3a46120bf (diff)
parentd091f023c0f0a5fbef13a42a728668a63703c5f7 (diff)
Merge "Add tests for VisualIndicatorViewContainer." into main
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainer.kt3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt250
2 files changed, 252 insertions, 1 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainer.kt
index 9625e48d5bd0..2317274dbbf0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainer.kt
@@ -49,7 +49,8 @@ import com.android.wm.shell.windowdecor.WindowDecoration.SurfaceControlViewHostF
* Container for the view / viewhost of the indicator, ensuring it is created / animated off the
* main thread.
*/
-private class VisualIndicatorViewContainer
+@VisibleForTesting
+class VisualIndicatorViewContainer
@JvmOverloads
constructor(
@ShellDesktopThread private val desktopExecutor: ShellExecutor,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt
new file mode 100644
index 000000000000..79b0f1c7eadd
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt
@@ -0,0 +1,250 @@
+/*
+ * 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.app.ActivityManager
+import android.app.ActivityManager.RunningTaskInfo
+import android.graphics.Rect
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.LayerDrawable
+import android.platform.test.annotations.EnableFlags
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.view.Display
+import android.view.SurfaceControl
+import android.view.SurfaceControlViewHost
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.TestRunningTaskInfoBuilder
+import com.android.wm.shell.TestShellExecutor
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.shared.bubbles.BubbleDropTargetBoundsProvider
+import com.android.wm.shell.windowdecor.WindowDecoration.SurfaceControlViewHostFactory
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import org.junit.Before
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.kotlin.any
+import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.verifyZeroInteractions
+import org.mockito.kotlin.whenever
+
+/**
+ * Test class for [VisualIndicatorViewContainer] and [VisualIndicatorAnimator]
+ *
+ * Usage: atest WMShellUnitTests:VisualIndicatorViewContainerTest
+ */
+@SmallTest
+@RunWithLooper
+@RunWith(AndroidTestingRunner::class)
+@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+class VisualIndicatorViewContainerTest : ShellTestCase() {
+ @Mock private lateinit var view: View
+ @Mock private lateinit var displayLayout: DisplayLayout
+ @Mock private lateinit var displayController: DisplayController
+ @Mock private lateinit var taskSurface: SurfaceControl
+ @Mock private lateinit var syncQueue: SyncTransactionQueue
+ @Mock private lateinit var mockSurfaceControlViewHostFactory: SurfaceControlViewHostFactory
+ @Mock private lateinit var mockBackground: LayerDrawable
+ @Mock private lateinit var bubbleDropTargetBoundsProvider: BubbleDropTargetBoundsProvider
+ private val taskInfo: RunningTaskInfo = createTaskInfo()
+ private val mainExecutor = TestShellExecutor()
+ private val desktopExecutor = TestShellExecutor()
+
+ @Before
+ fun setUp() {
+ whenever(displayController.getDisplayLayout(anyInt())).thenReturn(displayLayout)
+ whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
+ (i.arguments.first() as Rect).set(DISPLAY_BOUNDS)
+ }
+ whenever(mockSurfaceControlViewHostFactory.create(any(), any(), any()))
+ .thenReturn(mock(SurfaceControlViewHost::class.java))
+ }
+
+ @Test
+ fun testTransitionIndicator_sameTypeReturnsEarly() {
+ val spyViewContainer = setupSpyViewContainer()
+ // Test early return on startType == endType.
+ spyViewContainer.transitionIndicator(
+ taskInfo,
+ displayController,
+ DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR,
+ DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR,
+ )
+ desktopExecutor.flushAll()
+ verify(spyViewContainer)
+ .transitionIndicator(
+ eq(taskInfo),
+ eq(displayController),
+ eq(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR),
+ eq(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR),
+ )
+ // Assert fadeIn, fadeOut, and animateIndicatorType were not called.
+ verifyZeroInteractions(spyViewContainer)
+ }
+
+ @Test
+ fun testTransitionIndicator_firstTypeNoIndicator_callsFadeIn() {
+ val spyViewContainer = setupSpyViewContainer()
+ spyViewContainer.transitionIndicator(
+ taskInfo,
+ displayController,
+ DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR,
+ DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR,
+ )
+ desktopExecutor.flushAll()
+ verify(spyViewContainer).fadeInIndicator(any(), any())
+ }
+
+ @Test
+ fun testTransitionIndicator_secondTypeNoIndicator_callsFadeOut() {
+ val spyViewContainer = setupSpyViewContainer()
+ spyViewContainer.transitionIndicator(
+ taskInfo,
+ displayController,
+ DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR,
+ DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR,
+ )
+ desktopExecutor.flushAll()
+ verify(spyViewContainer)
+ .fadeOutIndicator(
+ any(),
+ eq(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR),
+ anyOrNull(),
+ )
+ }
+
+ @Test
+ fun testTransitionIndicator_differentTypes_callsTransitionIndicator() {
+ val spyViewContainer = setupSpyViewContainer()
+ spyViewContainer.transitionIndicator(
+ taskInfo,
+ displayController,
+ DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR,
+ DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR,
+ )
+ desktopExecutor.flushAll()
+ verify(spyViewContainer)
+ .transitionIndicator(
+ any(),
+ any(),
+ eq(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR),
+ eq(DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR),
+ )
+ }
+
+ @Test
+ fun testFadeInBoundsCalculation() {
+ val spyIndicator = setupSpyViewContainer()
+ val animator =
+ spyIndicator.indicatorView?.let {
+ VisualIndicatorViewContainer.VisualIndicatorAnimator.fadeBoundsIn(
+ it,
+ DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR,
+ displayLayout,
+ bubbleDropTargetBoundsProvider,
+ )
+ }
+ assertThat(animator?.indicatorStartBounds).isEqualTo(Rect(15, 15, 985, 985))
+ assertThat(animator?.indicatorEndBounds).isEqualTo(Rect(0, 0, 1000, 1000))
+ }
+
+ @Test
+ fun testFadeOutBoundsCalculation() {
+ val spyIndicator = setupSpyViewContainer()
+ val animator =
+ spyIndicator.indicatorView?.let {
+ VisualIndicatorViewContainer.VisualIndicatorAnimator.fadeBoundsOut(
+ it,
+ DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR,
+ displayLayout,
+ bubbleDropTargetBoundsProvider,
+ )
+ }
+ assertThat(animator?.indicatorStartBounds).isEqualTo(Rect(0, 0, 1000, 1000))
+ assertThat(animator?.indicatorEndBounds).isEqualTo(Rect(15, 15, 985, 985))
+ }
+
+ @Test
+ fun testChangeIndicatorTypeBoundsCalculation() {
+ // Test fullscreen to split-left bounds.
+ var animator =
+ VisualIndicatorViewContainer.VisualIndicatorAnimator.animateIndicatorType(
+ view,
+ displayLayout,
+ DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR,
+ DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR,
+ bubbleDropTargetBoundsProvider,
+ )
+ // Test desktop to split-right bounds.
+ animator =
+ VisualIndicatorViewContainer.VisualIndicatorAnimator.animateIndicatorType(
+ view,
+ displayLayout,
+ DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR,
+ DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR,
+ bubbleDropTargetBoundsProvider,
+ )
+ }
+
+ private fun setupSpyViewContainer(): VisualIndicatorViewContainer {
+ val viewContainer =
+ VisualIndicatorViewContainer(
+ desktopExecutor,
+ mainExecutor,
+ SurfaceControl.Builder(),
+ syncQueue,
+ mockSurfaceControlViewHostFactory,
+ bubbleDropTargetBoundsProvider,
+ )
+ viewContainer.createView(
+ context,
+ mock(Display::class.java),
+ displayLayout,
+ taskInfo,
+ taskSurface,
+ )
+ desktopExecutor.flushAll()
+ viewContainer.indicatorView?.background = mockBackground
+ whenever(mockBackground.findDrawableByLayerId(anyInt()))
+ .thenReturn(mock(Drawable::class.java))
+ return spy(viewContainer)
+ }
+
+ private fun createTaskInfo(): RunningTaskInfo {
+ val taskDescriptionBuilder = ActivityManager.TaskDescription.Builder()
+ return TestRunningTaskInfoBuilder()
+ .setDisplayId(Display.DEFAULT_DISPLAY)
+ .setTaskDescriptionBuilder(taskDescriptionBuilder)
+ .setVisible(true)
+ .build()
+ }
+
+ companion object {
+ private val DISPLAY_BOUNDS = Rect(0, 0, 1000, 1000)
+ }
+}