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