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