1 /*
2  * Copyright (C) 2021 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.pip
18 
19 import android.app.Instrumentation
20 import android.content.Intent
21 import android.platform.test.annotations.Presubmit
22 import android.view.Surface
23 import androidx.test.platform.app.InstrumentationRegistry
24 import com.android.server.wm.flicker.FlickerBuilderProvider
25 import com.android.server.wm.flicker.FlickerTestParameter
26 import com.android.server.wm.flicker.dsl.FlickerBuilder
27 import com.android.server.wm.flicker.entireScreenCovered
28 import com.android.server.wm.flicker.helpers.WindowUtils
29 import com.android.server.wm.flicker.helpers.isRotated
30 import com.android.server.wm.flicker.helpers.setRotation
31 import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
32 import com.android.server.wm.flicker.navBarLayerIsVisible
33 import com.android.server.wm.flicker.navBarLayerRotatesAndScales
34 import com.android.server.wm.flicker.navBarWindowIsVisible
35 import com.android.server.wm.flicker.repetitions
36 import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
37 import com.android.server.wm.flicker.startRotation
38 import com.android.server.wm.flicker.statusBarLayerIsVisible
39 import com.android.server.wm.flicker.statusBarLayerRotatesScales
40 import com.android.server.wm.flicker.statusBarWindowIsVisible
41 import com.android.wm.shell.flicker.helpers.PipAppHelper
42 import com.android.wm.shell.flicker.testapp.Components
43 import org.junit.Test
44 
45 abstract class PipTransition(protected val testSpec: FlickerTestParameter) {
46     protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
47     protected val isRotated = testSpec.config.startRotation.isRotated()
48     protected val pipApp = PipAppHelper(instrumentation)
49     protected val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation)
50     protected val broadcastActionTrigger = BroadcastActionTrigger(instrumentation)
51     protected abstract val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
52     // Helper class to process test actions by broadcast.
53     protected class BroadcastActionTrigger(private val instrumentation: Instrumentation) {
54         private fun createIntentWithAction(broadcastAction: String): Intent {
55             return Intent(broadcastAction).setFlags(Intent.FLAG_RECEIVER_FOREGROUND)
56         }
57 
58         fun doAction(broadcastAction: String) {
59             instrumentation.context
60                 .sendBroadcast(createIntentWithAction(broadcastAction))
61         }
62 
63         fun requestOrientationForPip(orientation: Int) {
64             instrumentation.context.sendBroadcast(
65                     createIntentWithAction(Components.PipActivity.ACTION_SET_REQUESTED_ORIENTATION)
66                     .putExtra(Components.PipActivity.EXTRA_PIP_ORIENTATION, orientation.toString())
67             )
68         }
69 
70         companion object {
71             // Corresponds to ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
72             @JvmStatic
73             val ORIENTATION_LANDSCAPE = 0
74 
75             // Corresponds to ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
76             @JvmStatic
77             val ORIENTATION_PORTRAIT = 1
78         }
79     }
80 
81     @FlickerBuilderProvider
82     fun buildFlicker(): FlickerBuilder {
83         return FlickerBuilder(instrumentation).apply {
84             withTestName { testSpec.name }
85             repeat { testSpec.config.repetitions }
86             transition(this, testSpec.config)
87         }
88     }
89 
90     /**
91      * Gets a configuration that handles basic setup and teardown of pip tests
92      */
93     protected val setupAndTeardown: FlickerBuilder.(Map<String, Any?>) -> Unit
94         get() = {
95             setup {
96                 test {
97                     removeAllTasksButHome()
98                     device.wakeUpAndGoToHomeScreen()
99                 }
100             }
101             teardown {
102                 eachRun {
103                     setRotation(Surface.ROTATION_0)
104                 }
105                 test {
106                     removeAllTasksButHome()
107                     pipApp.exit(wmHelper)
108                 }
109             }
110         }
111 
112     /**
113      * Gets a configuration that handles basic setup and teardown of pip tests and that
114      * launches the Pip app for test
115      *
116      * @param eachRun If the pip app should be launched in each run (otherwise only 1x per test)
117      * @param stringExtras Arguments to pass to the PIP launch intent
118      * @param extraSpec Addicional segment of flicker specification
119      */
120     @JvmOverloads
121     protected open fun buildTransition(
122         eachRun: Boolean,
123         stringExtras: Map<String, String> = mapOf(Components.PipActivity.EXTRA_ENTER_PIP to "true"),
124         extraSpec: FlickerBuilder.(Map<String, Any?>) -> Unit = {}
125     ): FlickerBuilder.(Map<String, Any?>) -> Unit {
126         return { configuration ->
127             setupAndTeardown(this, configuration)
128 
129             setup {
130                 test {
131                     removeAllTasksButHome()
132                     if (!eachRun) {
133                         pipApp.launchViaIntent(wmHelper, stringExtras = stringExtras)
134                         wmHelper.waitFor { it.wmState.hasPipWindow() }
135                     }
136                 }
137                 eachRun {
138                     if (eachRun) {
139                         pipApp.launchViaIntent(wmHelper, stringExtras = stringExtras)
140                         wmHelper.waitFor { it.wmState.hasPipWindow() }
141                     }
142                 }
143             }
144             teardown {
145                 eachRun {
146                     if (eachRun) {
147                         pipApp.exit(wmHelper)
148                     }
149                 }
150                 test {
151                     if (!eachRun) {
152                         pipApp.exit(wmHelper)
153                     }
154                     removeAllTasksButHome()
155                 }
156             }
157 
158             extraSpec(this, configuration)
159         }
160     }
161 
162     @Presubmit
163     @Test
164     open fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible()
165 
166     @Presubmit
167     @Test
168     open fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible()
169 
170     @Presubmit
171     @Test
172     open fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible()
173 
174     @Presubmit
175     @Test
176     open fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible()
177 
178     @Presubmit
179     @Test
180     open fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales()
181 
182     @Presubmit
183     @Test
184     open fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales()
185 
186     @Presubmit
187     @Test
188     open fun entireScreenCovered() = testSpec.entireScreenCovered()
189 }