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.helpers
18 
19 import android.app.Instrumentation
20 import android.tools.PlatformConsts
21 import android.tools.device.apphelpers.StandardAppHelper
22 import android.tools.helpers.FIND_TIMEOUT
23 import android.tools.traces.component.ComponentNameMatcher
24 import android.tools.traces.parsers.WindowManagerStateHelper
25 import android.tools.traces.parsers.toFlickerComponent
26 import android.util.Log
27 import androidx.test.uiautomator.By
28 import androidx.test.uiautomator.Until
29 import androidx.window.extensions.WindowExtensions
30 import androidx.window.extensions.WindowExtensionsProvider
31 import androidx.window.extensions.embedding.ActivityEmbeddingComponent
32 import com.android.server.wm.flicker.testapp.ActivityOptions
33 import org.junit.Assume.assumeNotNull
34 
35 class ActivityEmbeddingAppHelper
36 @JvmOverloads
37 constructor(
38     instr: Instrumentation,
39     launcherName: String = ActivityOptions.ActivityEmbedding.MainActivity.LABEL,
40     component: ComponentNameMatcher = MAIN_ACTIVITY_COMPONENT
41 ) : StandardAppHelper(instr, launcherName, component) {
42 
43     /**
44      * Clicks the button to launch the secondary activity, which should split with the main activity
45      * based on the split pair rule.
46      */
launchSecondaryActivitynull47     fun launchSecondaryActivity(wmHelper: WindowManagerStateHelper) {
48         launchSecondaryActivityFromButton(wmHelper, "launch_secondary_activity_button")
49     }
50 
51     /**
52      * Clicks the button to launch the secondary activity in RTL, which should split with the main
53      * activity based on the split pair rule.
54      */
launchSecondaryActivityRTLnull55     fun launchSecondaryActivityRTL(wmHelper: WindowManagerStateHelper) {
56         launchSecondaryActivityFromButton(wmHelper, "launch_secondary_activity_rtl_button")
57     }
58 
59     /** Clicks the button to launch the secondary activity in a horizontal split. */
launchSecondaryActivityHorizontallynull60     fun launchSecondaryActivityHorizontally(wmHelper: WindowManagerStateHelper) {
61         launchSecondaryActivityFromButton(wmHelper, "launch_secondary_activity_horizontally_button")
62     }
63 
64     /** Clicks the button to launch a third activity over a secondary activity. */
launchThirdActivitynull65     fun launchThirdActivity(wmHelper: WindowManagerStateHelper) {
66         val launchButton =
67             uiDevice.wait(
68                 Until.findObject(By.res(packageName, "launch_third_activity_button")),
69                 FIND_TIMEOUT
70             )
71         require(launchButton != null) { "Can't find launch third activity button on screen." }
72         launchButton.click()
73         wmHelper
74             .StateSyncBuilder()
75             .withActivityState(MAIN_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED)
76             .withActivityState(SECONDARY_ACTIVITY_COMPONENT, PlatformConsts.STATE_STOPPED)
77             .withActivityState(THIRD_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED)
78             .waitForAndVerify()
79     }
80 
81     /**
82      * Clicks the button to launch the trampoline activity, which should launch the secondary
83      * activity and finish itself.
84      */
launchTrampolineActivitynull85     fun launchTrampolineActivity(wmHelper: WindowManagerStateHelper) {
86         val launchButton =
87             uiDevice.wait(
88                 Until.findObject(By.res(packageName, "launch_trampoline_button")),
89                 FIND_TIMEOUT
90             )
91         require(launchButton != null) { "Can't find launch trampoline activity button on screen." }
92         launchButton.click()
93         wmHelper
94             .StateSyncBuilder()
95             .withActivityState(SECONDARY_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED)
96             .withActivityRemoved(TRAMPOLINE_ACTIVITY_COMPONENT)
97             .waitForAndVerify()
98     }
99 
100     /**
101      * Clicks the button to finishes the secondary activity launched through
102      * [launchSecondaryActivity], waits for the main activity to resume.
103      */
finishSecondaryActivitynull104     fun finishSecondaryActivity(wmHelper: WindowManagerStateHelper) {
105         val finishButton =
106             uiDevice.wait(
107                 Until.findObject(By.res(packageName, "finish_secondary_activity_button")),
108                 FIND_TIMEOUT
109             )
110         require(finishButton != null) { "Can't find finish secondary activity button on screen." }
111         finishButton.click()
112         wmHelper
113             .StateSyncBuilder()
114             .withActivityRemoved(SECONDARY_ACTIVITY_COMPONENT)
115             .waitForAndVerify()
116     }
117 
118     /** Clicks the button to toggle the split ratio of secondary activity. */
changeSecondaryActivityRationull119     fun changeSecondaryActivityRatio(wmHelper: WindowManagerStateHelper) {
120         val launchButton =
121             uiDevice.wait(
122                 Until.findObject(By.res(packageName, "toggle_split_ratio_button")),
123                 FIND_TIMEOUT
124             )
125         require(launchButton != null) {
126             "Can't find toggle ratio for secondary activity button on screen."
127         }
128         launchButton.click()
129         wmHelper
130             .StateSyncBuilder()
131             .withAppTransitionIdle()
132             .withTransitionSnapshotGone()
133             .waitForAndVerify()
134     }
135 
secondaryActivityEnterPipnull136     fun secondaryActivityEnterPip(wmHelper: WindowManagerStateHelper) {
137         val pipButton =
138             uiDevice.wait(
139                 Until.findObject(By.res(packageName, "secondary_enter_pip_button")),
140                 FIND_TIMEOUT
141             )
142         require(pipButton != null) { "Can't find enter pip button on screen." }
143         pipButton.click()
144         wmHelper.StateSyncBuilder().withAppTransitionIdle().withPipShown().waitForAndVerify()
145     }
146 
147     /**
148      * Clicks the button to launch a secondary activity with alwaysExpand enabled, which will launch
149      * a fullscreen window on top of the visible region.
150      */
launchAlwaysExpandActivitynull151     fun launchAlwaysExpandActivity(wmHelper: WindowManagerStateHelper) {
152         val launchButton =
153             uiDevice.wait(
154                 Until.findObject(By.res(packageName, "launch_always_expand_activity_button")),
155                 FIND_TIMEOUT
156             )
157         require(launchButton != null) {
158             "Can't find launch always expand activity button on screen."
159         }
160         launchButton.click()
161         wmHelper
162             .StateSyncBuilder()
163             .withActivityState(ALWAYS_EXPAND_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED)
164             .withActivityState(
165                 MAIN_ACTIVITY_COMPONENT,
166                 PlatformConsts.STATE_PAUSED,
167                 PlatformConsts.STATE_STOPPED
168             )
169             .waitForAndVerify()
170     }
171 
launchSecondaryActivityFromButtonnull172     private fun launchSecondaryActivityFromButton(
173         wmHelper: WindowManagerStateHelper,
174         buttonName: String
175     ) {
176         val launchButton =
177             uiDevice.wait(Until.findObject(By.res(packageName, buttonName)), FIND_TIMEOUT)
178         require(launchButton != null) {
179             "Can't find launch secondary activity button : " + buttonName + "on screen."
180         }
181         launchButton.click()
182         wmHelper
183             .StateSyncBuilder()
184             .withActivityState(SECONDARY_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED)
185             .withActivityState(MAIN_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED)
186             .waitForAndVerify()
187     }
188 
189     /**
190      * Clicks the button to launch the placeholder primary activity, which should launch the
191      * placeholder secondary activity based on the placeholder rule.
192      */
launchPlaceholderSplitnull193     fun launchPlaceholderSplit(wmHelper: WindowManagerStateHelper) {
194         val launchButton =
195             uiDevice.wait(
196                 Until.findObject(By.res(packageName, "launch_placeholder_split_button")),
197                 FIND_TIMEOUT
198             )
199         require(launchButton != null) { "Can't find launch placeholder split button on screen." }
200         launchButton.click()
201         wmHelper
202             .StateSyncBuilder()
203             .withActivityState(PLACEHOLDER_PRIMARY_COMPONENT, PlatformConsts.STATE_RESUMED)
204             .withActivityState(PLACEHOLDER_SECONDARY_COMPONENT, PlatformConsts.STATE_RESUMED)
205             .waitForAndVerify()
206     }
207 
208     /**
209      * Clicks the button to launch the placeholder primary activity in RTL, which should launch the
210      * placeholder secondary activity based on the placeholder rule.
211      */
launchPlaceholderSplitRTLnull212     fun launchPlaceholderSplitRTL(wmHelper: WindowManagerStateHelper) {
213         val launchButton =
214             uiDevice.wait(
215                 Until.findObject(By.res(packageName, "launch_placeholder_split_rtl_button")),
216                 FIND_TIMEOUT
217             )
218         require(launchButton != null) { "Can't find launch placeholder split button on screen." }
219         launchButton.click()
220         wmHelper
221             .StateSyncBuilder()
222             .withActivityState(PLACEHOLDER_PRIMARY_COMPONENT, PlatformConsts.STATE_RESUMED)
223             .withActivityState(PLACEHOLDER_SECONDARY_COMPONENT, PlatformConsts.STATE_RESUMED)
224             .waitForAndVerify()
225     }
226 
227     companion object {
228         private const val TAG = "ActivityEmbeddingAppHelper"
229 
230         val MAIN_ACTIVITY_COMPONENT =
231             ActivityOptions.ActivityEmbedding.MainActivity.COMPONENT.toFlickerComponent()
232 
233         val SECONDARY_ACTIVITY_COMPONENT =
234             ActivityOptions.ActivityEmbedding.SecondaryActivity.COMPONENT.toFlickerComponent()
235 
236         val THIRD_ACTIVITY_COMPONENT =
237             ActivityOptions.ActivityEmbedding.ThirdActivity.COMPONENT.toFlickerComponent()
238 
239         val ALWAYS_EXPAND_ACTIVITY_COMPONENT =
240             ActivityOptions.ActivityEmbedding.AlwaysExpandActivity.COMPONENT.toFlickerComponent()
241 
242         val PLACEHOLDER_PRIMARY_COMPONENT =
243             ActivityOptions.ActivityEmbedding.PlaceholderPrimaryActivity.COMPONENT
244                 .toFlickerComponent()
245 
246         val PLACEHOLDER_SECONDARY_COMPONENT =
247             ActivityOptions.ActivityEmbedding.PlaceholderSecondaryActivity.COMPONENT
248                 .toFlickerComponent()
249 
250         val TRAMPOLINE_ACTIVITY_COMPONENT =
251             ActivityOptions.ActivityEmbedding.TrampolineActivity.COMPONENT.toFlickerComponent()
252 
253         @JvmStatic
getWindowExtensionsnull254         fun getWindowExtensions(): WindowExtensions? {
255             try {
256                 return WindowExtensionsProvider.getWindowExtensions()
257             } catch (e: NoClassDefFoundError) {
258                 Log.d(TAG, "Extension implementation not found")
259             } catch (e: UnsupportedOperationException) {
260                 Log.d(TAG, "Stub Extension")
261             }
262             return null
263         }
264 
265         @JvmStatic
getActivityEmbeddingComponentnull266         fun getActivityEmbeddingComponent(): ActivityEmbeddingComponent? {
267             return getWindowExtensions()?.activityEmbeddingComponent
268         }
269 
270         @JvmStatic
assumeActivityEmbeddingSupportedDevicenull271         fun assumeActivityEmbeddingSupportedDevice() {
272             assumeNotNull(getActivityEmbeddingComponent())
273         }
274     }
275 }
276