Commit e0cfe5c5 authored by Peter Kotwicz's avatar Peter Kotwicz Committed by Commit Bot

[Android TWA] Fix status bar colour in night mode

This CL changes the status bar colour used by TWAs when in night mode.
This CL makes TWAs use the default theme colour instead of the toolbar
colour provided in the TWA launch intent.

This CL also:
- Adds TabThemeColorHelper#isDefaultColorForced() in order to
discriminate between reasons why
TabThemeColorHelper#isDefaultColorUsed() returns true:
  - Using the default theme colour because using the default theme
    colour is forced (e.g. previews, night mode)
  - Using the default theme colour because the web contents do not
    specify a custom theme colour
- Adds TabObserver#onDidChangeForcedThemeColor() to listen to changes
  in TabThemeColorHelper#isDefaultColorForced().

BUG=997796

Change-Id: I490e8ebe6c28b4dbe3b01112acd327e307efedb8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1795571Reviewed-by: default avatarTheresa  <twellington@chromium.org>
Reviewed-by: default avatarMatthew Jones <mdjones@chromium.org>
Reviewed-by: default avatarPavel Shmakov <pshmakov@chromium.org>
Commit-Queue: Peter Kotwicz <pkotwicz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#707966}
parent f7186db5
......@@ -131,6 +131,7 @@ import org.chromium.chrome.browser.sync.ProfileSyncService;
import org.chromium.chrome.browser.sync.SyncController;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabBrowserControlsState;
import org.chromium.chrome.browser.tab.TabState;
import org.chromium.chrome.browser.tabmodel.AsyncTabParamsManager;
import org.chromium.chrome.browser.tabmodel.EmptyTabModel;
import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
......@@ -882,6 +883,18 @@ public abstract class ChromeActivity<C extends ChromeActivityComponent>
return mStatusBarColorController;
}
/**
* Returns theme color which should be used when:
* - Web page does not provide a custom theme color.
* AND
* - Browser is in a state where it can be themed (no intersitial showing etc.)
* {@link TabState#UNSPECIFIED_THEME_COLOR} should be returned if the activity should use the
* default color in this scenario.
*/
public int getActivityThemeColor() {
return TabState.UNSPECIFIED_THEME_COLOR;
}
@Override
public int getBaseStatusBarColor() {
return StatusBarColorController.UNDEFINED_STATUS_BAR_COLOR;
......
......@@ -63,10 +63,10 @@ import org.chromium.chrome.browser.night_mode.PowerSavingModeMonitor;
import org.chromium.chrome.browser.night_mode.SystemNightModeMonitor;
import org.chromium.chrome.browser.page_info.PageInfoController;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabState;
import org.chromium.chrome.browser.tabmodel.ChromeTabCreator;
import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.browser.tabmodel.TabModelSelectorImpl;
import org.chromium.chrome.browser.toolbar.ToolbarColors;
import org.chromium.chrome.browser.usage_stats.UsageStatsService;
import org.chromium.chrome.browser.util.IntentUtils;
import org.chromium.content_public.browser.LoadUrlParams;
......@@ -188,8 +188,7 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
@Override
public void performPostInflationStartup() {
super.performPostInflationStartup();
getStatusBarColorController().updateStatusBarColor(ToolbarColors.isUsingDefaultToolbarColor(
getResources(), false, getBaseStatusBarColor()));
getStatusBarColorController().updateStatusBarColor(mTabProvider.getTab());
// Properly attach tab's InfoBarContainer to the view hierarchy if the tab is already
// attached to a ChromeActivity, as the main tab might have been initialized prior to
......@@ -472,6 +471,15 @@ public class CustomTabActivity extends ChromeActivity<CustomTabActivityComponent
this, getTabModelSelector(), R.id.bookmark_this_page_id, R.id.preferences_id);
}
@Override
public int getActivityThemeColor() {
if (mIntentDataProvider.isOpenedByChrome()) {
return TabState.UNSPECIFIED_THEME_COLOR;
}
return mIntentDataProvider.getToolbarColor();
}
@Override
public int getBaseStatusBarColor() {
return mCustomTabStatusBarColorProvider
......
......@@ -6,14 +6,9 @@ package org.chromium.chrome.browser.customtabs;
import static org.chromium.chrome.browser.ui.system.StatusBarColorController.UNDEFINED_STATUS_BAR_COLOR;
import android.content.res.Resources;
import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabProvider;
import org.chromium.chrome.browser.ActivityTabProvider;
import org.chromium.chrome.browser.dependency_injection.ActivityScope;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabThemeColorHelper;
import org.chromium.chrome.browser.ui.system.StatusBarColorController;
import org.chromium.chrome.browser.util.ColorUtils;
import javax.inject.Inject;
......@@ -22,43 +17,19 @@ import javax.inject.Inject;
*/
@ActivityScope
public class CustomTabStatusBarColorProvider {
private final Resources mResources;
private final CustomTabIntentDataProvider mIntentDataProvider;
private final CustomTabActivityTabProvider mCustomTabActivityTabProvider;
private final ActivityTabProvider mActivityTabProvider;
private final StatusBarColorController mStatusBarColorController;
private final TabThemeColorHelperWrapper mTabThemeColorHelperWrapper;
private boolean mUseTabThemeColor;
private boolean mCachedIsFallbackColorDefault;
/** Wrapper class to help unit testing. */
static class TabThemeColorHelperWrapper {
boolean isDefaultColorUsed(Tab tab) {
return TabThemeColorHelper.isDefaultColorUsed(tab);
}
}
/** Constructor for production use. */
@Inject
public CustomTabStatusBarColorProvider(Resources resources,
CustomTabIntentDataProvider intentDataProvider,
CustomTabActivityTabProvider customTabActivityTabProvider,
public CustomTabStatusBarColorProvider(CustomTabIntentDataProvider intentDataProvider,
ActivityTabProvider activityTabProvider,
StatusBarColorController statusBarColorController) {
this(resources, intentDataProvider, customTabActivityTabProvider, statusBarColorController,
new TabThemeColorHelperWrapper());
}
/** Constructor for unit testing that allows using a custom TabThemeColorHelperWrapper. */
public CustomTabStatusBarColorProvider(Resources resources,
CustomTabIntentDataProvider intentDataProvider,
CustomTabActivityTabProvider customTabActivityTabProvider,
StatusBarColorController statusBarColorController,
TabThemeColorHelperWrapper tabThemeColorHelperWrapper) {
mResources = resources;
mIntentDataProvider = intentDataProvider;
mCustomTabActivityTabProvider = customTabActivityTabProvider;
mActivityTabProvider = activityTabProvider;
mStatusBarColorController = statusBarColorController;
mTabThemeColorHelperWrapper = tabThemeColorHelperWrapper;
}
/**
......@@ -69,50 +40,18 @@ public class CustomTabStatusBarColorProvider {
if (mUseTabThemeColor == useTabThemeColor) return;
mUseTabThemeColor = useTabThemeColor;
// We keep the last value that {@link #isStatusBarDefaultThemeColor} was called with so we
// can use it here. Getting the live value would be somewhat complicated - we'd need to call
// isStatusBarDefaultThemeColor on the ChromeActivity, not the CustomTabActivity. At the
// time of writing I don't believe this is worth the complexity as:
// - ChromeActivity#isStatusBarDefaultThemeColor always returns false.
// - Our isStatusBarDefaultThemeColor method only uses the fallback color when the custom
// tab is opened by Chrome, and we don't call setUseTabThemeColor in this case.
mStatusBarColorController.updateStatusBarColor(
isStatusBarDefaultThemeColor(mCachedIsFallbackColorDefault));
mStatusBarColorController.updateStatusBarColor(mActivityTabProvider.get());
}
int getBaseStatusBarColor(int fallbackStatusBarColor) {
if (mIntentDataProvider.isOpenedByChrome()) return fallbackStatusBarColor;
Tab tab = mCustomTabActivityTabProvider.getTab();
if (tab != null) {
if (tab.isPreview()) return ColorUtils.getDefaultThemeColor(mResources, false);
// Returning the color as undefined causes the Tab's theme color to be used.
if (mUseTabThemeColor && !mTabThemeColorHelperWrapper.isDefaultColorUsed(tab)) {
return UNDEFINED_STATUS_BAR_COLOR;
}
}
return mIntentDataProvider.getToolbarColor();
return mActivityTabProvider.get() != null && mUseTabThemeColor
? UNDEFINED_STATUS_BAR_COLOR
: mIntentDataProvider.getToolbarColor();
}
boolean isStatusBarDefaultThemeColor(boolean isFallbackColorDefault) {
mCachedIsFallbackColorDefault = isFallbackColorDefault;
if (mIntentDataProvider.isOpenedByChrome()) return isFallbackColorDefault;
if (mUseTabThemeColor) {
Tab tab = mCustomTabActivityTabProvider.getTab();
if (tab != null) return mTabThemeColorHelperWrapper.isDefaultColorUsed(tab);
}
return false;
}
/**
* Called when toolbar color is changed so that the status bar can adapt.
*/
public void onToolbarColorChanged() {
mStatusBarColorController.updateStatusBarColor(false);
return mIntentDataProvider.isOpenedByChrome() && isFallbackColorDefault;
}
}
......@@ -176,11 +176,10 @@ public class CustomTabToolbarCoordinator implements InflationObserver {
}
/**
* Updates the color of the Activity's status bar and the CCT Toolbar. When a preview is
* shown, it should be reset to the default color. If the user later navigates away from
* that preview to a non-preview page, reset the color back to the original. This does
* not interfere with site-specific theme colors which are disabled when a preview is
* being shown.
* Updates the color of the Activity's CCT Toolbar. When a preview is shown, it should
* be reset to the default color. If the user later navigates away from that preview to
* a non-preview page, reset the color back to the original. This does not interfere
* with site-specific theme colors which are disabled when a preview is being shown.
*/
private void updateColor(Tab tab) {
ToolbarManager manager = mToolbarManager.get();
......@@ -204,7 +203,6 @@ public class CustomTabToolbarCoordinator implements InflationObserver {
mOriginalColor = 0;
}
mStatusBarColorProvider.onToolbarColorChanged();
manager.setShouldUpdateToolbarPrimaryColor(shouldUpdateOriginal);
}
});
......
......@@ -362,9 +362,9 @@ public class TabState {
tabState.tabLaunchTypeAtCreation = tab.getLaunchTypeAtInitialTabCreation();
// Don't save the actual default theme color because it could change on night mode state
// changed.
tabState.themeColor = TabThemeColorHelper.isDefaultColorUsed(tab)
? TabState.UNSPECIFIED_THEME_COLOR
: TabThemeColorHelper.getColor(tab);
tabState.themeColor = TabThemeColorHelper.isUsingColorFromTabContents(tab)
? TabThemeColorHelper.getColor(tab)
: TabState.UNSPECIFIED_THEME_COLOR;
tabState.rootId = tab.getRootId();
return tabState;
}
......
......@@ -35,6 +35,12 @@ public class TabThemeColorHelper extends EmptyTabObserver implements UserData {
/** Whether or not the default color is used. */
private boolean mIsDefaultColorUsed;
/**
* Whether or not the color provided by the web page is used. False if the web page does not
* provide a custom theme color.
*/
private boolean mIsUsingColorFromTabContents;
public static void createForTab(Tab tab) {
assert get(tab) == null;
tab.getUserDataHost().setUserData(USER_DATA_KEY, new TabThemeColorHelper(tab));
......@@ -60,6 +66,11 @@ public class TabThemeColorHelper extends EmptyTabObserver implements UserData {
return get(tab).mIsDefaultColorUsed;
}
/** @return Whether the color provided by the web page is used for the specified {@link Tab}. */
public static boolean isUsingColorFromTabContents(Tab tab) {
return get(tab).mIsUsingColorFromTabContents;
}
/** @return Whether background color of the specified {@link Tab}. */
public static int getBackgroundColor(Tab tab) {
return get(tab).getBackgroundColor();
......@@ -68,9 +79,13 @@ public class TabThemeColorHelper extends EmptyTabObserver implements UserData {
private TabThemeColorHelper(Tab tab) {
mTab = tab;
mDefaultColor = calculateDefaultColor();
mColor = calculateThemeColor(false);
mIsDefaultColorUsed = true;
mIsUsingColorFromTabContents = false;
mColor = mDefaultColor;
updateDefaultBackgroundColor();
tab.addObserver(this);
updateThemeColor(false);
}
private void updateDefaultColor() {
......@@ -89,46 +104,61 @@ public class TabThemeColorHelper extends EmptyTabObserver implements UserData {
}
/**
* Calculate the theme color based on if the page is native, the theme color changed, etc.
* Updates the theme color based on if the page is native, the theme color changed, etc.
* @param didWebContentsThemeColorChange If the theme color of the web contents is known to have
* changed.
* @return The theme color that should be used for this tab.
*/
private int calculateThemeColor(boolean didWebContentsThemeColorChange) {
// If default color is not used, start by assuming the current theme color is the one that
// should be used. This will either be transparent, the last theme color, or the color
// restored from TabState.
int themeColor = mIsDefaultColorUsed ? getDefaultColor() : mColor;
private void updateThemeColor(boolean didWebContentsThemeColorChange) {
// Start by assuming the current theme color is the one that should be used. This will
// either be transparent, the last theme color, or the color restored from TabState.
int themeColor = mColor;
// Only use the web contents for the theme color if it is known to have changed, This
// Only use the web contents for the theme color if it is known to have changed. This
// corresponds to the didChangeThemeColor in WebContentsObserver.
if (mTab.getWebContents() != null && didWebContentsThemeColorChange) {
themeColor = mTab.getWebContents().getThemeColor();
if (!ColorUtils.isValidThemeColor(themeColor)) {
themeColor = TabState.UNSPECIFIED_THEME_COLOR;
} else {
mIsUsingColorFromTabContents = themeColor != TabState.UNSPECIFIED_THEME_COLOR
&& ColorUtils.isValidThemeColor(themeColor);
if (mIsUsingColorFromTabContents) {
mIsDefaultColorUsed = false;
}
}
// Do not apply the theme color if there are any security issues on the page.
final int securityLevel = mTab.getSecurityLevel();
if (securityLevel == ConnectionSecurityLevel.DANGEROUS
|| securityLevel == ConnectionSecurityLevel.SECURE_WITH_POLICY_INSTALLED_CERT
|| (mTab.getActivity() != null && mTab.getActivity().isTablet())
|| (mTab.getActivity() != null
&& mTab.getActivity().getNightModeStateProvider().isInNightMode())
|| mTab.isNativePage() || mTab.isShowingInterstitialPage()
|| themeColor == TabState.UNSPECIFIED_THEME_COLOR || mTab.isIncognito()
|| mTab.isPreview()) {
themeColor = getDefaultColor();
boolean isThemingAllowed = checkThemingAllowed();
if (!isThemingAllowed) {
mIsUsingColorFromTabContents = false;
}
if (!mIsUsingColorFromTabContents) {
themeColor = mDefaultColor;
mIsDefaultColorUsed = true;
if (mTab.getActivity() != null && isThemingAllowed) {
int customThemeColor = mTab.getActivity().getActivityThemeColor();
if (customThemeColor != TabState.UNSPECIFIED_THEME_COLOR) {
themeColor = customThemeColor;
mIsDefaultColorUsed = false;
}
}
}
// Ensure there is no alpha component to the theme color as that is not supported in the
// dependent UI.
themeColor |= 0xFF000000;
return themeColor;
mColor = themeColor | 0xFF000000;
}
/**
* Returns whether theming the activity is allowed (either by the web contents or by the
* activity).
*/
private boolean checkThemingAllowed() {
// Do not apply the theme color if there are any security issues on the page.
final int securityLevel = mTab.getSecurityLevel();
return securityLevel != ConnectionSecurityLevel.DANGEROUS
&& securityLevel != ConnectionSecurityLevel.SECURE_WITH_POLICY_INSTALLED_CERT
&& (mTab.getActivity() == null || !mTab.getActivity().isTablet())
&& (mTab.getActivity() == null
|| !mTab.getActivity().getNightModeStateProvider().isInNightMode())
&& !mTab.isNativePage() && !mTab.isShowingInterstitialPage() && !mTab.isIncognito()
&& !mTab.isPreview();
}
/**
......@@ -137,10 +167,10 @@ public class TabThemeColorHelper extends EmptyTabObserver implements UserData {
* changed.
*/
public void updateIfNeeded(boolean didWebContentsThemeColorChange) {
int themeColor = calculateThemeColor(didWebContentsThemeColorChange);
if (themeColor == mColor) return;
mColor = themeColor;
mTab.notifyThemeColorChanged(themeColor);
int oldThemeColor = mColor;
updateThemeColor(didWebContentsThemeColorChange);
if (oldThemeColor == mColor) return;
mTab.notifyThemeColorChanged(mColor);
}
/**
......@@ -182,7 +212,8 @@ public class TabThemeColorHelper extends EmptyTabObserver implements UserData {
if (tabState == null) return;
// Update from TabState.
mIsDefaultColorUsed = !tabState.hasThemeColor();
mIsUsingColorFromTabContents = tabState.hasThemeColor();
mIsDefaultColorUsed = !mIsUsingColorFromTabContents;
mColor = mIsDefaultColorUsed ? getDefaultColor() : tabState.getThemeColor();
updateIfNeeded(false);
}
......
......@@ -237,7 +237,7 @@ public class StatusBarColorController
* @param tab The tab that is currently showing, used to determine whether {@code color} is the
* default theme color.
*/
private void updateStatusBarColor(@Nullable Tab tab) {
public void updateStatusBarColor(@Nullable Tab tab) {
setStatusBarColor(calculateBaseStatusBarColor(), isDefaultThemeColor(tab));
}
......
......@@ -5,16 +5,12 @@
package org.chromium.chrome.browser.customtabs;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.chromium.chrome.browser.ui.system.StatusBarColorController.UNDEFINED_STATUS_BAR_COLOR;
import android.content.res.Resources;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
......@@ -24,7 +20,7 @@ import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabProvider;
import org.chromium.chrome.browser.ActivityTabProvider;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.ui.system.StatusBarColorController;
......@@ -38,12 +34,10 @@ public class CustomTabStatusBarColorProviderTest {
private static final int FALLBACK_COLOR = 0x55667788;
private static final int USER_PROVIDED_COLOR = 0x99aabbcc;
@Mock public Resources mResources;
@Mock public CustomTabIntentDataProvider mCustomTabIntentDataProvider;
@Mock public CustomTabActivityTabProvider mCustomTabActivityTabProvider;
@Mock
public ActivityTabProvider mActivityTabProvider;
@Mock public StatusBarColorController mStatusBarColorController;
@Mock public CustomTabStatusBarColorProvider.TabThemeColorHelperWrapper
mTabThemeColorHelperWrapper;
@Mock public Tab mTab;
private CustomTabStatusBarColorProvider mColorProvider;
......@@ -51,20 +45,12 @@ public class CustomTabStatusBarColorProviderTest {
public void setUp() {
MockitoAnnotations.initMocks(this);
mColorProvider = new CustomTabStatusBarColorProvider(mResources,
mCustomTabIntentDataProvider, mCustomTabActivityTabProvider,
mStatusBarColorController, mTabThemeColorHelperWrapper);
// The color is accessed through ApiCompatibilityUtils which calls either
// Resources#getColor(int, Theme) or Resources#getColor(int) depending on the Android
// version. We mock out both calls so things don't break if we change the Android version
// the tests are run with.
when(mResources.getColor(anyInt(), any())).thenReturn(DEFAULT_COLOR);
when(mResources.getColor(anyInt())).thenReturn(DEFAULT_COLOR);
mColorProvider = new CustomTabStatusBarColorProvider(
mCustomTabIntentDataProvider, mActivityTabProvider, mStatusBarColorController);
when(mCustomTabIntentDataProvider.getToolbarColor()).thenReturn(USER_PROVIDED_COLOR);
when(mCustomTabActivityTabProvider.getTab()).thenReturn(mTab);
when(mActivityTabProvider.get()).thenReturn(mTab);
}
@Test
......@@ -77,14 +63,6 @@ public class CustomTabStatusBarColorProviderTest {
Assert.assertFalse(mColorProvider.isStatusBarDefaultThemeColor(false));
}
@Test
public void defaultThemeForPreviews() {
when(mTab.isPreview()).thenReturn(true);
Assert.assertEquals(DEFAULT_COLOR, getStatusBarColor());
Assert.assertFalse(mColorProvider.isStatusBarDefaultThemeColor(true));
}
@Test
public void userProvidedColor() {
Assert.assertEquals(USER_PROVIDED_COLOR, getStatusBarColor());
......@@ -95,18 +73,18 @@ public class CustomTabStatusBarColorProviderTest {
public void useTabThemeColor_enable() {
mColorProvider.setUseTabThemeColor(true);
Assert.assertEquals(UNDEFINED_STATUS_BAR_COLOR, getStatusBarColor());
verify(mStatusBarColorController).updateStatusBarColor(anyBoolean());
verify(mStatusBarColorController).updateStatusBarColor(any(Tab.class));
}
@Test
public void useTabThemeColor_disable() {
mColorProvider.setUseTabThemeColor(true);
Assert.assertEquals(UNDEFINED_STATUS_BAR_COLOR, getStatusBarColor());
verify(mStatusBarColorController).updateStatusBarColor(anyBoolean());
verify(mStatusBarColorController).updateStatusBarColor(any(Tab.class));
mColorProvider.setUseTabThemeColor(false);
Assert.assertEquals(USER_PROVIDED_COLOR, getStatusBarColor());
verify(mStatusBarColorController, times(2)).updateStatusBarColor(anyBoolean());
verify(mStatusBarColorController, times(2)).updateStatusBarColor(any(Tab.class));
}
@Test
......@@ -115,15 +93,7 @@ public class CustomTabStatusBarColorProviderTest {
mColorProvider.setUseTabThemeColor(true);
Assert.assertEquals(UNDEFINED_STATUS_BAR_COLOR, getStatusBarColor());
verify(mStatusBarColorController).updateStatusBarColor(anyBoolean());
}
@Test
public void useTabThemeColor_notIfTabHasDefaultColor() {
when(mTabThemeColorHelperWrapper.isDefaultColorUsed(any())).thenReturn(true);
mColorProvider.setUseTabThemeColor(true);
Assert.assertEquals(USER_PROVIDED_COLOR, getStatusBarColor());
verify(mStatusBarColorController).updateStatusBarColor(any(Tab.class));
}
private int getStatusBarColor() {
......
<!DOCTYPE html>
<html>
<head>
<title>Test page</title>
<meta name="theme-color" content="#0000ff">
</head>
<body>
Chromium theme colors2!
</body>
</html>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment