1 /* 2 * Copyright (C) 2017 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.server.wm; 18 19 import static android.app.UiModeManager.MODE_NIGHT_AUTO; 20 import static android.app.UiModeManager.MODE_NIGHT_CUSTOM; 21 import static android.app.UiModeManager.MODE_NIGHT_NO; 22 import static android.app.UiModeManager.MODE_NIGHT_YES; 23 import static android.content.Intent.ACTION_MAIN; 24 import static android.content.Intent.CATEGORY_HOME; 25 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; 26 import static android.server.wm.CliIntentExtra.extraBool; 27 import static android.server.wm.CliIntentExtra.extraString; 28 import static android.server.wm.WindowManagerState.STATE_RESUMED; 29 import static android.server.wm.app.Components.HANDLE_SPLASH_SCREEN_EXIT_ACTIVITY; 30 import static android.server.wm.app.Components.HOME_ACTIVITY; 31 import static android.server.wm.app.Components.SPLASHSCREEN_ACTIVITY; 32 import static android.server.wm.app.Components.SPLASH_SCREEN_REPLACE_ICON_ACTIVITY; 33 import static android.server.wm.app.Components.SPLASH_SCREEN_REPLACE_THEME_ACTIVITY; 34 import static android.server.wm.app.Components.TestActivity.COMMAND_START_ACTIVITY; 35 import static android.server.wm.app.Components.TestActivity.EXTRA_INTENT; 36 import static android.server.wm.app.Components.TestStartingWindowKeys.CANCEL_HANDLE_EXIT; 37 import static android.server.wm.app.Components.TestStartingWindowKeys.CENTER_VIEW_IS_SURFACE_VIEW; 38 import static android.server.wm.app.Components.TestStartingWindowKeys.CONTAINS_BRANDING_VIEW; 39 import static android.server.wm.app.Components.TestStartingWindowKeys.CONTAINS_CENTER_VIEW; 40 import static android.server.wm.app.Components.TestStartingWindowKeys.DELAY_RESUME; 41 import static android.server.wm.app.Components.TestStartingWindowKeys.GET_NIGHT_MODE_ACTIVITY_CHANGED; 42 import static android.server.wm.app.Components.TestStartingWindowKeys.HANDLE_SPLASH_SCREEN_EXIT; 43 import static android.server.wm.app.Components.TestStartingWindowKeys.ICON_ANIMATION_DURATION; 44 import static android.server.wm.app.Components.TestStartingWindowKeys.ICON_ANIMATION_START; 45 import static android.server.wm.app.Components.TestStartingWindowKeys.ICON_BACKGROUND_COLOR; 46 import static android.server.wm.app.Components.TestStartingWindowKeys.OVERRIDE_THEME_COLOR; 47 import static android.server.wm.app.Components.TestStartingWindowKeys.OVERRIDE_THEME_COMPONENT; 48 import static android.server.wm.app.Components.TestStartingWindowKeys.OVERRIDE_THEME_ENABLED; 49 import static android.server.wm.app.Components.TestStartingWindowKeys.RECEIVE_SPLASH_SCREEN_EXIT; 50 import static android.server.wm.app.Components.TestStartingWindowKeys.REPLACE_ICON_EXIT; 51 import static android.server.wm.app.Components.TestStartingWindowKeys.REQUEST_HANDLE_EXIT_ON_CREATE; 52 import static android.server.wm.app.Components.TestStartingWindowKeys.REQUEST_HANDLE_EXIT_ON_RESUME; 53 import static android.server.wm.app.Components.TestStartingWindowKeys.REQUEST_SET_NIGHT_MODE_ON_CREATE; 54 import static android.view.Display.DEFAULT_DISPLAY; 55 import static android.view.WindowInsets.Type.captionBar; 56 import static android.view.WindowInsets.Type.systemBars; 57 58 import static org.hamcrest.MatcherAssert.assertThat; 59 import static org.hamcrest.Matchers.greaterThanOrEqualTo; 60 import static org.hamcrest.Matchers.lessThanOrEqualTo; 61 import static org.junit.Assert.assertEquals; 62 import static org.junit.Assert.assertFalse; 63 import static org.junit.Assert.assertTrue; 64 import static org.junit.Assert.fail; 65 import static org.junit.Assume.assumeFalse; 66 import static org.junit.Assume.assumeTrue; 67 68 import android.app.UiModeManager; 69 import android.content.ComponentName; 70 import android.content.Intent; 71 import android.content.pm.LauncherApps; 72 import android.content.pm.ShortcutInfo; 73 import android.content.pm.ShortcutManager; 74 import android.content.res.Configuration; 75 import android.graphics.Bitmap; 76 import android.graphics.Color; 77 import android.graphics.Insets; 78 import android.graphics.Rect; 79 import android.os.Bundle; 80 import android.platform.test.annotations.Presubmit; 81 import android.view.WindowManager; 82 import android.view.WindowMetrics; 83 84 import androidx.core.graphics.ColorUtils; 85 86 import com.android.compatibility.common.util.TestUtils; 87 88 import org.junit.After; 89 import org.junit.Before; 90 import org.junit.Rule; 91 import org.junit.Test; 92 93 import java.util.Collections; 94 import java.util.function.Consumer; 95 96 /** 97 * Build/Install/Run: 98 * atest CtsWindowManagerDeviceTestCases:SplashscreenTests 99 */ 100 @Presubmit 101 @android.server.wm.annotation.Group1 102 public class SplashscreenTests extends ActivityManagerTestBase { 103 104 private static final int CENTER_ICON_SIZE = 192; 105 106 @Rule 107 public final DumpOnFailure dumpOnFailure = new DumpOnFailure(); 108 109 @Before setUp()110 public void setUp() throws Exception { 111 super.setUp(); 112 mWmState.setSanityCheckWithFocusedWindow(false); 113 } 114 115 @After tearDown()116 public void tearDown() { 117 mWmState.setSanityCheckWithFocusedWindow(true); 118 } 119 prepareTestLauncher()120 private CommandSession.ActivitySession prepareTestLauncher() { 121 createManagedHomeActivitySession(HOME_ACTIVITY); 122 return createManagedActivityClientSession() 123 .startActivity(new Intent(ACTION_MAIN) 124 .addCategory(CATEGORY_HOME) 125 .addFlags(FLAG_ACTIVITY_NEW_TASK) 126 .setComponent(HOME_ACTIVITY)); 127 } 128 startActivityFromTestLauncher(CommandSession.ActivitySession homeActivity, ComponentName componentName, Consumer<Intent> fillExtra)129 private void startActivityFromTestLauncher(CommandSession.ActivitySession homeActivity, 130 ComponentName componentName, Consumer<Intent> fillExtra) { 131 132 final Bundle data = new Bundle(); 133 final Intent startIntent = new Intent(); 134 startIntent.setComponent(componentName); 135 startIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 136 fillExtra.accept(startIntent); 137 data.putParcelable(EXTRA_INTENT, startIntent); 138 homeActivity.sendCommand(COMMAND_START_ACTIVITY, data); 139 } 140 141 @Test testSplashscreenContent()142 public void testSplashscreenContent() { 143 // TODO(b/192431448): Allow Automotive to skip this test until Splash Screen is properly 144 // applied insets by system bars in AAOS. 145 assumeFalse(isCar()); 146 147 launchActivityNoWait(SPLASHSCREEN_ACTIVITY); 148 // The windowSplashScreenContent attribute is set to RED. We check that it is ignored. 149 testSplashScreenColor(SPLASHSCREEN_ACTIVITY, Color.BLUE, Color.WHITE); 150 } 151 testSplashScreenColor(ComponentName name, int primaryColor, int secondaryColor)152 private void testSplashScreenColor(ComponentName name, int primaryColor, int secondaryColor) { 153 // Activity may not be launched yet even if app transition is in idle state. 154 mWmState.waitForActivityState(name, STATE_RESUMED); 155 mWmState.waitForAppTransitionIdleOnDisplay(DEFAULT_DISPLAY); 156 final Bitmap image = takeScreenshot(); 157 final WindowMetrics windowMetrics = mWm.getMaximumWindowMetrics(); 158 final Rect stableBounds = new Rect(windowMetrics.getBounds()); 159 Insets insets = windowMetrics.getWindowInsets().getInsetsIgnoringVisibility( 160 systemBars() & ~captionBar()); 161 stableBounds.inset(insets); 162 WindowManagerState.WindowState startingWindow = mWmState.findFirstWindowWithType( 163 WindowManager.LayoutParams.TYPE_APPLICATION_STARTING); 164 165 Rect startingWindowBounds = startingWindow.getBounds(); 166 final Rect appBounds; 167 if (startingWindowBounds != null) { 168 appBounds = new Rect(startingWindowBounds); 169 } else { 170 appBounds = new Rect(startingWindow.getFrame()); 171 } 172 173 Rect topInsetsBounds = new Rect(insets.left, 0, appBounds.right - insets.right, insets.top); 174 Rect bottomInsetsBounds = new Rect(insets.left, appBounds.bottom - insets.bottom, 175 appBounds.right - insets.right, appBounds.bottom); 176 assertFalse("Top insets bounds rect is empty", topInsetsBounds.isEmpty()); 177 assertFalse("Bottom insets bounds rect is empty", bottomInsetsBounds.isEmpty()); 178 179 if (appBounds.isEmpty()) { 180 fail("Couldn't find splash screen bounds. Impossible to assert the colors"); 181 } 182 183 // Use ratios to flexibly accommodate circular or not quite rectangular displays 184 // Note: Color.BLACK is the pixel color outside of the display region 185 186 int px = WindowManagerState.dpToPx(CENTER_ICON_SIZE, 187 mContext.getResources().getConfiguration().densityDpi); 188 Rect ignoreRect = new Rect(0, 0, px, px); 189 ignoreRect.offsetTo(appBounds.centerX() - ignoreRect.width() / 2, 190 appBounds.centerY() - ignoreRect.height() / 2); 191 192 appBounds.intersect(stableBounds); 193 assertColors(image, appBounds, primaryColor, 0.99f, secondaryColor, 0.02f, ignoreRect); 194 assertColors(image, topInsetsBounds, primaryColor, 0.80f, secondaryColor, 0.10f, null); 195 assertColors(image, bottomInsetsBounds, primaryColor, 0.80f, secondaryColor, 0.10f, null); 196 } 197 198 // For real devices, gamma correction might be applied on hardware driver, so the colors may 199 // not exactly match. isSimilarColor(int a, int b)200 private static boolean isSimilarColor(int a, int b) { 201 if (a == b) { 202 return true; 203 } 204 return Math.abs(Color.alpha(a) - Color.alpha(b)) + 205 Math.abs(Color.red(a) - Color.red(b)) + 206 Math.abs(Color.green(a) - Color.green(b)) + 207 Math.abs(Color.blue(a) - Color.blue(b)) < 10; 208 } 209 assertColors(Bitmap img, Rect bounds, int primaryColor, float expectedPrimaryRatio, int secondaryColor, float acceptableWrongRatio, Rect ignoreRect)210 private void assertColors(Bitmap img, Rect bounds, int primaryColor, float expectedPrimaryRatio, 211 int secondaryColor, float acceptableWrongRatio, Rect ignoreRect) { 212 213 int primaryPixels = 0; 214 int secondaryPixels = 0; 215 int wrongPixels = 0; 216 217 assertThat(bounds.top, greaterThanOrEqualTo(0)); 218 assertThat(bounds.left, greaterThanOrEqualTo(0)); 219 assertThat(bounds.right, lessThanOrEqualTo(img.getWidth())); 220 assertThat(bounds.bottom, lessThanOrEqualTo(img.getHeight())); 221 222 for (int x = bounds.left; x < bounds.right; x++) { 223 for (int y = bounds.top; y < bounds.bottom; y++) { 224 if (ignoreRect != null && ignoreRect.contains(x, y)) { 225 continue; 226 } 227 final int color = img.getPixel(x, y); 228 if (isSimilarColor(primaryColor, color)) { 229 primaryPixels++; 230 } else if (isSimilarColor(secondaryColor, color)) { 231 secondaryPixels++; 232 } else { 233 wrongPixels++; 234 } 235 } 236 } 237 238 int totalPixels = bounds.width() * bounds.height(); 239 if (ignoreRect != null) { 240 totalPixels -= ignoreRect.width() * ignoreRect.height(); 241 } 242 243 final float primaryRatio = (float) primaryPixels / totalPixels; 244 if (primaryRatio < expectedPrimaryRatio) { 245 generateFailureImage(img, bounds, primaryColor, secondaryColor, ignoreRect); 246 fail("Less than " + (expectedPrimaryRatio * 100.0f) 247 + "% of pixels have non-primary color primaryPixels=" + primaryPixels 248 + " secondaryPixels=" + secondaryPixels + " wrongPixels=" + wrongPixels); 249 } 250 // Some pixels might be covered by screen shape decorations, like rounded corners. 251 // On circular displays, there is an antialiased edge. 252 final float wrongRatio = (float) wrongPixels / totalPixels; 253 if (wrongRatio > acceptableWrongRatio) { 254 generateFailureImage(img, bounds, primaryColor, secondaryColor, ignoreRect); 255 fail("More than " + (acceptableWrongRatio * 100.0f) 256 + "% of pixels have wrong color primaryPixels=" + primaryPixels 257 + " secondaryPixels=" + secondaryPixels + " wrongPixels=" 258 + wrongPixels); 259 } 260 } 261 generateFailureImage(Bitmap img, Rect bounds, int primaryColor, int secondaryColor, Rect ignoreRect)262 private void generateFailureImage(Bitmap img, Rect bounds, int primaryColor, 263 int secondaryColor, Rect ignoreRect) { 264 265 // Create a bitmap with on the left the original image and on the right the result of the 266 // test. The pixel marked in green have the right color, the transparent black one are 267 // ignored and the wrong pixels have the original color. 268 final int ignoredDebugColor = 0xEE000000; 269 final int validDebugColor = 0x6600FF00; 270 Bitmap result = Bitmap.createBitmap(img.getWidth() * 2, img.getHeight(), 271 Bitmap.Config.ARGB_8888); 272 273 // Execute the exact same logic applied in assertColor() to avoid bugs between the assertion 274 // method and the failure method 275 for (int x = bounds.left; x < bounds.right; x++) { 276 for (int y = bounds.top; y < bounds.bottom; y++) { 277 final int pixel = img.getPixel(x, y); 278 if (ignoreRect != null && ignoreRect.contains(x, y)) { 279 markDebugPixel(pixel, result, x, y, ignoredDebugColor, 0.95f); 280 continue; 281 } 282 if (isSimilarColor(primaryColor, pixel)) { 283 markDebugPixel(pixel, result, x, y, validDebugColor, 0.8f); 284 } else if (isSimilarColor(secondaryColor, pixel)) { 285 markDebugPixel(pixel, result, x, y, validDebugColor, 0.8f); 286 } else { 287 markDebugPixel(pixel, result, x, y, Color.TRANSPARENT, 0.0f); 288 } 289 } 290 } 291 292 // Mark the pixels outside the bounds as ignored 293 for (int x = 0; x < img.getWidth(); x++) { 294 for (int y = 0; y < img.getHeight(); y++) { 295 if (bounds.contains(x, y)) { 296 continue; 297 } 298 markDebugPixel(img.getPixel(x, y), result, x, y, ignoredDebugColor, 0.95f); 299 } 300 } 301 dumpOnFailure.dumpOnFailure("splashscreen-color-check", result); 302 } 303 markDebugPixel(int pixel, Bitmap result, int x, int y, int color, float ratio)304 private void markDebugPixel(int pixel, Bitmap result, int x, int y, int color, float ratio) { 305 int debugPixel = ColorUtils.blendARGB(pixel, color, ratio); 306 result.setPixel(x, y, pixel); 307 int debugOffsetX = result.getWidth() / 2; 308 result.setPixel(x + debugOffsetX, y, debugPixel); 309 } 310 311 @Test testHandleExitAnimationOnCreate()312 public void testHandleExitAnimationOnCreate() throws Exception { 313 assumeFalse(isLeanBack()); 314 launchRuntimeHandleExitAnimationActivity(true, false, false, true); 315 } 316 317 @Test testHandleExitAnimationOnResume()318 public void testHandleExitAnimationOnResume() throws Exception { 319 assumeFalse(isLeanBack()); 320 launchRuntimeHandleExitAnimationActivity(false, true, false, true); 321 } 322 323 @Test testHandleExitAnimationCancel()324 public void testHandleExitAnimationCancel() throws Exception { 325 assumeFalse(isLeanBack()); 326 launchRuntimeHandleExitAnimationActivity(true, false, true, false); 327 } 328 launchRuntimeHandleExitAnimationActivity(boolean extraOnCreate, boolean extraOnResume, boolean extraCancel, boolean expectResult)329 private void launchRuntimeHandleExitAnimationActivity(boolean extraOnCreate, 330 boolean extraOnResume, boolean extraCancel, boolean expectResult) throws Exception { 331 TestJournalProvider.TestJournalContainer.start(); 332 final CommandSession.ActivitySession homeActivity = prepareTestLauncher(); 333 startActivityFromTestLauncher(homeActivity, HANDLE_SPLASH_SCREEN_EXIT_ACTIVITY, intent -> { 334 intent.putExtra(REQUEST_HANDLE_EXIT_ON_CREATE, extraOnCreate); 335 intent.putExtra(REQUEST_HANDLE_EXIT_ON_RESUME, extraOnResume); 336 intent.putExtra(CANCEL_HANDLE_EXIT, extraCancel); 337 }); 338 339 mWmState.computeState(HANDLE_SPLASH_SCREEN_EXIT_ACTIVITY); 340 mWmState.assertVisibility(HANDLE_SPLASH_SCREEN_EXIT_ACTIVITY, true); 341 final TestJournalProvider.TestJournal journal = 342 TestJournalProvider.TestJournalContainer.get(HANDLE_SPLASH_SCREEN_EXIT); 343 if (expectResult) { 344 TestUtils.waitUntil("Waiting for runtime onSplashScreenExit", 5 /* timeoutSecond */, 345 () -> journal.extras.getBoolean(RECEIVE_SPLASH_SCREEN_EXIT)); 346 assertTrue("No entry for CONTAINS_CENTER_VIEW", 347 journal.extras.containsKey(CONTAINS_CENTER_VIEW)); 348 assertTrue("No entry for CONTAINS_BRANDING_VIEW", 349 journal.extras.containsKey(CONTAINS_BRANDING_VIEW)); 350 assertTrue("Center View shouldn't be null", journal.extras.getBoolean(CONTAINS_CENTER_VIEW)); 351 assertTrue(journal.extras.getBoolean(CONTAINS_BRANDING_VIEW)); 352 assertEquals(Color.BLUE, journal.extras.getInt(ICON_BACKGROUND_COLOR, Color.YELLOW)); 353 } 354 } 355 356 @Test testSetApplicationNightMode()357 public void testSetApplicationNightMode() throws Exception { 358 final UiModeManager uiModeManager = mContext.getSystemService(UiModeManager.class); 359 assumeTrue(uiModeManager != null); 360 final int systemNightMode = uiModeManager.getNightMode(); 361 final int testNightMode = (systemNightMode == MODE_NIGHT_AUTO 362 || systemNightMode == MODE_NIGHT_CUSTOM) ? MODE_NIGHT_YES 363 : systemNightMode == MODE_NIGHT_YES ? MODE_NIGHT_NO : MODE_NIGHT_YES; 364 final int testConfigNightMode = testNightMode == MODE_NIGHT_YES 365 ? Configuration.UI_MODE_NIGHT_YES 366 : Configuration.UI_MODE_NIGHT_NO; 367 final String nightModeNo = String.valueOf(testNightMode); 368 369 TestJournalProvider.TestJournalContainer.start(); 370 launchActivity(HANDLE_SPLASH_SCREEN_EXIT_ACTIVITY, 371 extraString(REQUEST_SET_NIGHT_MODE_ON_CREATE, nightModeNo)); 372 mWmState.computeState(HANDLE_SPLASH_SCREEN_EXIT_ACTIVITY); 373 mWmState.assertVisibility(HANDLE_SPLASH_SCREEN_EXIT_ACTIVITY, true); 374 final TestJournalProvider.TestJournal journal = 375 TestJournalProvider.TestJournalContainer.get(HANDLE_SPLASH_SCREEN_EXIT); 376 TestUtils.waitUntil("Waiting for night mode changed", 5 /* timeoutSecond */, () -> 377 testConfigNightMode == journal.extras.getInt(GET_NIGHT_MODE_ACTIVITY_CHANGED)); 378 assertEquals(testConfigNightMode, 379 journal.extras.getInt(GET_NIGHT_MODE_ACTIVITY_CHANGED)); 380 } 381 382 @Test testSetBackgroundColorActivity()383 public void testSetBackgroundColorActivity() { 384 // TODO(b/192431448): Allow Automotive to skip this test until Splash Screen is properly 385 // applied insets by system bars in AAOS. 386 assumeFalse(isCar()); 387 388 launchActivityNoWait(SPLASH_SCREEN_REPLACE_ICON_ACTIVITY, extraBool(DELAY_RESUME, true)); 389 testSplashScreenColor(SPLASH_SCREEN_REPLACE_ICON_ACTIVITY, Color.BLUE, Color.WHITE); 390 } 391 392 @Test testHandleExitIconAnimatingActivity()393 public void testHandleExitIconAnimatingActivity() throws Exception { 394 assumeFalse(isLeanBack()); 395 final CommandSession.ActivitySession homeActivity = prepareTestLauncher(); 396 TestJournalProvider.TestJournalContainer.start(); 397 398 startActivityFromTestLauncher(homeActivity, SPLASH_SCREEN_REPLACE_ICON_ACTIVITY, intent -> { 399 intent.putExtra(REQUEST_HANDLE_EXIT_ON_CREATE, true); 400 }); 401 mWmState.computeState(SPLASH_SCREEN_REPLACE_ICON_ACTIVITY); 402 mWmState.assertVisibility(SPLASH_SCREEN_REPLACE_ICON_ACTIVITY, true); 403 final TestJournalProvider.TestJournal journal = 404 TestJournalProvider.TestJournalContainer.get(REPLACE_ICON_EXIT); 405 TestUtils.waitUntil("Waiting for runtime onSplashScreenExit", 5 /* timeoutSecond */, 406 () -> journal.extras.getBoolean(RECEIVE_SPLASH_SCREEN_EXIT)); 407 assertTrue(journal.extras.getBoolean(CONTAINS_CENTER_VIEW)); 408 final long iconAnimationStart = journal.extras.getLong(ICON_ANIMATION_START); 409 final long iconAnimationDuration = journal.extras.getLong(ICON_ANIMATION_DURATION); 410 assertTrue(iconAnimationStart != 0); 411 assertEquals(iconAnimationDuration, 500); 412 assertFalse(journal.extras.getBoolean(CONTAINS_BRANDING_VIEW)); 413 assertTrue(journal.extras.getBoolean(CENTER_VIEW_IS_SURFACE_VIEW)); 414 } 415 416 @Test testCancelHandleExitIconAnimatingActivity()417 public void testCancelHandleExitIconAnimatingActivity() { 418 assumeFalse(isLeanBack()); 419 final CommandSession.ActivitySession homeActivity = prepareTestLauncher(); 420 TestJournalProvider.TestJournalContainer.start(); 421 startActivityFromTestLauncher(homeActivity, SPLASH_SCREEN_REPLACE_ICON_ACTIVITY, intent -> { 422 intent.putExtra(REQUEST_HANDLE_EXIT_ON_CREATE, true); 423 intent.putExtra(CANCEL_HANDLE_EXIT, true); 424 }); 425 mWmState.waitForActivityState(SPLASH_SCREEN_REPLACE_ICON_ACTIVITY, STATE_RESUMED); 426 mWmState.waitForAppTransitionIdleOnDisplay(DEFAULT_DISPLAY); 427 428 final TestJournalProvider.TestJournal journal = 429 TestJournalProvider.TestJournalContainer.get(REPLACE_ICON_EXIT); 430 assertFalse(journal.extras.getBoolean(RECEIVE_SPLASH_SCREEN_EXIT)); 431 } 432 433 @Test testShortcutChangeTheme()434 public void testShortcutChangeTheme() { 435 // TODO(b/192431448): Allow Automotive to skip this test until Splash Screen is properly 436 // applied insets by system bars in AAOS. 437 assumeFalse(isCar()); 438 439 final LauncherApps launcherApps = mContext.getSystemService(LauncherApps.class); 440 final ShortcutManager shortcutManager = mContext.getSystemService(ShortcutManager.class); 441 assumeTrue(launcherApps != null && shortcutManager != null); 442 443 final String shortCutId = "shortcut1"; 444 final ShortcutInfo.Builder b = new ShortcutInfo.Builder( 445 mContext, shortCutId); 446 final Intent i = new Intent(ACTION_MAIN) 447 .setComponent(SPLASHSCREEN_ACTIVITY); 448 final ShortcutInfo shortcut = b.setShortLabel("label") 449 .setLongLabel("long label") 450 .setIntent(i) 451 .setStartingTheme(android.R.style.Theme_Black_NoTitleBar_Fullscreen) 452 .build(); 453 try { 454 shortcutManager.addDynamicShortcuts(Collections.singletonList(shortcut)); 455 runWithShellPermission(() -> launcherApps.startShortcut(shortcut, null, null)); 456 testSplashScreenColor(SPLASHSCREEN_ACTIVITY, Color.BLACK, Color.WHITE); 457 } finally { 458 shortcutManager.removeDynamicShortcuts(Collections.singletonList(shortCutId)); 459 } 460 } 461 waitAndAssertOverrideThemeColor(int expectedColor)462 private void waitAndAssertOverrideThemeColor(int expectedColor) { 463 final ComponentName activity = SPLASH_SCREEN_REPLACE_THEME_ACTIVITY; 464 final Bundle resultExtras = Condition.waitForResult( 465 new Condition<Bundle>("splash screen theme color of " + activity) 466 .setResultSupplier(() -> TestJournalProvider.TestJournalContainer.get( 467 OVERRIDE_THEME_COMPONENT).extras) 468 .setResultValidator(extras -> extras.containsKey(OVERRIDE_THEME_COLOR))); 469 if (resultExtras == null) { 470 fail("No reported override theme color from " + activity); 471 } 472 if (expectedColor > 0) { 473 assertEquals("Override theme color must match", 474 Integer.toHexString(expectedColor), 475 Integer.toHexString(resultExtras.getInt(OVERRIDE_THEME_COLOR))); 476 } 477 mWmState.waitForActivityRemoved(activity); 478 separateTestJournal(); 479 } 480 481 @Test testOverrideSplashscreenTheme()482 public void testOverrideSplashscreenTheme() { 483 assumeFalse(isLeanBack()); 484 final CommandSession.ActivitySession homeActivity = prepareTestLauncher(); 485 486 // Pre-launch the activity to ensure status is cleared on the device 487 startActivityFromTestLauncher(homeActivity, SPLASH_SCREEN_REPLACE_THEME_ACTIVITY, 488 intent -> {}); 489 waitAndAssertOverrideThemeColor(0 /* ignore */); 490 491 // Launch the activity a first time, check that the splashscreen use the default theme, 492 // and override the theme for the next launch 493 startActivityFromTestLauncher(homeActivity, SPLASH_SCREEN_REPLACE_THEME_ACTIVITY, 494 intent -> intent.putExtra(OVERRIDE_THEME_ENABLED, true)); 495 waitAndAssertOverrideThemeColor(Color.BLUE); 496 497 // Launch the activity a second time, check that the theme has been overridden and reset 498 // to the default theme 499 startActivityFromTestLauncher(homeActivity, SPLASH_SCREEN_REPLACE_THEME_ACTIVITY, 500 intent -> {}); 501 waitAndAssertOverrideThemeColor(Color.RED); 502 503 // Launch the activity a third time just to check that the theme has indeed been reset. 504 startActivityFromTestLauncher(homeActivity, SPLASH_SCREEN_REPLACE_THEME_ACTIVITY, 505 intent -> {}); 506 waitAndAssertOverrideThemeColor(Color.BLUE); 507 } 508 } 509