Commit f3f4b4d2 authored by Pedro Amaral's avatar Pedro Amaral Committed by Commit Bot

Add theming to the bottom toolbar

Make the bottom toolbar take on the theme color. Also have the bottom
toolbar buttons become light/dark depending on theme color.

The main complexity of this patch arises from having to support two
different ways for theming button icons. Some icons are tintable
and in that case we can just set their tint to light or dark. Other
icons aren't tintable and have 2 separate drawables and swap them out
depending on the theme.

There are a still an issue this patch doesn't address. If a site is themed
and then we go to tab switcher mode the bottom toolbar will retain the
original site's theming. To solve this we'll need a sane theme provider.


Bug: 852140

Change-Id: I3ec960c1e5c7d83b663ee97101d6a11e528e4f95
Reviewed-on: https://chromium-review.googlesource.com/1166291
Commit-Queue: Pedro Amaral <amaralp@chromium.org>
Reviewed-by: default avatarTed Choc <tedchoc@chromium.org>
Reviewed-by: default avatarMatthew Jones <mdjones@chromium.org>
Cr-Commit-Position: refs/heads/master@{#583769}
parent 8db7d0d6
......@@ -4,6 +4,8 @@
package org.chromium.chrome.browser.toolbar;
import android.content.res.ColorStateList;
import android.support.v7.content.res.AppCompatResources;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
......@@ -21,6 +23,7 @@ import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.browser.toolbar.BottomToolbarViewBinder.ViewHolder;
import org.chromium.chrome.browser.toolbar.ToolbarButtonSlotData.ToolbarButtonData;
import org.chromium.chrome.browser.util.ColorUtils;
import org.chromium.ui.base.WindowAndroid;
import org.chromium.ui.resources.ResourceManager;
......@@ -40,11 +43,17 @@ public class BottomToolbarCoordinator {
/** The menu button that lives in the bottom toolbar. */
private final MenuButton mMenuButton;
/** The light mode tint to be used in bottom toolbar buttons. */
private final ColorStateList mLightModeTint;
/** The dark mode tint to be used in bottom toolbar buttons. */
private final ColorStateList mDarkModeTint;
/**
* Build the coordinator that manages the bottom toolbar.
* @param fullscreenManager A {@link ChromeFullscreenManager} to update the bottom controls
* height for the renderer.
* @param root The root {@link ViewGroup} for locating the vies to inflate.
* @param root The root {@link ViewGroup} for locating the views to inflate.
* @param firstSlotData The data required to fill in the leftmost bottom toolbar button slot.
* @param secondSlotData The data required to fill in the second bottom toolbar button slot.
*/
......@@ -71,6 +80,11 @@ public class BottomToolbarCoordinator {
mTabSwitcherButtonCoordinator = new TabSwitcherButtonCoordinator(toolbarRoot);
mMenuButton = toolbarRoot.findViewById(R.id.menu_button_wrapper);
mLightModeTint =
AppCompatResources.getColorStateList(root.getContext(), R.color.light_mode_tint);
mDarkModeTint =
AppCompatResources.getColorStateList(root.getContext(), R.color.dark_mode_tint);
}
/**
......@@ -149,6 +163,15 @@ public class BottomToolbarCoordinator {
return mMenuButton.getMenuButton();
}
public void setPrimaryColor(int color) {
mMediator.setPrimaryColor(color);
final boolean useLight = ColorUtils.shouldUseLightForegroundOnBackground(color);
final ColorStateList tint = useLight ? mLightModeTint : mDarkModeTint;
mTabSwitcherButtonCoordinator.setTint(tint);
mMenuButton.setTint(tint);
}
/**
* Clean up any state when the bottom toolbar is destroyed.
*/
......
......@@ -247,4 +247,8 @@ class BottomToolbarMediator implements ContextualSearchObserver, FullscreenListe
mSecondSlotData.tabSwitcherModeButtonData);
}
}
void setPrimaryColor(int color) {
mModel.setValue(BottomToolbarModel.PRIMARY_COLOR, color);
}
}
......@@ -47,10 +47,13 @@ public class BottomToolbarModel extends PropertyModel {
public static final ObjectPropertyKey<ToolbarButtonData> SECOND_BUTTON_DATA =
new ObjectPropertyKey<>();
/** Primary color of bottom toolbar. */
public static final IntPropertyKey PRIMARY_COLOR = new IntPropertyKey();
/** Default constructor. */
public BottomToolbarModel() {
super(Y_OFFSET, ANDROID_VIEW_VISIBLE, COMPOSITED_VIEW_VISIBLE, LAYOUT_MANAGER,
TOOLBAR_SWIPE_LAYOUT, RESOURCE_MANAGER, TOOLBAR_SWIPE_HANDLER, FIRST_BUTTON_DATA,
SECOND_BUTTON_DATA);
SECOND_BUTTON_DATA, PRIMARY_COLOR);
}
}
......@@ -12,6 +12,7 @@ import org.chromium.chrome.browser.compositor.scene_layer.ScrollingBottomViewSce
import org.chromium.chrome.browser.modelutil.PropertyKey;
import org.chromium.chrome.browser.modelutil.PropertyModelChangeProcessor;
import org.chromium.chrome.browser.toolbar.ToolbarButtonSlotData.ToolbarButtonData;
import org.chromium.chrome.browser.util.ColorUtils;
import org.chromium.chrome.browser.widget.TintedImageButton;
/**
......@@ -89,21 +90,40 @@ public class BottomToolbarViewBinder
model.getValue(BottomToolbarModel.TOOLBAR_SWIPE_HANDLER));
} else if (BottomToolbarModel.FIRST_BUTTON_DATA == propertyKey) {
updateButton(view.firstTintedImageButton,
model.getValue(BottomToolbarModel.FIRST_BUTTON_DATA));
model.getValue(BottomToolbarModel.FIRST_BUTTON_DATA), useLightIcons(model));
} else if (BottomToolbarModel.SECOND_BUTTON_DATA == propertyKey) {
updateButton(view.secondTintedImageButton,
model.getValue(BottomToolbarModel.SECOND_BUTTON_DATA));
model.getValue(BottomToolbarModel.SECOND_BUTTON_DATA), useLightIcons(model));
} else if (BottomToolbarModel.PRIMARY_COLOR == propertyKey) {
final boolean useLightIcons = useLightIcons(model);
view.toolbarRoot.findViewById(R.id.bottom_sheet_toolbar)
.setBackgroundColor(model.getValue(BottomToolbarModel.PRIMARY_COLOR));
updateButtonDrawable(view.firstTintedImageButton,
model.getValue(BottomToolbarModel.FIRST_BUTTON_DATA), useLightIcons);
updateButtonDrawable(view.secondTintedImageButton,
model.getValue(BottomToolbarModel.SECOND_BUTTON_DATA), useLightIcons);
} else {
assert false : "Unhandled property detected in BottomToolbarViewBinder!";
}
}
private static void updateButton(TintedImageButton button, ToolbarButtonData buttonData) {
private static boolean useLightIcons(BottomToolbarModel model) {
return ColorUtils.shouldUseLightForegroundOnBackground(
model.getValue(BottomToolbarModel.PRIMARY_COLOR));
}
private static void updateButton(
TintedImageButton button, ToolbarButtonData buttonData, boolean useLightIcons) {
if (buttonData == null) {
button.setVisibility(View.INVISIBLE);
} else {
buttonData.updateButton(button);
buttonData.updateButton(button, useLightIcons);
button.setVisibility(View.VISIBLE);
}
}
private static void updateButtonDrawable(
TintedImageButton button, ToolbarButtonData buttonData, boolean useLightIcons) {
if (buttonData != null) buttonData.updateButtonDrawable(button, useLightIcons);
}
}
......@@ -5,19 +5,21 @@
package org.chromium.chrome.browser.toolbar;
import android.content.Context;
import android.content.res.ColorStateList;
import android.util.AttributeSet;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.FrameLayout;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.widget.TintedImageButton;
/**
* The overflow menu button.
*/
class MenuButton extends FrameLayout {
/** The view for the menu button. */
private View mMenuButtonView;
/** The {@link TintedImageButton} for the menu button. */
private TintedImageButton mMenuTintedImageButton;
/** The view for the update badge. */
private View mUpdateBadgeView;
......@@ -29,7 +31,7 @@ class MenuButton extends FrameLayout {
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mMenuButtonView = findViewById(R.id.menu_button);
mMenuTintedImageButton = findViewById(R.id.menu_button);
mUpdateBadgeView = findViewById(R.id.menu_badge);
}
......@@ -38,7 +40,7 @@ class MenuButton extends FrameLayout {
* clicked.
*/
void setTouchListener(OnTouchListener onTouchListener) {
mMenuButtonView.setOnTouchListener(onTouchListener);
mMenuTintedImageButton.setOnTouchListener(onTouchListener);
}
/**
......@@ -56,6 +58,14 @@ class MenuButton extends FrameLayout {
}
View getMenuButton() {
return mMenuButtonView;
return mMenuTintedImageButton;
}
/**
* @param tintList The {@link ColorStateList} that will tint the menu button (the badge is not
* tinted).
*/
void setTint(ColorStateList tintList) {
mMenuTintedImageButton.setTint(tintList);
}
}
......@@ -4,6 +4,7 @@
package org.chromium.chrome.browser.toolbar;
import android.content.res.ColorStateList;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
......@@ -121,6 +122,13 @@ public class TabSwitcherButtonCoordinator {
updateTabCount();
}
/**
* @param tint The {@ColorStateList} used to tint the button.
*/
public void setTint(ColorStateList tint) {
mTabSwitcherButtonModel.setValue(TabSwitcherButtonProperties.TINT, tint);
}
public void destroy() {
if (mTabModelSelector != null) mTabModelSelector.removeObserver(mTabModelSelectorObserver);
if (mTabModelSelectorTabModelObserver != null) mTabModelSelectorTabModelObserver.destroy();
......
......@@ -4,6 +4,7 @@
package org.chromium.chrome.browser.toolbar;
import android.content.res.ColorStateList;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
......@@ -26,6 +27,9 @@ public interface TabSwitcherButtonProperties {
public static final ObjectPropertyKey<OnLongClickListener> ON_LONG_CLICK_LISTENER =
new ObjectPropertyKey<>();
/** The button tint. */
public static final ObjectPropertyKey<ColorStateList> TINT = new ObjectPropertyKey<>();
public static final PropertyKey[] ALL_KEYS =
new PropertyKey[] {NUMBER_OF_TABS, ON_CLICK_LISTENER, ON_LONG_CLICK_LISTENER};
new PropertyKey[] {NUMBER_OF_TABS, ON_CLICK_LISTENER, ON_LONG_CLICK_LISTENER, TINT};
}
......@@ -5,6 +5,7 @@
package org.chromium.chrome.browser.toolbar;
import android.content.Context;
import android.content.res.ColorStateList;
import android.util.AttributeSet;
import android.widget.ImageView;
......@@ -18,7 +19,7 @@ public class TabSwitcherButtonView extends ImageView {
/**
* A drawable for the tab switcher icon.
*/
private TabSwitcherDrawable mTabSwitcherButtonButtonDrawable;
private TabSwitcherDrawable mTabSwitcherButtonDrawable;
public TabSwitcherButtonView(Context context, AttributeSet attrs) {
super(context, attrs);
......@@ -28,9 +29,9 @@ public class TabSwitcherButtonView extends ImageView {
protected void onFinishInflate() {
super.onFinishInflate();
mTabSwitcherButtonButtonDrawable =
mTabSwitcherButtonDrawable =
TabSwitcherDrawable.createTabSwitcherDrawable(getContext(), false);
setImageDrawable(mTabSwitcherButtonButtonDrawable);
setImageDrawable(mTabSwitcherButtonDrawable);
}
/**
......@@ -41,6 +42,13 @@ public class TabSwitcherButtonView extends ImageView {
setContentDescription(getResources().getQuantityString(
R.plurals.accessibility_toolbar_btn_tabswitcher_toggle, numberOfTabs,
numberOfTabs));
mTabSwitcherButtonButtonDrawable.updateForTabCount(numberOfTabs, false);
mTabSwitcherButtonDrawable.updateForTabCount(numberOfTabs, false);
}
/**
* @param tint The {@ColorStateList} used to tint the button.
*/
public void setTint(ColorStateList tint) {
mTabSwitcherButtonDrawable.setTint(tint);
}
}
......@@ -32,6 +32,8 @@ public class TabSwitcherButtonViewBinder
} else if (TabSwitcherButtonProperties.ON_LONG_CLICK_LISTENER == propertyKey) {
view.setOnLongClickListener(
model.getValue(TabSwitcherButtonProperties.ON_LONG_CLICK_LISTENER));
} else if (TabSwitcherButtonProperties.TINT == propertyKey) {
view.setTint(model.getValue(TabSwitcherButtonProperties.TINT));
} else {
assert false : "Unhandled property detected in TabSwitcherViewBinder!";
}
......
......@@ -5,7 +5,10 @@
package org.chromium.chrome.browser.toolbar;
import android.content.Context;
import android.support.v4.content.ContextCompat;
import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v7.content.res.AppCompatResources;
import android.view.View.OnClickListener;
import org.chromium.chrome.R;
......@@ -34,42 +37,52 @@ class ToolbarButtonSlotData {
* buttons when entering or leaving tab switching mode.
*/
static class ToolbarButtonData {
private final int mDrawableResId;
// TODO(amaralp): Add incognito accessibility string.
private final CharSequence mAccessibilityStringResId;
private final Drawable mDrawable;
private final CharSequence mLightAccessibilityString;
private final CharSequence mDarkAccessibilityString;
private final OnClickListener mOnClickListener;
private final boolean mShouldTint;
private final ColorStateList mLightTint;
private final ColorStateList mDarkTint;
/**
* @param drawableResId The drawable's resource id.
* @param accessibilityStringResId The accessibility's resource id.
* @param onClickListener An {@link OnClickListener} that is triggered when this button is
* clicked.
* @param shouldTint Whether the button should be tinted.
* @param context The {@link Context} used to get the drawable and accessibility string
* resources.
* @param drawable The {@link Drawable} that will be shown in the button slot.
* @param lightAccessibilityString The accessibility string to be used in light mode.
* @param darkAccessibilityString The accessibility string to be used in dark mode.
* @param onClickListener The listener that will be fired when this button is clicked.
* @param context The {@link Context} that is used to obtain tinting information.
*/
ToolbarButtonData(int drawableResId, int accessibilityStringResId,
OnClickListener onClickListener, boolean shouldTint, Context context) {
mAccessibilityStringResId = context.getString(accessibilityStringResId);
ToolbarButtonData(Drawable drawable, CharSequence lightAccessibilityString,
CharSequence darkAccessibilityString, OnClickListener onClickListener,
Context context) {
mLightTint = AppCompatResources.getColorStateList(context, R.color.light_mode_tint);
mDarkTint = AppCompatResources.getColorStateList(context, R.color.dark_mode_tint);
mDrawable = drawable;
mLightAccessibilityString = lightAccessibilityString;
mDarkAccessibilityString = darkAccessibilityString;
mOnClickListener = onClickListener;
mDrawableResId = drawableResId;
mShouldTint = shouldTint;
}
/**
* @param imageButton The {@link TintedImageButton} this button data will fill.
* @param isLight Whether or not to use light mode.
*/
void updateButton(TintedImageButton imageButton) {
void updateButton(TintedImageButton imageButton, boolean isLight) {
imageButton.setOnClickListener(mOnClickListener);
imageButton.setImageResource(mDrawableResId);
imageButton.setContentDescription(mAccessibilityStringResId);
if (mShouldTint) {
imageButton.setImageTintList(ContextCompat.getColorStateList(
imageButton.getContext(), R.color.dark_mode_tint));
} else {
imageButton.setImageTintList(null);
}
updateButtonDrawable(imageButton, isLight);
}
/**
* @param imageButton The {@link TintedImageButton} this button data will fill.
* @param isLight Whether or not to use light mode.
*/
void updateButtonDrawable(TintedImageButton imageButton, boolean isLight) {
DrawableCompat.setTintList(mDrawable, isLight ? mLightTint : mDarkTint);
imageButton.setImageDrawable(mDrawable);
imageButton.setContentDescription(
isLight ? mLightAccessibilityString : mDarkAccessibilityString);
imageButton.invalidate();
}
}
}
......@@ -4,7 +4,6 @@
package org.chromium.chrome.browser.toolbar;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.drawable.Drawable;
import android.os.Handler;
......@@ -14,6 +13,8 @@ import android.os.SystemClock;
import android.support.annotation.DrawableRes;
import android.support.annotation.IntDef;
import android.support.annotation.StringRes;
import android.support.graphics.drawable.VectorDrawableCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.ActionBar;
import android.text.TextUtils;
import android.view.View;
......@@ -642,9 +643,9 @@ public class ToolbarManager implements ToolbarTabController, UrlFocusChangeListe
public void enableBottomToolbar() {
if (FeatureUtilities.isBottomToolbarEnabled()) {
final ToolbarButtonSlotData firstButtonSlot =
new ToolbarButtonSlotData(createHomeButton(mActivity));
new ToolbarButtonSlotData(createHomeButton());
final ToolbarButtonSlotData secondButtonSlot =
new ToolbarButtonSlotData(createSearchAccelerator(mActivity));
new ToolbarButtonSlotData(createSearchAccelerator());
mBottomToolbarCoordinator = new BottomToolbarCoordinator(
mActivity.getFullscreenManager(), mActivity.findViewById(R.id.coordinator),
firstButtonSlot, secondButtonSlot);
......@@ -672,38 +673,54 @@ public class ToolbarManager implements ToolbarTabController, UrlFocusChangeListe
};
}
private ToolbarButtonData createHomeButton(Context context) {
private ToolbarButtonData createHomeButton() {
final OnClickListener homeButtonListener = v -> {
recordBottomToolbarUseForIPH();
openHomepage();
};
return new ToolbarButtonData(R.drawable.btn_toolbar_home,
R.string.accessibility_toolbar_btn_home, homeButtonListener, true, context);
final Drawable drawable = ContextCompat.getDrawable(mActivity, R.drawable.btn_toolbar_home);
final CharSequence accessibilityString =
mActivity.getString(R.string.accessibility_toolbar_btn_home);
return new ToolbarButtonData(
drawable, accessibilityString, accessibilityString, homeButtonListener, mActivity);
}
private ToolbarButtonData createNewTabButton(
OnClickListener newTabClickListener, Context context) {
return new ToolbarButtonData(R.drawable.btn_new_tab_white_normal,
R.string.accessibility_toolbar_btn_new_tab, newTabClickListener, false, context);
private ToolbarButtonData createNewTabButton(OnClickListener newTabClickListener) {
final CharSequence normalAccessibilityString =
mActivity.getString(R.string.accessibility_toolbar_btn_new_tab);
final CharSequence incognitoAccessibilityString =
mActivity.getString(R.string.accessibility_toolbar_btn_new_incognito_tab);
final Drawable drawable = VectorDrawableCompat.create(
mActivity.getResources(), R.drawable.new_tab_icon, mActivity.getTheme());
return new ToolbarButtonData(drawable, normalAccessibilityString,
incognitoAccessibilityString, newTabClickListener, mActivity);
}
private ToolbarButtonData createSearchAccelerator(Context context) {
private ToolbarButtonData createSearchAccelerator() {
final OnClickListener searchAcceleratorListener = v -> {
recordBottomToolbarUseForIPH();
recordOmniboxFocusReason(OmniboxFocusReason.ACCELERATOR_TAP);
ACCELERATOR_BUTTON_TAP_ACTION.record();
setUrlBarFocus(true);
};
return new ToolbarButtonData(R.drawable.ic_search,
R.string.accessibility_toolbar_btn_search_accelerator, searchAcceleratorListener,
true, context);
final Drawable drawable = ContextCompat.getDrawable(mActivity, R.drawable.ic_search);
final CharSequence accessibilityString =
mActivity.getString(R.string.accessibility_toolbar_btn_search_accelerator);
return new ToolbarButtonData(drawable, accessibilityString, accessibilityString,
searchAcceleratorListener, mActivity);
}
private ToolbarButtonData createIncognitoToggleButton(
OnClickListener incognitoToggleClickHandler, Context context) {
return new ToolbarButtonData(R.drawable.btn_tabstrip_switch_normal,
R.string.accessibility_tabstrip_btn_incognito_toggle_standard,
incognitoToggleClickHandler, false, context);
OnClickListener incognitoToggleClickHandler) {
final CharSequence normalAccessibilityString =
mActivity.getString(R.string.accessibility_tabstrip_btn_incognito_toggle_standard);
final CharSequence incognitoAccessibilityString =
mActivity.getString(R.string.accessibility_tabstrip_btn_incognito_toggle_incognito);
final Drawable drawable =
ContextCompat.getDrawable(mActivity, R.drawable.incognito_statusbar);
return new ToolbarButtonData(drawable, normalAccessibilityString,
incognitoAccessibilityString, incognitoToggleClickHandler, mActivity);
}
/**
......@@ -831,8 +848,7 @@ public class ToolbarManager implements ToolbarTabController, UrlFocusChangeListe
&& PrefServiceBridge.getInstance().isIncognitoModeEnabled();
final ToolbarButtonData firstSlotTabSwitcherButtonData = showIncognitoToggleButton
? createIncognitoToggleButton(
wrapBottomToolbarClickListenerForIPH(incognitoClickHandler),
mActivity)
wrapBottomToolbarClickListenerForIPH(incognitoClickHandler))
: null;
mAppMenuButtonHelper.setOnClickRunnable(() -> recordBottomToolbarUseForIPH());
mBottomToolbarCoordinator.initializeWithNative(
......@@ -842,8 +858,7 @@ public class ToolbarManager implements ToolbarTabController, UrlFocusChangeListe
mAppMenuButtonHelper, mTabModelSelector, mOverviewModeBehavior,
mActivity.getContextualSearchManager(), mActivity.getWindowAndroid(),
firstSlotTabSwitcherButtonData,
createNewTabButton(
wrapBottomToolbarClickListenerForIPH(newTabClickHandler), mActivity));
createNewTabButton(wrapBottomToolbarClickListenerForIPH(newTabClickHandler)));
Tab currentTab = tabModelSelector.getCurrentTab();
maybeShowDuetHelpBubble(currentTab);
......@@ -1288,6 +1303,10 @@ public class ToolbarManager implements ToolbarTabController, UrlFocusChangeListe
mCurrentThemeColor = color;
mToolbarModel.setPrimaryColor(color);
mToolbar.onPrimaryColorChanged(shouldAnimate);
if (mBottomToolbarCoordinator != null) {
mBottomToolbarCoordinator.setPrimaryColor(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