chromium/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappSplashScreenTest.java

// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.webapps;

import static org.chromium.chrome.browser.ui.system.StatusBarColorController.UNDEFINED_STATUS_BAR_COLOR;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.ColorDrawable;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;

import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;

import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.task.PostTask;
import org.chromium.base.task.TaskTraits;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Feature;
import org.chromium.base.test.util.Restriction;
import org.chromium.chrome.browser.browserservices.intents.BitmapHelper;
import org.chromium.chrome.browser.browserservices.intents.WebappConstants;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabTestUtils;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.R;
import org.chromium.ui.test.util.UiRestriction;

/** Tests for splash screens. */
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
public class WebappSplashScreenTest {
    @Rule public final WebappActivityTestRule mActivityTestRule = new WebappActivityTestRule();

    private int getHistogramTotalCountFor(String histogram, int buckets) {
        int count = 0;

        for (int i = 0; i < buckets; ++i) {
            count += RecordHistogram.getHistogramValueCountForTesting(histogram, i);
        }

        return count;
    }

    private boolean hasHistogramEntry(String histogram, int maxValue) {
        for (int i = 0; i < maxValue; ++i) {
            if (RecordHistogram.getHistogramValueCountForTesting(histogram, i) > 0) {
                return true;
            }
        }
        return false;
    }

    @Test
    @SmallTest
    @Feature({"Webapps"})
    public void testDefaultBackgroundColor() {
        ViewGroup splashScreen = mActivityTestRule.startWebappActivityAndWaitForSplashScreen();
        ColorDrawable background = (ColorDrawable) splashScreen.getBackground();

        Assert.assertEquals(
                mActivityTestRule.getActivity().getColor(R.color.webapp_default_bg),
                background.getColor());
    }

    @Test
    @SmallTest
    @Feature({"StatusBar", "Webapps"})
    @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
    public void testThemeColorWhenNotSpecified() {
        mActivityTestRule.startWebappActivityAndWaitForSplashScreen();

        Tab tab = mActivityTestRule.getActivity().getActivityTab();

        // UNDEFINED_STATUS_BAR_COLOR signals we're using the tab's theme color.
        Assert.assertEquals(
                UNDEFINED_STATUS_BAR_COLOR,
                mActivityTestRule.getActivity().getBaseStatusBarColor(tab));
    }

    @Test
    @SmallTest
    @Feature({"Webapps"})
    public void testHidesAfterFirstPaint() {
        mActivityTestRule.startWebappActivityAndWaitForSplashScreen();
        Assert.assertTrue(mActivityTestRule.isSplashScreenVisible());

        PostTask.runOrPostTask(
                TaskTraits.UI_DEFAULT,
                () ->
                        TabTestUtils.simulateFirstVisuallyNonEmptyPaint(
                                mActivityTestRule.getActivity().getActivityTab()));

        mActivityTestRule.waitUntilSplashscreenHides();
    }

    @Test
    @SmallTest
    @Feature({"Webapps"})
    public void testHidesAfterCrash() {
        mActivityTestRule.startWebappActivityAndWaitForSplashScreen();
        Assert.assertTrue(mActivityTestRule.isSplashScreenVisible());

        PostTask.runOrPostTask(
                TaskTraits.UI_DEFAULT,
                () ->
                        TabTestUtils.simulateCrash(
                                mActivityTestRule.getActivity().getActivityTab(), true));

        mActivityTestRule.waitUntilSplashscreenHides();
    }

    @Test
    @SmallTest
    @Feature({"Webapps"})
    public void testHidesAfterLoadCompletes() {
        mActivityTestRule.startWebappActivityAndWaitForSplashScreen();
        Assert.assertTrue(mActivityTestRule.isSplashScreenVisible());

        PostTask.runOrPostTask(
                TaskTraits.UI_DEFAULT,
                () ->
                        TabTestUtils.simulatePageLoadFinished(
                                mActivityTestRule.getActivity().getActivityTab()));

        mActivityTestRule.waitUntilSplashscreenHides();
    }

    @Test
    @SmallTest
    @Feature({"Webapps"})
    public void testHidesAfterLoadFails() {
        mActivityTestRule.startWebappActivityAndWaitForSplashScreen();
        Assert.assertTrue(mActivityTestRule.isSplashScreenVisible());

        PostTask.runOrPostTask(
                TaskTraits.UI_DEFAULT,
                () ->
                        TabTestUtils.simulatePageLoadFailed(
                                mActivityTestRule.getActivity().getActivityTab(), 0));

        mActivityTestRule.waitUntilSplashscreenHides();
    }

    @Test
    @SmallTest
    @Feature({"Webapps"})
    public void testHidesAfterMultipleEvents() {
        mActivityTestRule.startWebappActivityAndWaitForSplashScreen();
        Assert.assertTrue(mActivityTestRule.isSplashScreenVisible());

        PostTask.runOrPostTask(
                TaskTraits.UI_DEFAULT,
                () -> {
                    Tab tab = mActivityTestRule.getActivity().getActivityTab();

                    TabTestUtils.simulatePageLoadFinished(tab);
                    TabTestUtils.simulatePageLoadFailed(tab, 0);
                    TabTestUtils.simulateFirstVisuallyNonEmptyPaint(tab);
                });

        mActivityTestRule.waitUntilSplashscreenHides();
    }

    @Test
    @SmallTest
    @Feature({"Webapps"})
    public void testRegularSplashScreenAppears() throws Exception {
        // Register a properly-sized icon for the splash screen.
        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
        int thresholdSize =
                context.getResources()
                        .getDimensionPixelSize(R.dimen.webapp_splash_image_size_minimum);
        int size = thresholdSize + 1;
        Bitmap splashBitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
        String bitmapString = BitmapHelper.encodeBitmapAsString(splashBitmap);

        TestFetchStorageCallback callback = new TestFetchStorageCallback();
        WebappRegistry.getInstance().register(WebappActivityTestRule.WEBAPP_ID, callback);
        callback.waitForCallback(0);
        callback.getStorage().updateSplashScreenImage(bitmapString);

        ViewGroup splashScreen = mActivityTestRule.startWebappActivityAndWaitForSplashScreen();
        Assert.assertTrue(mActivityTestRule.isSplashScreenVisible());

        ImageView splashImage = splashScreen.findViewById(R.id.webapp_splash_screen_icon);
        Assert.assertEquals(size, splashImage.getMeasuredWidth());
        Assert.assertEquals(size, splashImage.getMeasuredHeight());

        TextView splashText = splashScreen.findViewById(R.id.webapp_splash_screen_name);
        int[] rules = ((RelativeLayout.LayoutParams) splashText.getLayoutParams()).getRules();
        Assert.assertEquals(RelativeLayout.TRUE, rules[RelativeLayout.ALIGN_PARENT_BOTTOM]);
        Assert.assertEquals(0, rules[RelativeLayout.BELOW]);
    }

    @Test
    @SmallTest
    @Feature({"Webapps"})
    public void testSplashScreenWithoutImageAppears() throws Exception {
        // Register an image that's too small for the splash screen.
        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
        int size =
                context.getResources()
                                .getDimensionPixelSize(R.dimen.webapp_splash_image_size_minimum)
                        - 1;
        Bitmap splashBitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
        String bitmapString = BitmapHelper.encodeBitmapAsString(splashBitmap);

        TestFetchStorageCallback callback = new TestFetchStorageCallback();
        WebappRegistry.getInstance().register(WebappActivityTestRule.WEBAPP_ID, callback);
        callback.waitForCallback(0);
        callback.getStorage().updateSplashScreenImage(bitmapString);

        ViewGroup splashScreen =
                mActivityTestRule.startWebappActivityAndWaitForSplashScreen(
                        mActivityTestRule
                                .createIntent()
                                .putExtra(WebappConstants.EXTRA_IS_ICON_GENERATED, true));
        Assert.assertTrue(mActivityTestRule.isSplashScreenVisible());

        // There's no icon displayed.
        ImageView splashImage = splashScreen.findViewById(R.id.webapp_splash_screen_icon);
        Assert.assertNull(splashImage);

        View spacer = splashScreen.findViewById(R.id.webapp_splash_space);
        Assert.assertNotNull(spacer);

        // The web app name is anchored to the top of the spacer.
        TextView splashText = splashScreen.findViewById(R.id.webapp_splash_screen_name);
        int[] rules = ((RelativeLayout.LayoutParams) splashText.getLayoutParams()).getRules();
        Assert.assertEquals(0, rules[RelativeLayout.ALIGN_PARENT_BOTTOM]);
        Assert.assertEquals(0, rules[RelativeLayout.BELOW]);
        Assert.assertEquals(R.id.webapp_splash_space, rules[RelativeLayout.ABOVE]);
    }

    @Test
    @SmallTest
    @Feature({"Webapps"})
    public void testSplashScreenAppearsWithoutRegisteredSplashImage() {
        // Don't register anything for the web app, which represents apps that were added to the
        // home screen before splash screen images were downloaded.
        ViewGroup splashScreen = mActivityTestRule.startWebappActivityAndWaitForSplashScreen();
        Assert.assertTrue(mActivityTestRule.isSplashScreenVisible());

        // There's no icon displayed.
        ImageView splashImage = splashScreen.findViewById(R.id.webapp_splash_screen_icon);
        Assert.assertNull(splashImage);

        View spacer = splashScreen.findViewById(R.id.webapp_splash_space);
        Assert.assertNotNull(spacer);

        // The web app name is anchored to the top of the spacer.
        TextView splashText = splashScreen.findViewById(R.id.webapp_splash_screen_name);
        int[] rules = ((RelativeLayout.LayoutParams) splashText.getLayoutParams()).getRules();
        Assert.assertEquals(0, rules[RelativeLayout.ALIGN_PARENT_BOTTOM]);
        Assert.assertEquals(0, rules[RelativeLayout.BELOW]);
        Assert.assertEquals(0, rules[RelativeLayout.CENTER_IN_PARENT]);
        Assert.assertEquals(R.id.webapp_splash_space, rules[RelativeLayout.ABOVE]);
    }

    @Test
    @SmallTest
    @Feature({"Webapps"})
    public void testSplashScreenWithSynchronousLayoutInflation() {
        WebappActivity.setOverrideCoreCountForTesting(2);

        mActivityTestRule.startWebappActivityAndWaitForSplashScreen();
        Assert.assertTrue(mActivityTestRule.isSplashScreenVisible());
        Assert.assertTrue(mActivityTestRule.getActivity().isInitialLayoutInflationComplete());
    }
}