chromium/chrome/test/android/javatests/src/org/chromium/chrome/test/util/FullscreenTestUtils.java

// Copyright 2016 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.test.util;

import android.app.Activity;
import android.os.Build;
import android.view.View;
import android.view.WindowManager;

import androidx.core.view.WindowInsetsCompat;

import org.hamcrest.Matchers;

import org.chromium.base.BuildInfo;
import org.chromium.base.task.PostTask;
import org.chromium.base.task.TaskTraits;
import org.chromium.base.test.util.Criteria;
import org.chromium.base.test.util.CriteriaHelper;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabTestUtils;
import org.chromium.chrome.browser.tab.TabWebContentsDelegateAndroid;

/** Static methods for use in tests that require toggling persistent fullscreen. */
public class FullscreenTestUtils {

    /**
     * Toggles persistent fullscreen for the tab and waits for the fullscreen flag to be set and the
     * tab to enter persistent fullscreen state.
     *
     * @param tab The {@link Tab} to toggle fullscreen on.
     * @param state Whether the tab should be set to fullscreen.
     * @param activity The {@link Activity} owning the tab.
     */
    public static void togglePersistentFullscreenAndAssert(
            final Tab tab, final boolean state, Activity activity) {
        togglePersistentFullscreenAndAssert(tab, state, activity, false);
    }

    /**
     * Toggles persistent fullscreen for the tab and waits for the fullscreen flag to be set and the
     * tab to enter persistent fullscreen state.
     *
     * @param tab The {@link Tab} to toggle fullscreen on.
     * @param state Whether the tab should be set to fullscreen.
     * @param activity The {@link Activity} owning the tab.
     * @param isFullscreenInsetsApiMigrationEnabled Whether the new fullscreen APIs are being used.
     */
    public static void togglePersistentFullscreenAndAssert(
            final Tab tab,
            final boolean state,
            Activity activity,
            boolean isFullscreenInsetsApiMigrationEnabled) {
        togglePersistentFullscreenAndAssert(
                tab, state, activity, false, false, isFullscreenInsetsApiMigrationEnabled);
    }

    /**
     * Toggles persistent fullscreen for the tab and waits for the fullscreen flag to be set and the
     * tab to enter persistent fullscreen state.
     *
     * @param tab The {@link Tab} to toggle fullscreen on.
     * @param state Whether the tab should be set to fullscreen.
     * @param activity The {@link Activity} owning the tab.
     * @param prefersNavigationBar Whether navigation bar should be shown when in fullscreen.
     * @param prefersStatusBar Whether status bar should be shown when in fullscreen.
     */
    public static void togglePersistentFullscreenAndAssert(
            final Tab tab,
            final boolean state,
            Activity activity,
            boolean prefersNavigationBar,
            boolean prefersStatusBar) {
        togglePersistentFullscreenAndAssert(
                tab, state, activity, prefersNavigationBar, prefersStatusBar, false);
    }

    /**
     * Toggles persistent fullscreen for the tab and waits for the fullscreen flag to be set and the
     * tab to enter persistent fullscreen state.
     *
     * @param tab The {@link Tab} to toggle fullscreen on.
     * @param state Whether the tab should be set to fullscreen.
     * @param activity The {@link Activity} owning the tab.
     * @param prefersNavigationBar Whether navigation bar should be shown when in fullscreen.
     * @param prefersStatusBar Whether status bar should be shown when in fullscreen.
     * @param isFullscreenInsetsApiMigrationEnabled Whether the new fullscreen APIs are being used.
     */
    public static void togglePersistentFullscreenAndAssert(
            final Tab tab,
            final boolean state,
            Activity activity,
            boolean prefersNavigationBar,
            boolean prefersStatusBar,
            boolean isFullscreenInsetsApiMigrationEnabled) {
        final TabWebContentsDelegateAndroid delegate = TabTestUtils.getTabWebContentsDelegate(tab);
        FullscreenTestUtils.togglePersistentFullscreen(
                delegate, state, prefersNavigationBar, prefersStatusBar);
        // In order for the status bar to be displayed, the fullscreen flag must not be set.
        // If we are entering fullscreen, then we expect the fullscreen flag state to match
        // negated |prefersStatusBar|:
        if (isFullscreenInsetsApiMigrationEnabled) {
            FullscreenTestUtils.waitForFullscreen(tab, state && !prefersStatusBar);
        } else {
            FullscreenTestUtils.waitForFullscreenFlag(tab, state && !prefersStatusBar, activity);
        }
        FullscreenTestUtils.waitForPersistentFullscreen(delegate, state);
    }

    /**
     * Toggles persistent fullscreen for the tab.
     *
     * @param delegate The {@link TabWebContentsDelegateAndroid} for the tab.
     * @param state Whether the tab should be set to fullscreen.
     */
    public static void togglePersistentFullscreen(
            final TabWebContentsDelegateAndroid delegate, final boolean state) {
        togglePersistentFullscreen(delegate, state, false, false);
    }

    /**
     * Toggles persistent fullscreen for the tab.
     *
     * @param delegate The {@link TabWebContentsDelegateAndroid} for the tab.
     * @param state Whether the tab should be set to fullscreen.
     * @param prefersNavigationBar Whether navigation bar should be shown when in fullscreen.
     * @param prefersStatusBar Whether status bar should be shown when in fullscreen.
     */
    public static void togglePersistentFullscreen(
            final TabWebContentsDelegateAndroid delegate,
            final boolean state,
            boolean prefersNavigationBar,
            boolean prefersStatusBar) {
        PostTask.runOrPostTask(
                TaskTraits.UI_DEFAULT,
                () -> {
                    if (state) {
                        delegate.enterFullscreenModeForTab(prefersNavigationBar, prefersStatusBar);
                    } else {
                        delegate.exitFullscreenModeForTab();
                    }
                });
    }

    /**
     * Waits for the fullscreen flag to be set on the specified {@link Tab}.
     *
     * @param tab The {@link Tab} that is expected to have the flag set.
     * @param state Whether the tab should be to fullscreen.
     * @param activity The {@link Activity} owning the tab.
     */
    public static void waitForFullscreenFlag(
            final Tab tab, final boolean state, final Activity activity) {
        CriteriaHelper.pollUiThread(
                () -> isFullscreenFlagSet(tab, state, activity),
                6000L,
                CriteriaHelper.DEFAULT_POLLING_INTERVAL);
    }

    public static void waitForHideNavigationFlag(
            final Tab tab, final boolean state, final Activity activity) {
        CriteriaHelper.pollUiThread(
                () -> isHideNavigationFlagSet(tab, state, activity),
                6000L,
                CriteriaHelper.DEFAULT_POLLING_INTERVAL);
    }

    public static void waitForFullscreen(final Tab tab, final boolean state) {
        CriteriaHelper.pollUiThread(
                () -> isFullscreenSet(tab, state), 6000L, CriteriaHelper.DEFAULT_POLLING_INTERVAL);
    }

    public static void waitForHideNavigation(final Tab tab, final boolean state) {
        CriteriaHelper.pollUiThread(
                () -> isHideNavigationSet(tab, state),
                6000L,
                CriteriaHelper.DEFAULT_POLLING_INTERVAL);
    }

    /**
     * Waits for the specified {@link Tab} to enter fullscreen. mode
     *
     * @param delegate The {@link TabWebContentsDelegateAndroid} for the tab.
     * @param state Whether the tab should be set to fullscreen.
     */
    public static void waitForPersistentFullscreen(
            final TabWebContentsDelegateAndroid delegate, boolean state) {
        CriteriaHelper.pollUiThread(
                () -> {
                    Criteria.checkThat(delegate.isFullscreenForTabOrPending(), Matchers.is(state));
                });
    }

    private static boolean isFlagSet(int flags, int flag) {
        return (flags & flag) == flag;
    }

    private static boolean isFullscreenSet(final Tab tab, final boolean state) {
        View view = tab.getContentView();
        WindowInsetsCompat windowInsets =
                WindowInsetsCompat.toWindowInsetsCompat(view.getRootWindowInsets(), view);
        return !windowInsets.isVisible(WindowInsetsCompat.Type.statusBars()) == state;
    }

    private static boolean isHideNavigationSet(final Tab tab, final boolean state) {
        View view = tab.getContentView();
        WindowInsetsCompat windowInsets =
                WindowInsetsCompat.toWindowInsetsCompat(view.getRootWindowInsets(), view);
        return !windowInsets.isVisible(WindowInsetsCompat.Type.navigationBars()) == state;
    }

    private static boolean isFullscreenFlagSet(
            final Tab tab, final boolean state, Activity activity) {
        // Status bars persist in fullscreen mode in automotive (see crrev.com/c/4569720) so system
        // UI flags are not set.
        if (BuildInfo.getInstance().isAutomotive) {
            return true;
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            View view = tab.getContentView();
            int visibility = view.getSystemUiVisibility();

            // SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN should only be used during the transition between
            // fullscreen states, so it should always be cleared when fullscreen transitions are
            // completed.
            return (!isFlagSet(visibility, View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN))
                    && (isFlagSet(visibility, View.SYSTEM_UI_FLAG_FULLSCREEN) == state);
        } else {
            WindowManager.LayoutParams attributes = activity.getWindow().getAttributes();
            return isFlagSet(attributes.flags, WindowManager.LayoutParams.FLAG_FULLSCREEN) == state;
        }
    }

    private static boolean isHideNavigationFlagSet(
            final Tab tab, final boolean state, Activity activity) {
        // Status bars persist in fullscreen mode in automotive (see crrev.com/c/4569720) so system
        // UI flags are not set.
        if (BuildInfo.getInstance().isAutomotive) {
            return true;
        }
        View view = tab.getContentView();
        int visibility = view.getSystemUiVisibility();

        return isFlagSet(visibility, View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == state;
    }
}