1 /*
<lambda>null2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.wm.shell.flicker.service.splitscreen.scenarios
18 
19 import android.app.Instrumentation
20 import android.graphics.Point
21 import android.tools.NavBar
22 import android.tools.Rotation
23 import android.tools.helpers.WindowUtils
24 import android.tools.traces.parsers.WindowManagerStateHelper
25 import androidx.test.platform.app.InstrumentationRegistry
26 import androidx.test.uiautomator.UiDevice
27 import com.android.launcher3.tapl.LauncherInstrumentation
28 import com.android.wm.shell.flicker.service.common.Utils
29 import com.android.wm.shell.flicker.utils.SplitScreenUtils
30 import org.junit.After
31 import org.junit.Before
32 import org.junit.Ignore
33 import org.junit.Rule
34 import org.junit.Test
35 
36 @Ignore("Base Test Class")
37 abstract class SwitchAppByDoubleTapDivider
38 @JvmOverloads
39 constructor(val rotation: Rotation = Rotation.ROTATION_0) {
40     private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
41     private val tapl = LauncherInstrumentation()
42     private val wmHelper = WindowManagerStateHelper(instrumentation)
43     private val device = UiDevice.getInstance(instrumentation)
44     private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
45     private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
46 
47     @Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
48 
49     @Before
50     fun setup() {
51         tapl.workspace.switchToOverview().dismissAllTasks()
52 
53         tapl.setEnableRotation(true)
54         tapl.setExpectedRotation(rotation.value)
55 
56         SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp, rotation)
57     }
58 
59     @Test
60     open fun switchAppByDoubleTapDivider() {
61         SplitScreenUtils.doubleTapDividerToSwitch(device)
62         wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
63 
64         waitForLayersToSwitch(wmHelper)
65         waitForWindowsToSwitch(wmHelper)
66     }
67 
68     @After
69     fun teardown() {
70         primaryApp.exit(wmHelper)
71         secondaryApp.exit(wmHelper)
72     }
73 
74     private fun waitForWindowsToSwitch(wmHelper: WindowManagerStateHelper) {
75         wmHelper
76             .StateSyncBuilder()
77             .add("appWindowsSwitched") {
78                 val primaryAppWindow =
79                     it.wmState.visibleWindows.firstOrNull { window ->
80                         primaryApp.windowMatchesAnyOf(window)
81                     }
82                         ?: return@add false
83                 val secondaryAppWindow =
84                     it.wmState.visibleWindows.firstOrNull { window ->
85                         secondaryApp.windowMatchesAnyOf(window)
86                     }
87                         ?: return@add false
88 
89                 if (isLandscape(rotation)) {
90                     return@add if (isTablet()) {
91                         secondaryAppWindow.frame.right <= primaryAppWindow.frame.left
92                     } else {
93                         primaryAppWindow.frame.right <= secondaryAppWindow.frame.left
94                     }
95                 } else {
96                     return@add if (isTablet()) {
97                         primaryAppWindow.frame.bottom <= secondaryAppWindow.frame.top
98                     } else {
99                         primaryAppWindow.frame.bottom <= secondaryAppWindow.frame.top
100                     }
101                 }
102             }
103             .waitForAndVerify()
104     }
105 
106     private fun waitForLayersToSwitch(wmHelper: WindowManagerStateHelper) {
107         wmHelper
108             .StateSyncBuilder()
109             .add("appLayersSwitched") {
110                 val primaryAppLayer =
111                     it.layerState.visibleLayers.firstOrNull { window ->
112                         primaryApp.layerMatchesAnyOf(window)
113                     }
114                         ?: return@add false
115                 val secondaryAppLayer =
116                     it.layerState.visibleLayers.firstOrNull { window ->
117                         secondaryApp.layerMatchesAnyOf(window)
118                     }
119                         ?: return@add false
120 
121                 val primaryVisibleRegion = primaryAppLayer.visibleRegion?.bounds ?: return@add false
122                 val secondaryVisibleRegion =
123                     secondaryAppLayer.visibleRegion?.bounds ?: return@add false
124 
125                 if (isLandscape(rotation)) {
126                     return@add if (isTablet()) {
127                         secondaryVisibleRegion.right <= primaryVisibleRegion.left
128                     } else {
129                         primaryVisibleRegion.right <= secondaryVisibleRegion.left
130                     }
131                 } else {
132                     return@add if (isTablet()) {
133                         primaryVisibleRegion.bottom <= secondaryVisibleRegion.top
134                     } else {
135                         primaryVisibleRegion.bottom <= secondaryVisibleRegion.top
136                     }
137                 }
138             }
139             .waitForAndVerify()
140     }
141 
142     private fun isLandscape(rotation: Rotation): Boolean {
143         val displayBounds = WindowUtils.getDisplayBounds(rotation)
144         return displayBounds.width() > displayBounds.height()
145     }
146 
147     private fun isTablet(): Boolean {
148         val sizeDp: Point = device.displaySizeDp
149         val LARGE_SCREEN_DP_THRESHOLD = 600
150         return sizeDp.x >= LARGE_SCREEN_DP_THRESHOLD && sizeDp.y >= LARGE_SCREEN_DP_THRESHOLD
151     }
152 }
153