1 /*
2  * Copyright (C) 2022 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.server.wm.flicker.ime
18 
19 import android.platform.test.annotations.Presubmit
20 import android.tools.common.traces.ConditionsFactory
21 import android.tools.common.traces.component.ComponentNameMatcher
22 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
23 import android.tools.device.flicker.legacy.FlickerBuilder
24 import android.tools.device.flicker.legacy.LegacyFlickerTest
25 import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
26 import android.tools.device.traces.parsers.WindowManagerStateHelper
27 import androidx.test.filters.RequiresDevice
28 import com.android.server.wm.flicker.BaseTest
29 import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
30 import com.android.server.wm.flicker.navBarLayerIsVisibleAtStartAndEnd
31 import com.android.server.wm.flicker.statusBarLayerIsVisibleAtStartAndEnd
32 import org.junit.Assume
33 import org.junit.FixMethodOrder
34 import org.junit.Ignore
35 import org.junit.Test
36 import org.junit.runner.RunWith
37 import org.junit.runners.MethodSorters
38 import org.junit.runners.Parameterized
39 
40 /**
41  * Test IME window layer will be associated with the app task when going to the overview screen. To
42  * run this test: `atest FlickerTests:OpenImeWindowToOverViewTest`
43  */
44 @RequiresDevice
45 @RunWith(Parameterized::class)
46 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
47 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
48 open class ShowImeWhileEnteringOverviewTest(flicker: LegacyFlickerTest) : BaseTest(flicker) {
49     private val imeTestApp =
50         ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation)
51 
52     /** {@inheritDoc} */
53     override val transition: FlickerBuilder.() -> Unit = {
54         setup { imeTestApp.launchViaIntent(wmHelper) }
55         transitions {
56             device.pressRecentApps()
57             val builder = wmHelper.StateSyncBuilder().withRecentsActivityVisible()
58             waitNavStatusBarVisibility(builder)
59             builder.waitForAndVerify()
60         }
61         teardown {
62             device.pressHome()
63             wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
64             imeTestApp.exit(wmHelper)
65         }
66     }
67 
68     /**
69      * The bars (including [ComponentNameMatcher.STATUS_BAR] and [ComponentNameMatcher.NAV_BAR]) are
70      * expected to be hidden while entering overview in landscape if launcher is set to portrait
71      * only. Because "showing portrait overview (launcher) in landscape display" is an intermediate
72      * state depending on the touch-up to decide the intention of gesture, the display may keep in
73      * landscape if return to app, or change to portrait if the gesture is to swipe-to-home.
74      *
75      * So instead of showing landscape bars with portrait launcher at the same time (especially
76      * return-to-home that launcher workspace becomes visible), hide the bars until leave overview
77      * to have cleaner appearance.
78      *
79      * b/227189877
80      */
81     private fun waitNavStatusBarVisibility(stateSync: WindowManagerStateHelper.StateSyncBuilder) {
82         when {
83             flicker.scenario.isLandscapeOrSeascapeAtStart && !flicker.scenario.isTablet ->
84                 stateSync.add(ConditionsFactory.isStatusBarVisible().negate())
85             else -> stateSync.withNavOrTaskBarVisible().withStatusBarVisible()
86         }
87     }
88 
89     @Presubmit
90     @Test
91     fun imeWindowIsAlwaysVisible() {
92         flicker.imeWindowIsAlwaysVisible()
93     }
94 
95     @Presubmit
96     @Test
97     fun navBarLayerIsVisibleAtStartAndEnd3Button() {
98         Assume.assumeFalse(flicker.scenario.isTablet)
99         Assume.assumeFalse(flicker.scenario.isGesturalNavigation)
100         flicker.navBarLayerIsVisibleAtStartAndEnd()
101     }
102 
103     /**
104      * In the legacy transitions, the nav bar is not marked as invisible. In the new transitions
105      * this is fixed and the nav bar shows as invisible
106      */
107     @Presubmit
108     @Test
109     fun navBarLayerIsInvisibleInLandscapeGestural() {
110         Assume.assumeFalse(flicker.scenario.isTablet)
111         Assume.assumeTrue(flicker.scenario.isLandscapeOrSeascapeAtStart)
112         Assume.assumeTrue(flicker.scenario.isGesturalNavigation)
113         flicker.assertLayersStart { this.isVisible(ComponentNameMatcher.NAV_BAR) }
114         flicker.assertLayersEnd { this.isInvisible(ComponentNameMatcher.NAV_BAR) }
115     }
116 
117     /**
118      * In the legacy transitions, the nav bar is not marked as invisible. In the new transitions
119      * this is fixed and the nav bar shows as invisible
120      */
121     @Presubmit
122     @Test
123     fun statusBarLayerIsInvisibleInLandscapePhone() {
124         Assume.assumeTrue(flicker.scenario.isLandscapeOrSeascapeAtStart)
125         Assume.assumeTrue(flicker.scenario.isGesturalNavigation)
126         Assume.assumeFalse(flicker.scenario.isTablet)
127         flicker.assertLayersStart { this.isVisible(ComponentNameMatcher.STATUS_BAR) }
128         flicker.assertLayersEnd { this.isInvisible(ComponentNameMatcher.STATUS_BAR) }
129     }
130 
131     /**
132      * In the legacy transitions, the nav bar is not marked as invisible. In the new transitions
133      * this is fixed and the nav bar shows as invisible
134      */
135     @Presubmit
136     @Test
137     fun statusBarLayerIsInvisibleInLandscapeTablet() {
138         Assume.assumeTrue(flicker.scenario.isLandscapeOrSeascapeAtStart)
139         Assume.assumeTrue(flicker.scenario.isGesturalNavigation)
140         Assume.assumeTrue(flicker.scenario.isTablet)
141         flicker.statusBarLayerIsVisibleAtStartAndEnd()
142     }
143 
144     /** {@inheritDoc} */
145     @Test
146     @Ignore("Visibility changes depending on orientation and navigation mode")
147     override fun navBarLayerIsVisibleAtStartAndEnd() {}
148 
149     /** {@inheritDoc} */
150     @Test
151     @Ignore("Visibility changes depending on orientation and navigation mode")
152     override fun navBarLayerPositionAtStartAndEnd() {}
153 
154     /** {@inheritDoc} */
155     @Test
156     @Ignore("Visibility changes depending on orientation and navigation mode")
157     override fun statusBarLayerPositionAtStartAndEnd() {}
158 
159     /** {@inheritDoc} */
160     @Test
161     @Ignore("Visibility changes depending on orientation and navigation mode")
162     override fun statusBarLayerIsVisibleAtStartAndEnd() {}
163 
164     @Presubmit
165     @Test
166     override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
167 
168     @Presubmit
169     @Test
170     fun statusBarLayerIsVisibleInPortrait() {
171         Assume.assumeFalse(flicker.scenario.isLandscapeOrSeascapeAtStart)
172         flicker.statusBarLayerIsVisibleAtStartAndEnd()
173     }
174 
175     @Presubmit
176     @Test
177     fun statusBarLayerIsInvisibleInLandscape() {
178         Assume.assumeTrue(flicker.scenario.isLandscapeOrSeascapeAtStart)
179         Assume.assumeFalse(flicker.scenario.isTablet)
180         flicker.assertLayersStart { this.isVisible(ComponentNameMatcher.STATUS_BAR) }
181         flicker.assertLayersEnd { this.isInvisible(ComponentNameMatcher.STATUS_BAR) }
182     }
183 
184     @Presubmit
185     @Test
186     fun imeLayerIsVisibleAndAssociatedWithAppWidow() {
187         flicker.assertLayersStart {
188             isVisible(ComponentNameMatcher.IME)
189                 .visibleRegion(ComponentNameMatcher.IME)
190                 .coversAtMost(isVisible(imeTestApp).visibleRegion(imeTestApp).region)
191         }
192         flicker.assertLayers {
193             this.invoke("imeLayerIsVisibleAndAlignAppWidow") {
194                 val imeVisibleRegion = it.visibleRegion(ComponentNameMatcher.IME)
195                 val appVisibleRegion = it.visibleRegion(imeTestApp)
196                 if (imeVisibleRegion.region.isNotEmpty) {
197                     it.isVisible(ComponentNameMatcher.IME)
198                     imeVisibleRegion.coversAtMost(appVisibleRegion.region)
199                 }
200             }
201         }
202     }
203 
204     companion object {
205         /**
206          * Creates the test configurations.
207          *
208          * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
209          * navigation modes.
210          */
211         @Parameterized.Parameters(name = "{0}")
212         @JvmStatic
213         fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
214     }
215 }
216