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