Commit 71357f78 authored by Peter E Conn's avatar Peter E Conn Committed by Commit Bot

🛃 Allow TWA sites to theme the status bar color.

When on the verified origin in a Trusted Web Activity, the page's theme
color will be used for the status bar. Previously this was ignored and
the toolbar color provided by the client was used. The page's theme
color takes precedence over the toolbar color if provided.

This results in the following logic for the status bar color in TWAs:
- If on the verified origin:
  - If the page has a theme color, use that.
  - Otherwise if the client provided a toolbar color, use that.
  - Otherwise use Chrome's default.
- Otherwise, if not on the verified origin:
  - If the client provided a toolbar color, use that.
  - Otherwise use Chrome's default.

Bug: 964914
Change-Id: Ic41f4cfd1b834fdc9c3444f33dc163de52c9792f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1624332
Commit-Queue: Peter Conn <peconn@chromium.org>
Reviewed-by: default avatarPavel Shmakov <pshmakov@chromium.org>
Cr-Commit-Position: refs/heads/master@{#662553}
parent 4cddf5b6
......@@ -9,10 +9,12 @@ import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controll
import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityOpenTimeRecorder;
import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityToolbarController;
import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityVerifier;
import org.chromium.chrome.browser.browserservices.trustedwebactivityui.controller.TrustedWebActivityVerifier.VerificationStatus;
import org.chromium.chrome.browser.browserservices.trustedwebactivityui.splashscreen.TwaSplashController;
import org.chromium.chrome.browser.browserservices.trustedwebactivityui.view.TrustedWebActivityDisclosureView;
import org.chromium.chrome.browser.browserservices.trustedwebactivityui.view.TrustedWebActivityToolbarView;
import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider;
import org.chromium.chrome.browser.customtabs.CustomTabStatusBarColorProvider;
import org.chromium.chrome.browser.customtabs.content.CustomTabActivityNavigationController;
import org.chromium.chrome.browser.dependency_injection.ActivityScope;
......@@ -37,13 +39,15 @@ public class TrustedWebActivityCoordinator {
CustomTabActivityNavigationController navigationController,
Lazy<TwaSplashController> splashController,
CustomTabIntentDataProvider intentDataProvider,
TrustedWebActivityUmaRecorder umaRecorder) {
TrustedWebActivityUmaRecorder umaRecorder,
CustomTabStatusBarColorProvider statusBarColorProvider) {
// We don't need to do anything with most of the classes above, we just need to resolve them
// so they start working.
navigationController.setLandingPageOnCloseCriterion(verifier::isPageOnVerifiedOrigin);
initSplashScreen(splashController, intentDataProvider, umaRecorder);
useTabThemeColorWhenVerified(statusBarColorProvider, verifier);
}
private void initSplashScreen(Lazy<TwaSplashController> splashController,
......@@ -58,4 +62,15 @@ public class TrustedWebActivityCoordinator {
umaRecorder.recordSplashScreenUsage(showSplashScreen);
}
private void useTabThemeColorWhenVerified(CustomTabStatusBarColorProvider colorProvider,
TrustedWebActivityVerifier verifier) {
verifier.addVerificationObserver(() -> {
if (verifier.getState() == null) return;
@VerificationStatus int status = verifier.getState().status;
colorProvider.setUseTabThemeColor(
status == VerificationStatus.PENDING || status == VerificationStatus.SUCCESS);
});
}
}
......@@ -4,11 +4,15 @@
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.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;
......@@ -21,27 +25,87 @@ public class CustomTabStatusBarColorProvider {
private final Resources mResources;
private final CustomTabIntentDataProvider mIntentDataProvider;
private final CustomTabActivityTabProvider mCustomTabActivityTabProvider;
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) {
CustomTabActivityTabProvider customTabActivityTabProvider,
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;
mStatusBarColorController = statusBarColorController;
mTabThemeColorHelperWrapper = tabThemeColorHelperWrapper;
}
/**
* Sets whether the tab's theme color should be used for the status bar and triggers an update
* of the status bar color if needed.
*/
public void setUseTabThemeColor(boolean useTabThemeColor) {
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));
}
int getBaseStatusBarColor(int fallbackStatusBarColor) {
if (mIntentDataProvider.isOpenedByChrome()) return fallbackStatusBarColor;
Tab tab = mCustomTabActivityTabProvider.getTab();
if (tab!= null && tab.isPreview()) {
return ColorUtils.getDefaultThemeColor(mResources, false);
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();
}
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;
}
}
......@@ -21,6 +21,7 @@ import org.chromium.chrome.browser.snackbar.SnackbarManager;
import org.chromium.chrome.browser.tabmodel.TabCreatorManager;
import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.browser.toolbar.ToolbarManager;
import org.chromium.chrome.browser.ui.system.StatusBarColorController;
import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetController;
import org.chromium.ui.base.ActivityWindowAndroid;
......@@ -131,4 +132,9 @@ public class ChromeActivityCommonsModule {
public TabCreatorManager provideTabCreatorManager() {
return (TabCreatorManager) mActivity;
}
@Provides
public StatusBarColorController provideStatusBarColorController() {
return mActivity.getStatusBarColorController();
}
}
......@@ -5,9 +5,14 @@
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;
......@@ -15,13 +20,13 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
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.tab.Tab;
import org.chromium.chrome.browser.ui.system.StatusBarColorController;
/**
* Tests for {@link CustomTabStatusBarColorProvider}.
......@@ -36,6 +41,10 @@ public class CustomTabStatusBarColorProviderTest {
@Mock public Resources mResources;
@Mock public CustomTabIntentDataProvider mCustomTabIntentDataProvider;
@Mock public CustomTabActivityTabProvider mCustomTabActivityTabProvider;
@Mock public StatusBarColorController mStatusBarColorController;
@Mock public CustomTabStatusBarColorProvider.TabThemeColorHelperWrapper
mTabThemeColorHelperWrapper;
@Mock public Tab mTab;
private CustomTabStatusBarColorProvider mColorProvider;
@Before
......@@ -43,7 +52,8 @@ public class CustomTabStatusBarColorProviderTest {
MockitoAnnotations.initMocks(this);
mColorProvider = new CustomTabStatusBarColorProvider(mResources,
mCustomTabIntentDataProvider, mCustomTabActivityTabProvider);
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
......@@ -53,13 +63,15 @@ public class CustomTabStatusBarColorProviderTest {
when(mResources.getColor(anyInt())).thenReturn(DEFAULT_COLOR);
when(mCustomTabIntentDataProvider.getToolbarColor()).thenReturn(USER_PROVIDED_COLOR);
when(mCustomTabActivityTabProvider.getTab()).thenReturn(mTab);
}
@Test
public void fallsBackWhenOpenedByChrome() {
when(mCustomTabIntentDataProvider.isOpenedByChrome()).thenReturn(true);
Assert.assertEquals(FALLBACK_COLOR, mColorProvider.getBaseStatusBarColor(FALLBACK_COLOR));
Assert.assertEquals(FALLBACK_COLOR, getStatusBarColor());
Assert.assertTrue(mColorProvider.isStatusBarDefaultThemeColor(true));
Assert.assertFalse(mColorProvider.isStatusBarDefaultThemeColor(false));
......@@ -67,18 +79,54 @@ public class CustomTabStatusBarColorProviderTest {
@Test
public void defaultThemeForPreviews() {
Tab tab = Mockito.mock(Tab.class);
when(tab.isPreview()).thenReturn(true);
when(mCustomTabActivityTabProvider.getTab()).thenReturn(tab);
when(mTab.isPreview()).thenReturn(true);
Assert.assertEquals(DEFAULT_COLOR, mColorProvider.getBaseStatusBarColor(FALLBACK_COLOR));
Assert.assertEquals(DEFAULT_COLOR, getStatusBarColor());
Assert.assertFalse(mColorProvider.isStatusBarDefaultThemeColor(true));
}
@Test
public void userProvidedColor() {
Assert.assertEquals(USER_PROVIDED_COLOR,
mColorProvider.getBaseStatusBarColor(FALLBACK_COLOR));
Assert.assertEquals(USER_PROVIDED_COLOR, getStatusBarColor());
Assert.assertFalse(mColorProvider.isStatusBarDefaultThemeColor(true));
}
@Test
public void useTabThemeColor_enable() {
mColorProvider.setUseTabThemeColor(true);
Assert.assertEquals(UNDEFINED_STATUS_BAR_COLOR, getStatusBarColor());
verify(mStatusBarColorController).updateStatusBarColor(anyBoolean());
}
@Test
public void useTabThemeColor_disable() {
mColorProvider.setUseTabThemeColor(true);
Assert.assertEquals(UNDEFINED_STATUS_BAR_COLOR, getStatusBarColor());
verify(mStatusBarColorController).updateStatusBarColor(anyBoolean());
mColorProvider.setUseTabThemeColor(false);
Assert.assertEquals(USER_PROVIDED_COLOR, getStatusBarColor());
verify(mStatusBarColorController, times(2)).updateStatusBarColor(anyBoolean());
}
@Test
public void useTabThemeColor_idempotent() {
mColorProvider.setUseTabThemeColor(true);
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());
}
private int getStatusBarColor() {
return mColorProvider.getBaseStatusBarColor(FALLBACK_COLOR);
}
}
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