1 /* 2 * Copyright (C) 2023 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 android.tools.device.apphelpers 18 19 import android.app.ActivityManager 20 import android.app.Instrumentation 21 import android.content.ComponentName 22 import android.content.Context 23 import android.content.Intent 24 import android.content.pm.PackageManager 25 import android.tools.PlatformConsts 26 import android.tools.traces.component.ComponentNameMatcher 27 import android.tools.traces.component.IComponentMatcher 28 import android.tools.traces.component.IComponentNameMatcher 29 import android.tools.traces.parsers.WindowManagerStateHelper 30 import android.tools.withTracing 31 import androidx.test.uiautomator.By 32 import androidx.test.uiautomator.BySelector 33 import androidx.test.uiautomator.UiDevice 34 import androidx.test.uiautomator.Until 35 import com.android.launcher3.tapl.LauncherInstrumentation 36 37 /** 38 * Class to take advantage of {@code IAppHelper} interface so the same test can be run against first 39 * party and third party apps. 40 */ 41 open class StandardAppHelper( 42 val instrumentation: Instrumentation, 43 val appName: String, 44 val componentMatcher: ComponentNameMatcher, 45 ) : IStandardAppHelper, IComponentNameMatcher by componentMatcher { 46 constructor( 47 instr: Instrumentation, 48 appName: String, 49 packageName: String, 50 activity: String, 51 ) : this(instr, appName, ComponentNameMatcher(packageName, ".$activity")) 52 53 protected val pkgManager: PackageManager = instrumentation.context.packageManager 54 <lambda>null55 protected val tapl: LauncherInstrumentation by lazy { LauncherInstrumentation() } 56 57 private val activityManager: ActivityManager? 58 get() = instrumentation.context.getSystemService(ActivityManager::class.java) 59 60 protected val context: Context 61 get() = instrumentation.context 62 63 override val packageName = componentMatcher.packageName 64 65 val packageNameMatcher = ComponentNameMatcher(componentMatcher.packageName, "") 66 67 override val className = componentMatcher.className 68 69 protected val uiDevice: UiDevice = UiDevice.getInstance(instrumentation) 70 getAppSelectornull71 private fun getAppSelector(expectedPackageName: String): BySelector { 72 val expected = expectedPackageName.ifEmpty { packageName } 73 return By.pkg(expected).depth(0) 74 } 75 opennull76 override fun open() { 77 open(packageName) 78 } 79 opennull80 protected fun open(expectedPackageName: String) { 81 tapl.goHome().switchToAllApps().getAppIcon(appName).launch(expectedPackageName) 82 } 83 84 /** {@inheritDoc} */ 85 open val openAppIntent: Intent 86 get() { 87 val intent = Intent() 88 intent.addCategory(Intent.CATEGORY_LAUNCHER) 89 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 90 intent.component = ComponentName(packageName, className) 91 return intent 92 } 93 94 /** {@inheritDoc} */ exitnull95 override fun exit() { 96 withTracing("${this::class.simpleName}#exit") { 97 // Ensure all testing components end up being closed. 98 activityManager?.forceStopPackage(packageName) 99 } 100 } 101 102 /** {@inheritDoc} */ exitnull103 override fun exit(wmHelper: WindowManagerStateHelper) { 104 withTracing("${this::class.simpleName}#exitAndWait") { 105 exit() 106 waitForActivityDestroyed(wmHelper) 107 } 108 } 109 110 /** Waits the activity until state change to {link WindowManagerState.STATE_DESTROYED} */ waitForActivityDestroyednull111 private fun waitForActivityDestroyed(wmHelper: WindowManagerStateHelper) { 112 val waitMsg = 113 "state of ${componentMatcher.toActivityIdentifier()} to be " + 114 PlatformConsts.STATE_DESTROYED 115 wmHelper 116 .StateSyncBuilder() 117 .add(waitMsg) { 118 !it.wmState.containsActivity(componentMatcher) || 119 it.wmState.hasActivityState(componentMatcher, PlatformConsts.STATE_DESTROYED) 120 } 121 .withAppTransitionIdle() 122 .waitForAndVerify() 123 } 124 launchAppViaIntentnull125 private fun launchAppViaIntent( 126 action: String? = null, 127 stringExtras: Map<String, String> = mapOf() 128 ) { 129 withTracing("${this::class.simpleName}#launchAppViaIntent") { 130 val intent = openAppIntent 131 intent.action = action ?: Intent.ACTION_MAIN 132 stringExtras.forEach { intent.putExtra(it.key, it.value) } 133 context.startActivity(intent) 134 } 135 } 136 137 /** {@inheritDoc} */ launchViaIntentnull138 override fun launchViaIntent( 139 expectedPackageName: String, 140 action: String?, 141 stringExtras: Map<String, String> 142 ) { 143 launchAppViaIntent(action, stringExtras) 144 val appSelector = getAppSelector(expectedPackageName) 145 uiDevice.wait(Until.hasObject(appSelector), APP_LAUNCH_WAIT_TIME_MS) 146 } 147 148 /** {@inheritDoc} */ launchViaIntentnull149 override fun launchViaIntent( 150 wmHelper: WindowManagerStateHelper, 151 launchedAppComponentMatcherOverride: IComponentMatcher?, 152 action: String?, 153 stringExtras: Map<String, String>, 154 waitConditionsBuilder: WindowManagerStateHelper.StateSyncBuilder 155 ) { 156 launchAppViaIntent(action, stringExtras) 157 doWaitShown(launchedAppComponentMatcherOverride, waitConditionsBuilder) 158 } 159 160 /** {@inheritDoc} */ launchViaIntentnull161 override fun launchViaIntent( 162 wmHelper: WindowManagerStateHelper, 163 intent: Intent, 164 launchedAppComponentMatcherOverride: IComponentMatcher?, 165 waitConditionsBuilder: WindowManagerStateHelper.StateSyncBuilder 166 ) { 167 withTracing("${this::class.simpleName}#launchViaIntent") { 168 context.startActivity(intent) 169 doWaitShown(launchedAppComponentMatcherOverride, waitConditionsBuilder) 170 } 171 } 172 doWaitShownnull173 private fun doWaitShown( 174 launchedAppComponentMatcherOverride: IComponentMatcher? = null, 175 waitConditionsBuilder: WindowManagerStateHelper.StateSyncBuilder 176 ) { 177 withTracing("${this::class.simpleName}#doWaitShown") { 178 val expectedWindow = launchedAppComponentMatcherOverride ?: componentMatcher 179 val builder = waitConditionsBuilder.withWindowSurfaceAppeared(expectedWindow) 180 builder.waitForAndVerify() 181 } 182 } 183 isAvailablenull184 override fun isAvailable(): Boolean { 185 return try { 186 pkgManager.getPackageInfo(packageName, PackageManager.PackageInfoFlags.of(0)) 187 true 188 } catch (e: PackageManager.NameNotFoundException) { 189 false 190 } 191 } 192 toStringnull193 override fun toString(): String = componentMatcher.toString() 194 195 companion object { 196 private const val APP_LAUNCH_WAIT_TIME_MS = 10000L 197 } 198 } 199