1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 package com.android.launcher3.ui; 17 18 import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; 19 20 import static androidx.test.InstrumentationRegistry.getInstrumentation; 21 22 import static com.android.launcher3.testing.shared.TestProtocol.ICON_MISSING; 23 import static com.android.launcher3.testing.shared.TestProtocol.WIDGET_CONFIG_NULL_EXTRA_INTENT; 24 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; 25 26 import static org.junit.Assert.assertEquals; 27 import static org.junit.Assert.assertTrue; 28 29 import android.content.BroadcastReceiver; 30 import android.content.ComponentName; 31 import android.content.Context; 32 import android.content.Intent; 33 import android.content.IntentFilter; 34 import android.content.pm.ActivityInfo; 35 import android.content.pm.LauncherActivityInfo; 36 import android.content.pm.LauncherApps; 37 import android.content.pm.PackageInfo; 38 import android.content.pm.PackageManager; 39 import android.graphics.Point; 40 import android.os.Debug; 41 import android.os.Process; 42 import android.os.RemoteException; 43 import android.os.UserHandle; 44 import android.os.UserManager; 45 import android.platform.test.flag.junit.SetFlagsRule; 46 import android.system.OsConstants; 47 import android.util.Log; 48 49 import androidx.annotation.NonNull; 50 import androidx.test.InstrumentationRegistry; 51 import androidx.test.uiautomator.By; 52 import androidx.test.uiautomator.BySelector; 53 import androidx.test.uiautomator.UiDevice; 54 import androidx.test.uiautomator.Until; 55 56 import com.android.launcher3.Launcher; 57 import com.android.launcher3.LauncherState; 58 import com.android.launcher3.Utilities; 59 import com.android.launcher3.celllayout.FavoriteItemsTransaction; 60 import com.android.launcher3.tapl.HomeAllApps; 61 import com.android.launcher3.tapl.HomeAppIcon; 62 import com.android.launcher3.tapl.LauncherInstrumentation; 63 import com.android.launcher3.tapl.TestHelpers; 64 import com.android.launcher3.testcomponent.TestCommandReceiver; 65 import com.android.launcher3.util.LooperExecutor; 66 import com.android.launcher3.util.SimpleBroadcastReceiver; 67 import com.android.launcher3.util.TestUtil; 68 import com.android.launcher3.util.Wait; 69 import com.android.launcher3.util.rule.ExtendedLongPressTimeoutRule; 70 import com.android.launcher3.util.rule.FailureWatcher; 71 import com.android.launcher3.util.rule.SamplerRule; 72 import com.android.launcher3.util.rule.ScreenRecordRule; 73 import com.android.launcher3.util.rule.ShellCommandRule; 74 import com.android.launcher3.util.rule.TestIsolationRule; 75 import com.android.launcher3.util.rule.TestStabilityRule; 76 import com.android.launcher3.util.rule.ViewCaptureRule; 77 78 import org.junit.After; 79 import org.junit.Assert; 80 import org.junit.Before; 81 import org.junit.Rule; 82 import org.junit.rules.RuleChain; 83 import org.junit.rules.TestRule; 84 85 import java.io.IOException; 86 import java.util.Objects; 87 import java.util.concurrent.Callable; 88 import java.util.concurrent.CountDownLatch; 89 import java.util.concurrent.TimeUnit; 90 import java.util.concurrent.TimeoutException; 91 import java.util.function.Consumer; 92 import java.util.function.Function; 93 import java.util.function.Supplier; 94 95 /** 96 * Base class for all instrumentation tests providing various utility methods. 97 */ 98 public abstract class AbstractLauncherUiTest<LAUNCHER_TYPE extends Launcher> { 99 100 public static final long DEFAULT_ACTIVITY_TIMEOUT = TimeUnit.SECONDS.toMillis(10); 101 public static final long DEFAULT_BROADCAST_TIMEOUT_SECS = 10; 102 103 public static final long DEFAULT_UI_TIMEOUT = TestUtil.DEFAULT_UI_TIMEOUT; 104 private static final String TAG = "AbstractLauncherUiTest"; 105 106 private static boolean sDumpWasGenerated = false; 107 private static boolean sActivityLeakReported = false; 108 private static boolean sSeenKeyguard = false; 109 private static boolean sFirstTimeWaitingForWizard = true; 110 111 private static final String SYSTEMUI_PACKAGE = "com.android.systemui"; 112 113 protected LooperExecutor mMainThreadExecutor = MAIN_EXECUTOR; 114 protected final UiDevice mDevice = getUiDevice(); 115 protected final LauncherInstrumentation mLauncher = createLauncherInstrumentation(); 116 117 @NonNull createLauncherInstrumentation()118 public static LauncherInstrumentation createLauncherInstrumentation() { 119 waitForSetupWizardDismissal(); // precondition for creating LauncherInstrumentation 120 return new LauncherInstrumentation(true); 121 } 122 123 protected Context mTargetContext; 124 protected String mTargetPackage; 125 private int mLauncherPid; 126 127 /** Detects activity leaks and throws an exception if a leak is found. */ checkDetectedLeaks(LauncherInstrumentation launcher)128 public static void checkDetectedLeaks(LauncherInstrumentation launcher) { 129 checkDetectedLeaks(launcher, false); 130 } 131 132 /** Detects activity leaks and throws an exception if a leak is found. */ checkDetectedLeaks(LauncherInstrumentation launcher, boolean requireOneActiveActivityUnused)133 public static void checkDetectedLeaks(LauncherInstrumentation launcher, 134 boolean requireOneActiveActivityUnused) { 135 if (TestStabilityRule.isPresubmit()) return; // b/313501215 136 137 final boolean requireOneActiveActivity = 138 false; // workaround for leaks when there is an unexpected Recents activity 139 140 if (sActivityLeakReported) return; 141 142 // Check whether activity leak detector has found leaked activities. 143 Wait.atMost(() -> getActivityLeakErrorMessage(launcher, requireOneActiveActivity), 144 () -> { 145 launcher.forceGc(); 146 return MAIN_EXECUTOR.submit( 147 () -> launcher.noLeakedActivities(requireOneActiveActivity)).get(); 148 }, DEFAULT_UI_TIMEOUT, launcher); 149 } 150 getAppPackageName()151 public static String getAppPackageName() { 152 return getInstrumentation().getContext().getPackageName(); 153 } 154 getActivityLeakErrorMessage(LauncherInstrumentation launcher, boolean requireOneActiveActivity)155 private static String getActivityLeakErrorMessage(LauncherInstrumentation launcher, 156 boolean requireOneActiveActivity) { 157 sActivityLeakReported = true; 158 return "Activity leak detector has found leaked activities, requirining 1 activity: " 159 + requireOneActiveActivity + "; " 160 + dumpHprofData(launcher, false, requireOneActiveActivity) + "."; 161 } 162 dumpHprofData(LauncherInstrumentation launcher, boolean intentionalLeak, boolean requireOneActiveActivity)163 private static String dumpHprofData(LauncherInstrumentation launcher, boolean intentionalLeak, 164 boolean requireOneActiveActivity) { 165 if (intentionalLeak) return "intentional leak; not generating dump"; 166 167 String result; 168 if (sDumpWasGenerated) { 169 result = "dump has already been generated by another test"; 170 } else { 171 try { 172 final String fileName = 173 getInstrumentation().getTargetContext().getFilesDir().getPath() 174 + "/ActivityLeakHeapDump.hprof"; 175 if (TestHelpers.isInLauncherProcess()) { 176 Debug.dumpHprofData(fileName); 177 } else { 178 final UiDevice device = getUiDevice(); 179 device.executeShellCommand( 180 "am dumpheap " + device.getLauncherPackageName() + " " + fileName); 181 } 182 Log.d(TAG, "Saved leak dump, the leak is still present: " 183 + !launcher.noLeakedActivities(requireOneActiveActivity)); 184 sDumpWasGenerated = true; 185 result = "saved memory dump as an artifact"; 186 } catch (Throwable e) { 187 Log.e(TAG, "dumpHprofData failed", e); 188 result = "failed to save memory dump"; 189 } 190 } 191 return result + ". Full list of activities: " + launcher.getRootedActivitiesList(); 192 } 193 AbstractLauncherUiTest()194 protected AbstractLauncherUiTest() { 195 mLauncher.enableCheckEventsForSuccessfulGestures(); 196 mLauncher.setAnomalyChecker(AbstractLauncherUiTest::verifyKeyguardInvisible); 197 try { 198 mDevice.setOrientationNatural(); 199 } catch (RemoteException e) { 200 throw new RuntimeException(e); 201 } 202 if (TestHelpers.isInLauncherProcess()) { 203 Utilities.enableRunningInTestHarnessForTests(); 204 mLauncher.setSystemHealthSupplier(startTime -> TestCommandReceiver.callCommand( 205 TestCommandReceiver.GET_SYSTEM_HEALTH_MESSAGE, startTime.toString()) 206 .getString("result")); 207 } 208 mLauncher.enableDebugTracing(); 209 // Avoid double-reporting of Launcher crashes. 210 mLauncher.setOnLauncherCrashed(() -> mLauncherPid = 0); 211 } 212 213 @Rule 214 public ShellCommandRule mDisableHeadsUpNotification = 215 ShellCommandRule.disableHeadsUpNotification(); 216 217 @Rule 218 public ScreenRecordRule mScreenRecordRule = new ScreenRecordRule(); 219 220 @Rule 221 public SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT); 222 223 @Rule 224 public ExtendedLongPressTimeoutRule mLongPressTimeoutRule = new ExtendedLongPressTimeoutRule(); 225 initialize(AbstractLauncherUiTest test)226 public static void initialize(AbstractLauncherUiTest test) throws Exception { 227 test.reinitializeLauncherData(); 228 test.mDevice.pressHome(); 229 test.waitForLauncherCondition("Launcher didn't start", Objects::nonNull); 230 test.waitForState("Launcher internal state didn't switch to Home", 231 () -> LauncherState.NORMAL); 232 test.waitForResumed("Launcher internal state is still Background"); 233 // Check that we switched to home. 234 test.mLauncher.getWorkspace(); 235 AbstractLauncherUiTest.checkDetectedLeaks(test.mLauncher, true); 236 } 237 clearPackageData(String pkg)238 protected void clearPackageData(String pkg) throws IOException, InterruptedException { 239 final CountDownLatch count = new CountDownLatch(2); 240 final SimpleBroadcastReceiver broadcastReceiver = 241 new SimpleBroadcastReceiver(i -> count.countDown()); 242 broadcastReceiver.registerPkgActions(mTargetContext, pkg, 243 Intent.ACTION_PACKAGE_RESTARTED, Intent.ACTION_PACKAGE_DATA_CLEARED); 244 245 mDevice.executeShellCommand("pm clear " + pkg); 246 assertTrue(pkg + " didn't restart", count.await(10, TimeUnit.SECONDS)); 247 mTargetContext.unregisterReceiver(broadcastReceiver); 248 } 249 getRulesInsideActivityMonitor()250 protected TestRule getRulesInsideActivityMonitor() { 251 final ViewCaptureRule viewCaptureRule = new ViewCaptureRule( 252 Launcher.ACTIVITY_TRACKER::getCreatedActivity); 253 final RuleChain inner = RuleChain 254 .outerRule(new PortraitLandscapeRunner<LAUNCHER_TYPE>(this)) 255 .around(new FailureWatcher(mLauncher, viewCaptureRule::getViewCaptureData)) 256 // .around(viewCaptureRule) // b/315482167 257 .around(new TestIsolationRule(mLauncher, true)); 258 259 return TestHelpers.isInLauncherProcess() 260 ? RuleChain.outerRule(ShellCommandRule.setDefaultLauncher()).around(inner) 261 : inner; 262 } 263 264 @Rule 265 public TestRule mOrderSensitiveRules = RuleChain 266 .outerRule(new SamplerRule()) 267 .around(new TestStabilityRule()) 268 .around(getRulesInsideActivityMonitor()); 269 getDevice()270 public UiDevice getDevice() { 271 return mDevice; 272 } 273 274 @Before setUp()275 public void setUp() throws Exception { 276 mLauncher.onTestStart(); 277 278 final String launcherPackageName = mDevice.getLauncherPackageName(); 279 try { 280 final Context context = InstrumentationRegistry.getContext(); 281 final PackageManager pm = context.getPackageManager(); 282 final PackageInfo launcherPackage = pm.getPackageInfo(launcherPackageName, 0); 283 284 if (!launcherPackage.versionName.equals("BuildFromAndroidStudio")) { 285 Assert.assertEquals("Launcher version doesn't match tests version", 286 pm.getPackageInfo(context.getPackageName(), 0).getLongVersionCode(), 287 launcherPackage.getLongVersionCode()); 288 } 289 } catch (PackageManager.NameNotFoundException e) { 290 throw new RuntimeException(e); 291 } 292 293 mLauncherPid = 0; 294 295 mTargetContext = InstrumentationRegistry.getTargetContext(); 296 mTargetPackage = mTargetContext.getPackageName(); 297 mLauncherPid = mLauncher.getPid(); 298 299 UserManager userManager = mTargetContext.getSystemService(UserManager.class); 300 if (userManager != null) { 301 for (UserHandle userHandle : userManager.getUserProfiles()) { 302 if (!userHandle.isSystem()) { 303 mDevice.executeShellCommand( 304 "pm remove-user --wait " + userHandle.getIdentifier()); 305 } 306 } 307 } 308 309 onTestStart(); 310 311 initialize(this); 312 } 313 314 /** Method that should be called when a test starts. */ onTestStart()315 public static void onTestStart() { 316 waitForSetupWizardDismissal(); 317 318 if (TestStabilityRule.isPresubmit()) { 319 aggressivelyUnlockSysUi(); 320 } else { 321 verifyKeyguardInvisible(); 322 } 323 } 324 hasSystemUiObject(String resId)325 private static boolean hasSystemUiObject(String resId) { 326 return getUiDevice().hasObject( 327 By.res(SYSTEMUI_PACKAGE, resId)); 328 } 329 330 @NonNull getUiDevice()331 private static UiDevice getUiDevice() { 332 return UiDevice.getInstance(getInstrumentation()); 333 } 334 aggressivelyUnlockSysUi()335 private static void aggressivelyUnlockSysUi() { 336 final UiDevice device = getUiDevice(); 337 for (int i = 0; i < 10 && hasSystemUiObject("keyguard_status_view"); ++i) { 338 Log.d(TAG, "Before attempting to unlock the phone"); 339 try { 340 device.executeShellCommand("input keyevent 82"); 341 } catch (IOException e) { 342 throw new RuntimeException(e); 343 } 344 device.waitForIdle(); 345 } 346 Assert.assertTrue("Keyguard still visible", 347 TestHelpers.wait( 348 Until.gone(By.res(SYSTEMUI_PACKAGE, "keyguard_status_view")), 60000)); 349 Log.d(TAG, "Keyguard is not visible"); 350 } 351 352 /** Waits for setup wizard to go away. */ waitForSetupWizardDismissal()353 private static void waitForSetupWizardDismissal() { 354 if (!TestStabilityRule.isPresubmit()) return; 355 356 if (sFirstTimeWaitingForWizard) { 357 try { 358 getUiDevice().executeShellCommand( 359 "am force-stop com.google.android.setupwizard"); 360 } catch (IOException e) { 361 throw new RuntimeException(e); 362 } 363 } 364 365 final boolean wizardDismissed = TestHelpers.wait( 366 Until.gone(By.pkg("com.google.android.setupwizard").depth(0)), 367 sFirstTimeWaitingForWizard ? 120000 : 0); 368 sFirstTimeWaitingForWizard = false; 369 Assert.assertTrue("Setup wizard is still visible", wizardDismissed); 370 } 371 372 /** Asserts that keyguard is not visible */ verifyKeyguardInvisible()373 public static void verifyKeyguardInvisible() { 374 final boolean keyguardAlreadyVisible = sSeenKeyguard; 375 376 sSeenKeyguard = sSeenKeyguard 377 || !TestHelpers.wait( 378 Until.gone(By.res(SYSTEMUI_PACKAGE, "keyguard_status_view")), 60000); 379 380 Assert.assertFalse( 381 "Keyguard is visible, which is likely caused by a crash in SysUI, seeing keyguard" 382 + " for the first time = " 383 + !keyguardAlreadyVisible, 384 sSeenKeyguard); 385 } 386 387 @After verifyLauncherState()388 public void verifyLauncherState() { 389 try { 390 // Limits UI tests affecting tests running after them. 391 mLauncher.waitForLauncherInitialized(); 392 if (mLauncherPid != 0) { 393 assertEquals("Launcher crashed, pid mismatch:", 394 mLauncherPid, mLauncher.getPid().intValue()); 395 } 396 } finally { 397 mLauncher.onTestFinish(); 398 } 399 } 400 reinitializeLauncherData()401 protected void reinitializeLauncherData() { 402 reinitializeLauncherData(false); 403 } 404 reinitializeLauncherData(boolean clearWorkspace)405 protected void reinitializeLauncherData(boolean clearWorkspace) { 406 if (clearWorkspace) { 407 mLauncher.clearLauncherData(); 408 } else { 409 mLauncher.reinitializeLauncherData(); 410 } 411 mLauncher.waitForLauncherInitialized(); 412 } 413 414 /** 415 * Runs the callback on the UI thread and returns the result. 416 */ getOnUiThread(final Callable<T> callback)417 protected <T> T getOnUiThread(final Callable<T> callback) { 418 try { 419 return mMainThreadExecutor.submit(callback).get(DEFAULT_UI_TIMEOUT, 420 TimeUnit.MILLISECONDS); 421 } catch (TimeoutException e) { 422 Log.e(TAG, "Timeout in getOnUiThread, sending SIGABRT", e); 423 Process.sendSignal(Process.myPid(), OsConstants.SIGABRT); 424 throw new RuntimeException(e); 425 } catch (Throwable e) { 426 throw new RuntimeException(e); 427 } 428 } 429 getFromLauncher(Function<LAUNCHER_TYPE, T> f)430 protected <T> T getFromLauncher(Function<LAUNCHER_TYPE, T> f) { 431 if (!TestHelpers.isInLauncherProcess()) return null; 432 return getOnUiThread(() -> f.apply(Launcher.ACTIVITY_TRACKER.getCreatedActivity())); 433 } 434 executeOnLauncher(Consumer<LAUNCHER_TYPE> f)435 protected void executeOnLauncher(Consumer<LAUNCHER_TYPE> f) { 436 getFromLauncher(launcher -> { 437 f.accept(launcher); 438 return null; 439 }); 440 } 441 442 // Execute an action on Launcher, but forgive it when launcher is null. 443 // Launcher can be null if teardown is happening after a failed setup step where launcher 444 // activity failed to be created. executeOnLauncherInTearDown(Consumer<LAUNCHER_TYPE> f)445 protected void executeOnLauncherInTearDown(Consumer<LAUNCHER_TYPE> f) { 446 executeOnLauncher(launcher -> { 447 if (launcher != null) f.accept(launcher); 448 }); 449 } 450 451 // Cannot be used in TaplTests between a Tapl call injecting a gesture and a tapl call 452 // expecting the results of that gesture because the wait can hide flakeness. waitForState(String message, Supplier<LauncherState> state)453 protected void waitForState(String message, Supplier<LauncherState> state) { 454 waitForLauncherCondition(message, 455 launcher -> launcher.getStateManager().getCurrentStableState() == state.get()); 456 } 457 458 // Cannot be used in TaplTests between a Tapl call injecting a gesture and a tapl call 459 // expecting the results of that gesture because the wait can hide flakeness. waitForStateTransitionToEnd(String message, Supplier<LauncherState> state)460 protected void waitForStateTransitionToEnd(String message, Supplier<LauncherState> state) { 461 waitForLauncherCondition(message, 462 launcher -> launcher.getStateManager().isInStableState(state.get()) 463 && !launcher.getStateManager().isInTransition()); 464 } 465 waitForResumed(String message)466 protected void waitForResumed(String message) { 467 waitForLauncherCondition(message, launcher -> launcher.hasBeenResumed()); 468 } 469 470 // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide 471 // flakiness. waitForLauncherCondition(String message, Function<LAUNCHER_TYPE, Boolean> condition)472 protected void waitForLauncherCondition(String 473 message, Function<LAUNCHER_TYPE, Boolean> condition) { 474 waitForLauncherCondition(message, condition, DEFAULT_ACTIVITY_TIMEOUT); 475 } 476 477 // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide 478 // flakiness. getOnceNotNull(String message, Function<LAUNCHER_TYPE, O> f)479 protected <O> O getOnceNotNull(String message, Function<LAUNCHER_TYPE, O> f) { 480 return getOnceNotNull(message, f, DEFAULT_ACTIVITY_TIMEOUT); 481 } 482 483 // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide 484 // flakiness. waitForLauncherCondition( String message, Function<LAUNCHER_TYPE, Boolean> condition, long timeout)485 protected void waitForLauncherCondition( 486 String message, Function<LAUNCHER_TYPE, Boolean> condition, long timeout) { 487 verifyKeyguardInvisible(); 488 if (!TestHelpers.isInLauncherProcess()) return; 489 Wait.atMost(message, () -> getFromLauncher(condition), timeout, mLauncher); 490 } 491 492 // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide 493 // flakiness. getOnceNotNull(String message, Function<LAUNCHER_TYPE, T> f, long timeout)494 protected <T> T getOnceNotNull(String message, Function<LAUNCHER_TYPE, T> f, long timeout) { 495 if (!TestHelpers.isInLauncherProcess()) return null; 496 497 final Object[] output = new Object[1]; 498 Wait.atMost(message, () -> { 499 final Object fromLauncher = getFromLauncher(f); 500 output[0] = fromLauncher; 501 return fromLauncher != null; 502 }, timeout, mLauncher); 503 return (T) output[0]; 504 } 505 506 // Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide 507 // flakiness. waitForLauncherCondition( String message, Runnable testThreadAction, Function<LAUNCHER_TYPE, Boolean> condition, long timeout)508 protected void waitForLauncherCondition( 509 String message, 510 Runnable testThreadAction, Function<LAUNCHER_TYPE, Boolean> condition, 511 long timeout) { 512 if (!TestHelpers.isInLauncherProcess()) return; 513 Wait.atMost(message, () -> { 514 testThreadAction.run(); 515 return getFromLauncher(condition); 516 }, timeout, mLauncher); 517 } 518 getSettingsApp()519 protected LauncherActivityInfo getSettingsApp() { 520 return mTargetContext.getSystemService(LauncherApps.class) 521 .getActivityList("com.android.settings", Process.myUserHandle()).get(0); 522 } 523 524 /** 525 * Broadcast receiver which blocks until the result is received. 526 */ 527 public class BlockingBroadcastReceiver extends BroadcastReceiver { 528 529 private final CountDownLatch latch = new CountDownLatch(1); 530 private Intent mIntent; 531 BlockingBroadcastReceiver(String action)532 public BlockingBroadcastReceiver(String action) { 533 mTargetContext.registerReceiver(this, new IntentFilter(action), 534 Context.RECEIVER_EXPORTED/*UNAUDITED*/); 535 } 536 537 @Override onReceive(Context context, Intent intent)538 public void onReceive(Context context, Intent intent) { 539 Log.d(WIDGET_CONFIG_NULL_EXTRA_INTENT, intent == null 540 ? "AbstractLauncherUiTest.onReceive(): inputted intent NULL" 541 : "AbstractLauncherUiTest.onReceive(): inputted intent NOT NULL"); 542 mIntent = intent; 543 latch.countDown(); 544 Log.d(WIDGET_CONFIG_NULL_EXTRA_INTENT, 545 "AbstractLauncherUiTest.onReceive() Countdown Latch started"); 546 } 547 blockingGetIntent()548 public Intent blockingGetIntent() throws InterruptedException { 549 Log.d(WIDGET_CONFIG_NULL_EXTRA_INTENT, 550 "AbstractLauncherUiTest.blockingGetIntent()"); 551 assertTrue("Timed Out", latch.await(DEFAULT_BROADCAST_TIMEOUT_SECS, TimeUnit.SECONDS)); 552 mTargetContext.unregisterReceiver(this); 553 Log.d(WIDGET_CONFIG_NULL_EXTRA_INTENT, mIntent == null 554 ? "AbstractLauncherUiTest.onReceive(): mIntent NULL" 555 : "AbstractLauncherUiTest.onReceive(): mIntent NOT NULL"); 556 return mIntent; 557 } 558 blockingGetExtraIntent()559 public Intent blockingGetExtraIntent() throws InterruptedException { 560 Intent intent = blockingGetIntent(); 561 return intent == null ? null : (Intent) intent.getParcelableExtra( 562 Intent.EXTRA_INTENT); 563 } 564 } 565 startAppFast(String packageName)566 public static void startAppFast(String packageName) { 567 startIntent( 568 getInstrumentation().getContext().getPackageManager().getLaunchIntentForPackage( 569 packageName), 570 By.pkg(packageName).depth(0), 571 true /* newTask */); 572 } 573 startTestActivity(String activityName, String activityLabel)574 public static void startTestActivity(String activityName, String activityLabel) { 575 final String packageName = getAppPackageName(); 576 final Intent intent = getInstrumentation().getContext().getPackageManager(). 577 getLaunchIntentForPackage(packageName); 578 intent.setComponent(new ComponentName(packageName, 579 "com.android.launcher3.tests." + activityName)); 580 startIntent(intent, By.pkg(packageName).text(activityLabel), 581 false /* newTask */); 582 } 583 startTestActivity(int activityNumber)584 public static void startTestActivity(int activityNumber) { 585 startTestActivity("Activity" + activityNumber, "TestActivity" + activityNumber); 586 } 587 startImeTestActivity()588 public static void startImeTestActivity() { 589 final String packageName = getAppPackageName(); 590 final Intent intent = getInstrumentation().getContext().getPackageManager(). 591 getLaunchIntentForPackage(packageName); 592 intent.setComponent(new ComponentName(packageName, 593 "com.android.launcher3.testcomponent.ImeTestActivity")); 594 startIntent(intent, By.pkg(packageName).text("ImeTestActivity"), 595 false /* newTask */); 596 } 597 598 /** Starts ExcludeFromRecentsTestActivity, which has excludeFromRecents="true". */ startExcludeFromRecentsTestActivity()599 public static void startExcludeFromRecentsTestActivity() { 600 final String packageName = getAppPackageName(); 601 final Intent intent = getInstrumentation().getContext().getPackageManager() 602 .getLaunchIntentForPackage(packageName); 603 intent.setComponent(new ComponentName(packageName, 604 "com.android.launcher3.testcomponent.ExcludeFromRecentsTestActivity")); 605 startIntent(intent, By.pkg(packageName).text("ExcludeFromRecentsTestActivity"), 606 false /* newTask */); 607 } 608 startIntent(Intent intent, BySelector selector, boolean newTask)609 private static void startIntent(Intent intent, BySelector selector, boolean newTask) { 610 intent.addCategory(Intent.CATEGORY_LAUNCHER); 611 if (newTask) { 612 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 613 } else { 614 intent.addFlags( 615 Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT); 616 } 617 getInstrumentation().getTargetContext().startActivity(intent); 618 assertTrue("App didn't start: " + selector, 619 TestHelpers.wait(Until.hasObject(selector), DEFAULT_UI_TIMEOUT)); 620 621 // Wait for the Launcher to stop. 622 final LauncherInstrumentation launcherInstrumentation = new LauncherInstrumentation(); 623 Wait.atMost("Launcher activity didn't stop", 624 () -> !launcherInstrumentation.isLauncherActivityStarted(), 625 DEFAULT_ACTIVITY_TIMEOUT, launcherInstrumentation); 626 } 627 resolveSystemAppInfo(String category)628 public static ActivityInfo resolveSystemAppInfo(String category) { 629 return getInstrumentation().getContext().getPackageManager().resolveActivity( 630 new Intent(Intent.ACTION_MAIN).addCategory(category), 631 PackageManager.MATCH_SYSTEM_ONLY). 632 activityInfo; 633 } 634 635 resolveSystemApp(String category)636 public static String resolveSystemApp(String category) { 637 return resolveSystemAppInfo(category).packageName; 638 } 639 closeLauncherActivity()640 protected void closeLauncherActivity() { 641 // Destroy Launcher activity. 642 executeOnLauncher(launcher -> { 643 if (launcher != null) { 644 onLauncherActivityClose(launcher); 645 launcher.finish(); 646 } 647 }); 648 waitForLauncherCondition( 649 "Launcher still active", launcher -> launcher == null, DEFAULT_UI_TIMEOUT); 650 } 651 isInLaunchedApp(LAUNCHER_TYPE launcher)652 protected boolean isInLaunchedApp(LAUNCHER_TYPE launcher) { 653 return launcher == null || !launcher.hasBeenResumed(); 654 } 655 isInState(Supplier<LauncherState> state)656 protected boolean isInState(Supplier<LauncherState> state) { 657 if (!TestHelpers.isInLauncherProcess()) return true; 658 return getFromLauncher( 659 launcher -> launcher.getStateManager().getState() == state.get()); 660 } 661 getAllAppsScroll(LAUNCHER_TYPE launcher)662 protected int getAllAppsScroll(LAUNCHER_TYPE launcher) { 663 return launcher.getAppsView().getActiveRecyclerView().computeVerticalScrollOffset(); 664 } 665 onLauncherActivityClose(LAUNCHER_TYPE launcher)666 protected void onLauncherActivityClose(LAUNCHER_TYPE launcher) { 667 } 668 createShortcutInCenterIfNotExist(String name)669 protected HomeAppIcon createShortcutInCenterIfNotExist(String name) { 670 Point dimension = mLauncher.getWorkspace().getIconGridDimensions(); 671 return createShortcutIfNotExist(name, dimension.x / 2, dimension.y / 2); 672 } 673 createShortcutIfNotExist(String name, Point cellPosition)674 protected HomeAppIcon createShortcutIfNotExist(String name, Point cellPosition) { 675 return createShortcutIfNotExist(name, cellPosition.x, cellPosition.y); 676 } 677 createShortcutIfNotExist(String name, int cellX, int cellY)678 protected HomeAppIcon createShortcutIfNotExist(String name, int cellX, int cellY) { 679 HomeAppIcon homeAppIcon = mLauncher.getWorkspace().tryGetWorkspaceAppIcon(name); 680 Log.d(ICON_MISSING, "homeAppIcon: " + homeAppIcon + " name: " + name + 681 " cell: " + cellX + ", " + cellY); 682 if (homeAppIcon == null) { 683 HomeAllApps allApps = mLauncher.getWorkspace().switchToAllApps(); 684 allApps.freeze(); 685 try { 686 allApps.getAppIcon(name).dragToWorkspace(cellX, cellY); 687 } finally { 688 allApps.unfreeze(); 689 } 690 homeAppIcon = mLauncher.getWorkspace().getWorkspaceAppIcon(name); 691 } 692 return homeAppIcon; 693 } 694 commitTransactionAndLoadHome(FavoriteItemsTransaction transaction)695 protected void commitTransactionAndLoadHome(FavoriteItemsTransaction transaction) { 696 transaction.commit(); 697 698 // Launch the home activity 699 UiDevice.getInstance(getInstrumentation()).pressHome(); 700 mLauncher.waitForLauncherInitialized(); 701 } 702 703 /** Clears all recent tasks */ clearAllRecentTasks()704 protected void clearAllRecentTasks() { 705 if (!mLauncher.getRecentTasks().isEmpty()) { 706 mLauncher.goHome().switchToOverview().dismissAllTasks(); 707 } 708 } 709 } 710