// 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.toolbar.top;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Build.VERSION;
import android.util.AttributeSet;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ProgressBar;
import androidx.annotation.CallSuper;
import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.widget.TooltipCompat;
import org.chromium.base.ObserverList;
import org.chromium.base.TraceEvent;
import org.chromium.base.lifetime.DestroyChecker;
import org.chromium.base.lifetime.Destroyable;
import org.chromium.base.supplier.ObservableSupplier;
import org.chromium.chrome.browser.browser_controls.BrowserStateBrowserControlsVisibilityDelegate;
import org.chromium.chrome.browser.layouts.LayoutStateProvider;
import org.chromium.chrome.browser.omnibox.LocationBar;
import org.chromium.chrome.browser.omnibox.LocationBarCoordinator;
import org.chromium.chrome.browser.omnibox.NewTabPageDelegate;
import org.chromium.chrome.browser.omnibox.OmniboxFocusReason;
import org.chromium.chrome.browser.omnibox.UrlBarData;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.theme.ThemeColorProvider;
import org.chromium.chrome.browser.theme.ThemeColorProvider.ThemeColorObserver;
import org.chromium.chrome.browser.theme.ThemeColorProvider.TintObserver;
import org.chromium.chrome.browser.theme.ThemeUtils;
import org.chromium.chrome.browser.toolbar.ButtonData;
import org.chromium.chrome.browser.toolbar.R;
import org.chromium.chrome.browser.toolbar.ToolbarDataProvider;
import org.chromium.chrome.browser.toolbar.ToolbarProgressBar;
import org.chromium.chrome.browser.toolbar.ToolbarTabController;
import org.chromium.chrome.browser.toolbar.menu_button.MenuButtonCoordinator;
import org.chromium.chrome.browser.toolbar.top.NavigationPopup.HistoryDelegate;
import org.chromium.chrome.browser.toolbar.top.ToolbarTablet.OfflineDownloader;
import org.chromium.chrome.browser.toolbar.top.TopToolbarCoordinator.ToolbarColorObserver;
import org.chromium.chrome.browser.toolbar.top.TopToolbarCoordinator.UrlExpansionObserver;
import org.chromium.chrome.browser.toolbar.top.tab_strip.TabStripTransitionCoordinator;
import org.chromium.chrome.browser.ui.appmenu.AppMenuButtonHelper;
import org.chromium.chrome.browser.ui.theme.BrandedColorScheme;
import org.chromium.chrome.browser.user_education.UserEducationHelper;
import org.chromium.chrome.browser.util.BrowserUiUtils;
import org.chromium.chrome.browser.util.BrowserUiUtils.ModuleTypeOnStartAndNtp;
import org.chromium.components.feature_engagement.Tracker;
import org.chromium.ui.UiUtils;
import org.chromium.ui.base.ViewUtils;
import org.chromium.ui.util.TokenHolder;
import org.chromium.url.GURL;
import java.util.function.BooleanSupplier;
/**
* Layout class that contains the base shared logic for manipulating the toolbar component. For
* interaction that are not from Views inside Toolbar hierarchy all interactions should be done
* through {@link Toolbar} rather than using this class directly.
*/
public abstract class ToolbarLayout extends FrameLayout
implements Destroyable, TintObserver, ThemeColorObserver {
private @Nullable ToolbarColorObserver mToolbarColorObserver;
protected final ObserverList<UrlExpansionObserver> mUrlExpansionObservers =
new ObserverList<>();
private final int[] mTempPosition = new int[2];
private final ColorStateList mDefaultTint;
private ToolbarDataProvider mToolbarDataProvider;
private ToolbarTabController mToolbarTabController;
@Nullable protected ToolbarProgressBar mProgressBar;
@Nullable protected BooleanSupplier mPartnerHomepageEnabledSupplier;
private boolean mNativeLibraryReady;
private boolean mUrlHasFocus;
private boolean mFindInPageToolbarShowing;
private ThemeColorProvider mThemeColorProvider;
private MenuButtonCoordinator mMenuButtonCoordinator;
private AppMenuButtonHelper mAppMenuButtonHelper;
private ToggleTabStackButtonCoordinator mTabSwitcherButtonCoordinator;
private TopToolbarOverlayCoordinator mOverlayCoordinator;
private BrowserStateBrowserControlsVisibilityDelegate mBrowserControlsVisibilityDelegate;
private int mShowBrowserControlsToken = TokenHolder.INVALID_TOKEN;
private TabStripTransitionCoordinator mTabStripTransitionCoordinator;
private int mTabStripTransitionToken = TokenHolder.INVALID_TOKEN;
protected final DestroyChecker mDestroyChecker;
/** Set if the progress bar is being drawn by WebContents for back forward transition. */
private boolean mShowingProgressBarForBackForwardTransition;
/** Caches the color for the toolbar hairline. */
private @ColorInt int mToolbarHairlineColor;
private ImageView mToolbarShadow;
/** Basic constructor for {@link ToolbarLayout}. */
public ToolbarLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mDefaultTint = ThemeUtils.getThemedToolbarIconTint(getContext(), false);
mDestroyChecker = new DestroyChecker();
addOnLayoutChangeListener(
new OnLayoutChangeListener() {
@Override
public void onLayoutChange(
View view,
int left,
int top,
int right,
int bottom,
int oldLeft,
int oldTop,
int oldRight,
int oldBottom) {
if (isNativeLibraryReady() && mProgressBar.getParent() != null) {
mProgressBar.initializeAnimation();
}
// Since this only needs to happen once, remove this listener from the view.
removeOnLayoutChangeListener(this);
}
});
}
/**
* Initialize the external dependencies required for view interaction.
*
* @param toolbarDataProvider The provider for toolbar data.
* @param tabController The controller that handles interactions with the tab.
* @param menuButtonCoordinator Coordinator for interacting with the MenuButton.
* @param historyDelegate Delegate used to display navigation history.
* @param partnerHomepageEnabledSupplier A supplier of a boolean indicating that partner
* homepage is enabled.
* @param offlineDownloader Triggers downloading an offline page.
* @param userEducationHelper Helper for user education flows.
* @param trackerSupplier Provides a {@link Tracker} when available.
*/
@CallSuper
public void initialize(
ToolbarDataProvider toolbarDataProvider,
ToolbarTabController tabController,
MenuButtonCoordinator menuButtonCoordinator,
ToggleTabStackButtonCoordinator tabSwitcherButtonCoordinator,
HistoryDelegate historyDelegate,
BooleanSupplier partnerHomepageEnabledSupplier,
OfflineDownloader offlineDownloader,
UserEducationHelper userEducationHelper,
ObservableSupplier<Tracker> trackerSupplier) {
mToolbarDataProvider = toolbarDataProvider;
mToolbarTabController = tabController;
mMenuButtonCoordinator = menuButtonCoordinator;
mTabSwitcherButtonCoordinator = tabSwitcherButtonCoordinator;
mPartnerHomepageEnabledSupplier = partnerHomepageEnabledSupplier;
mProgressBar = createProgressBar();
}
/** @param overlay The coordinator for the texture version of the top toolbar. */
void setOverlayCoordinator(TopToolbarOverlayCoordinator overlay) {
mOverlayCoordinator = overlay;
mOverlayCoordinator.setIsAndroidViewVisible(getVisibility() == View.VISIBLE);
}
@Override
public void setVisibility(int visibility) {
super.setVisibility(visibility);
if (mOverlayCoordinator != null) {
mOverlayCoordinator.setIsAndroidViewVisible(visibility == View.VISIBLE);
}
}
/**
* @param appMenuButtonHelper The helper for managing menu button interactions.
*/
void setAppMenuButtonHelper(AppMenuButtonHelper appMenuButtonHelper) {
mAppMenuButtonHelper = appMenuButtonHelper;
}
// TODO(pnoland, https://crbug.com/865801): Move this from ToolbarLayout to forthcoming
// BrowsingModeToolbarCoordinator.
public void setLocationBarCoordinator(LocationBarCoordinator locationBarCoordinator) {}
/** Cleans up any code as necessary. */
@Override
public void destroy() {
mDestroyChecker.destroy();
if (mThemeColorProvider != null) {
mThemeColorProvider.removeTintObserver(this);
mThemeColorProvider.removeThemeColorObserver(this);
mThemeColorProvider = null;
}
if (mToolbarColorObserver != null) {
mToolbarColorObserver = null;
}
}
/**
* @param urlExpansionObserver The observer that observes URL expansion progress change.
*/
void addUrlExpansionObserver(UrlExpansionObserver urlExpansionObserver) {
mUrlExpansionObservers.addObserver(urlExpansionObserver);
}
/**
* @param urlExpansionObserver The observer that observes URL expansion progress change.
*/
void removeUrlExpansionObserver(UrlExpansionObserver urlExpansionObserver) {
mUrlExpansionObservers.removeObserver(urlExpansionObserver);
}
/**
* @param toolbarColorObserver The observer that observes toolbar color change.
*/
public void setToolbarColorObserver(@NonNull ToolbarColorObserver toolbarColorObserver) {
mToolbarColorObserver = toolbarColorObserver;
}
/**
* @param themeColorProvider The {@link ThemeColorProvider} used for tinting the toolbar
* buttons.
*/
void setThemeColorProvider(ThemeColorProvider themeColorProvider) {
mThemeColorProvider = themeColorProvider;
mThemeColorProvider.addTintObserver(this);
mThemeColorProvider.addThemeColorObserver(this);
}
/**
* @return The tint the toolbar buttons should use.
*/
protected ColorStateList getTint() {
return mThemeColorProvider == null ? mDefaultTint : mThemeColorProvider.getTint();
}
protected ImageView getToolbarShadow() {
return mToolbarShadow;
}
/**
* Notifies whether the progress bar is being drawn by WebContents for back forward transition
* UI.
*/
public final void setShowingProgressBarForBackForwardTransition(
boolean showingProgressBarForBackForwardTransition) {
if (mShowingProgressBarForBackForwardTransition
== showingProgressBarForBackForwardTransition) return;
mShowingProgressBarForBackForwardTransition = showingProgressBarForBackForwardTransition;
mProgressBar.setVisibility(
mShowingProgressBarForBackForwardTransition ? View.GONE : View.VISIBLE);
updateShadowVisibility();
}
/** Update the visibility of the toolbar shadow. */
protected void updateShadowVisibility() {
boolean shouldDrawShadow = shouldDrawShadow();
int shadowVisibility = shouldDrawShadow ? View.VISIBLE : View.INVISIBLE;
if (mToolbarShadow != null && mToolbarShadow.getVisibility() != shadowVisibility) {
mToolbarShadow.setVisibility(shadowVisibility);
}
}
/**
* @return Whether the toolbar shadow should be drawn.
*/
protected boolean shouldDrawShadow() {
return !mShowingProgressBarForBackForwardTransition;
}
@Override
public void onTintChanged(
ColorStateList tint,
ColorStateList activityFocusTint,
@BrandedColorScheme int brandedColorScheme) {}
@Override
public void onThemeColorChanged(@ColorInt int color, boolean shouldAnimate) {}
/**
* Set the height that the progress bar should be.
*
* @return The progress bar height in px.
*/
int getProgressBarHeight() {
return getResources().getDimensionPixelSize(R.dimen.toolbar_progress_bar_height);
}
/**
* @return A progress bar for Chrome to use.
*/
private ToolbarProgressBar createProgressBar() {
return new ToolbarProgressBar(getContext(), getProgressBarHeight(), this, false);
}
/** TODO comment */
@CallSuper
protected void onMenuButtonDisabled() {}
// Set hover tooltip text for buttons shared between phones and tablets.
public void setTooltipTextForToolbarButtons() {
// Set hover tooltip text for home.
setTooltipText(
getHomeButton(), getContext().getString(R.string.accessibility_toolbar_btn_home));
}
/**
* Set hover tooltip text for buttons shared between phones and tablets. @TODO: Remove and use
* the method in UiUtils.java instead once JaCoCo issue is resolved.
*/
protected void setTooltipText(View button, String text) {
if (button != null && VERSION.SDK_INT >= Build.VERSION_CODES.O) {
TooltipCompat.setTooltipText(button, text);
}
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
// Initialize the provider to an empty version to avoid null checking everywhere.
mToolbarDataProvider =
new ToolbarDataProvider() {
@Override
public boolean isIncognito() {
return false;
}
@Override
public boolean isIncognitoBranded() {
return false;
}
@Override
public boolean isOffTheRecord() {
return false;
}
@Override
public Profile getProfile() {
return null;
}
@Override
public Tab getTab() {
return null;
}
@Override
public String getCurrentUrl() {
return "";
}
@Override
public GURL getCurrentGurl() {
return GURL.emptyGURL();
}
@Override
public UrlBarData getUrlBarData() {
return UrlBarData.EMPTY;
}
@Override
public NewTabPageDelegate getNewTabPageDelegate() {
return NewTabPageDelegate.EMPTY;
}
@Override
public @ColorInt int getPrimaryColor() {
return 0;
}
@Override
public boolean isUsingBrandColor() {
return false;
}
@Override
public @DrawableRes int getSecurityIconResource(boolean isTablet) {
return 0;
}
@Override
public boolean isPaintPreview() {
return false;
}
};
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
try (TraceEvent e = TraceEvent.scoped("ToolbarLayout.onMeasure")) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
try (TraceEvent e = TraceEvent.scoped("ToolbarLayout.onLayout")) {
super.onLayout(changed, left, top, right, bottom);
}
}
@Override
public void draw(Canvas canvas) {
try (TraceEvent e = TraceEvent.scoped("ToolbarLayout.draw")) {
super.draw(canvas);
}
}
/**
* Quick getter for LayoutParams for a View inside a FrameLayout.
* @param view {@link View} to fetch the layout params for.
* @return {@link LayoutParams} the given {@link View} is currently using.
*/
FrameLayout.LayoutParams getFrameLayoutParams(View view) {
return ((FrameLayout.LayoutParams) view.getLayoutParams());
}
/** This function handles native dependent initialization for this class. */
protected void onNativeLibraryReady() {
mNativeLibraryReady = true;
if (mProgressBar.getParent() != null) mProgressBar.initializeAnimation();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mToolbarShadow = getRootView().findViewById(R.id.toolbar_hairline);
updateShadowVisibility();
// TODO(crbug.com/40657306): lazy initialize progress bar.
// Posting adding progress bar can prevent parent view group from using a stale children
// count, which can cause a crash in rare cases.
post(this::addProgressBarToHierarchy);
}
/**
* @return The view containing the menu button and menu button badge.
*/
MenuButtonCoordinator getMenuButtonCoordinator() {
return mMenuButtonCoordinator;
}
ToggleTabStackButtonCoordinator getTabSwitcherButtonCoordinator() {
return mTabSwitcherButtonCoordinator;
}
void setMenuButtonCoordinatorForTesting(MenuButtonCoordinator menuButtonCoordinator) {
mMenuButtonCoordinator = menuButtonCoordinator;
}
void setTabSwitcherButtonCoordinatorForTesting(
ToggleTabStackButtonCoordinator toggleTabStackButtonCoordinator) {
mTabSwitcherButtonCoordinator = toggleTabStackButtonCoordinator;
}
/**
* @return The {@link ProgressBar} this layout uses.
*/
protected @Nullable ToolbarProgressBar getProgressBar() {
return mProgressBar;
}
void getPositionRelativeToContainer(View containerView, int[] position) {
ViewUtils.getRelativeDrawPosition(containerView, this, position);
}
/**
* @return Whether or not the native library is loaded and ready.
*/
boolean isNativeLibraryReady() {
return mNativeLibraryReady;
}
/** Add the toolbar's progress bar to the view hierarchy. */
void addProgressBarToHierarchy() {
ViewGroup controlContainer = (ViewGroup) getRootView().findViewById(R.id.control_container);
int progressBarPosition =
UiUtils.insertAfter(controlContainer, mProgressBar, (View) getParent());
assert progressBarPosition >= 0;
mProgressBar.setProgressBarContainer(controlContainer);
}
/**
* @return The provider for toolbar related data.
*/
public ToolbarDataProvider getToolbarDataProvider() {
return mToolbarDataProvider;
}
/**
* Gives inheriting classes the chance to respond to
* {@link FindToolbar} state changes.
* @param showing Whether or not the {@code FindToolbar} will be showing.
*/
void handleFindLocationBarStateChange(boolean showing) {
mFindInPageToolbarShowing = showing;
}
/**
* Sets the OnClickListener that will be notified when the bookmark button is pressed.
*
* @param listener The callback that will be notified when the bookmark button is pressed.
*/
void setBookmarkClickHandler(OnClickListener listener) {}
/**
* Sets the OnClickListener to notify when the close button is pressed in a custom tab.
* @param listener The callback that will be notified when the close button is pressed.
*/
protected void setCustomTabCloseClickHandler(OnClickListener listener) {}
/** Sets whether the urlbar should be hidden on first page load. */
protected void setUrlBarHidden(boolean hide) {}
/** Tells the Toolbar to update what buttons it is currently displaying. */
void updateButtonVisibility() {}
/**
* Gives inheriting classes the chance to update the visibility of the
* back button.
* @param canGoBack Whether or not the current tab has any history to go back to.
*/
void updateBackButtonVisibility(boolean canGoBack) {}
/**
* Gives inheriting classes the chance to update the visibility of the
* forward button.
* @param canGoForward Whether or not the current tab has any history to go forward to.
*/
void updateForwardButtonVisibility(boolean canGoForward) {}
/**
* Gives inheriting classes the chance to update the visibility of the
* reload button.
* @param isReloading Whether or not the current tab is loading.
*/
void updateReloadButtonVisibility(boolean isReloading) {}
/**
* Gives inheriting classes the chance to update the visual status of the bookmark button.
*
* @param isBookmarked Whether or not the current tab is already bookmarked.
* @param editingAllowed Whether or not bookmarks can be modified (added, edited, or removed).
*/
void updateBookmarkButton(boolean isBookmarked, boolean editingAllowed) {}
/**
* Gives inheriting classes the chance to update home button UI if home button preference is
* changed.
*
* @param homeButtonEnabled Whether or not home button is enabled in preference.
*/
void onHomeButtonUpdate(boolean homeButtonEnabled) {}
/**
* Triggered when the current tab or model has changed.
* <p>
* As there are cases where you can select a model with no tabs (i.e. having incognito
* tabs but no normal tabs will still allow you to select the normal model), this should
* not guarantee that the model's current tab is non-null.
*/
void onTabOrModelChanged() {}
/**
* For extending classes to override and carry out the changes related with the primary color
* for the current tab changing.
*/
public void onPrimaryColorChanged(boolean shouldAnimate) {}
/**
* Sets the icon drawable that the close button in the toolbar (if any) should show, or hides it
* if {@code drawable} is {@code null}.
*/
protected void setCloseButtonImageResource(@Nullable Drawable drawable) {}
/**
* Adds a custom action button to the toolbar layout, if it is supported.
*
* @param drawable The icon for the button.
* @param description The content description for the button.
* @param listener The {@link OnClickListener} to use for clicks to the button.
*/
protected void addCustomActionButton(
Drawable drawable, String description, OnClickListener listener) {
// This method should only be called for subclasses that override it.
assert false;
}
/**
* Updates the visual appearance of a custom action button in the toolbar layout,
* if it is supported.
* @param index The index of the button.
* @param drawable The icon for the button.
* @param description The content description for the button.
*/
protected void updateCustomActionButton(int index, Drawable drawable, String description) {
// This method should only be called for subclasses that override it.
assert false;
}
/**
* Return the height of the tab strip from the layout resource. Return 0 for toolbars that do
* not have a tab strip.
*/
protected int getTabStripHeightFromResource() {
return getResources().getDimensionPixelSize(R.dimen.tab_strip_height);
}
/** Triggered when the content view for the specified tab has changed. */
void onTabContentViewChanged() {}
/** Triggered when the page of the specified tab had painted something non-empty. */
public void onDidFirstVisuallyNonEmptyPaint() {}
protected abstract CaptureReadinessResult isReadyForTextureCapture();
boolean setForceTextureCapture(boolean forceTextureCapture) {
return false;
}
void setLayoutUpdater(Runnable layoutUpdater) {}
/**
* @param attached Whether or not the web content is attached to the view heirarchy.
*/
void setContentAttached(boolean attached) {}
/**
* Called when tab switcher mode is entered or exited.
* @param inTabSwitcherMode Whether or not tab switcher mode is being shown or hidden.
* @param showToolbar Whether or not to show the normal toolbar while animating.
* @param delayAnimation Whether or not to delay the animation until after the transition has
* finished (which can be detected by a call to
* {@link #onTabSwitcherTransitionFinished()}).
*/
void setTabSwitcherMode(boolean inTabSwitcherMode) {}
/**
* Gives inheriting classes the chance to update their state when the TabSwitcher transition has
* finished.
*/
void onTabSwitcherTransitionFinished() {}
/**
* Gives inheriting classes the chance to observe tab count changes.
*
* @param tabCountSupplier The observable supplier subclasses can observe.
*/
void setTabCountSupplier(ObservableSupplier<Integer> tabCountSupplier) {}
/**
* Gives inheriting classes the chance to update themselves based on default search engine
* changes.
*/
void onDefaultSearchEngineChanged() {}
@Override
public boolean onGenericMotionEvent(MotionEvent event) {
// Consumes mouse button events on toolbar so they don't get leaked to content layer.
// See https://crbug.com/740855.
if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0
&& event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE) {
int action = event.getActionMasked();
if (action == MotionEvent.ACTION_BUTTON_PRESS
|| action == MotionEvent.ACTION_BUTTON_RELEASE) {
return true;
}
}
return super.onGenericMotionEvent(event);
}
void getLocationBarContentRect(Rect outRect) {
View container = getLocationBar().getContainerView();
outRect.set(
container.getPaddingLeft(),
container.getPaddingTop(),
container.getWidth() - container.getPaddingRight(),
container.getHeight() - container.getPaddingBottom());
ViewUtils.getRelativeDrawPosition(this, getLocationBar().getContainerView(), mTempPosition);
outRect.offset(mTempPosition[0], mTempPosition[1]);
}
protected void setTextureCaptureMode(boolean textureMode) {}
boolean shouldIgnoreSwipeGesture() {
if (mUrlHasFocus || mFindInPageToolbarShowing) return true;
return mAppMenuButtonHelper != null && mAppMenuButtonHelper.isAppMenuActive();
}
/**
* @return Whether or not the url bar has focus.
*/
boolean urlHasFocus() {
return mUrlHasFocus;
}
/**
* Triggered when the URL input field has gained or lost focus.
* @param hasFocus Whether the URL field has gained focus.
*/
void onUrlFocusChange(boolean hasFocus) {
mUrlHasFocus = hasFocus;
}
/** Notified when a navigation to a different page has occurred. */
protected void onNavigatedToDifferentPage() {}
/** Finish any toolbar animations. */
void finishAnimations() {}
/**
* @return The current View showing in the Tab.
*/
View getCurrentTabView() {
Tab tab = mToolbarDataProvider.getTab();
if (tab != null) {
return tab.getView();
}
return null;
}
/**
* TODO(crbug.com/350654700): clean up usages and remove isIncognito.
*
* @return Whether or not the toolbar is incognito.
* @deprecated Use {@link #isIncognitoBranded()} or {@link #isOffTheRecord()}.
*/
@Deprecated
protected boolean isIncognito() {
return mToolbarDataProvider.isIncognito();
}
/**
* @return Whether or not the toolbar is incognito branded.
* @see {@link ToolbarDataProvider#isIncognitoBranded()}
*/
protected boolean isIncognitoBranded() {
return mToolbarDataProvider.isIncognitoBranded();
}
/**
* @return Whether or not the toolbar is off the record.
* @see {@link ToolbarDataProvider#isOffTheRecord()}
*/
protected boolean isOffTheRecord() {
return mToolbarDataProvider.isOffTheRecord();
}
/**
* @return {@link LocationBar} object this {@link ToolbarLayout} contains.
*/
@VisibleForTesting
public abstract LocationBar getLocationBar();
/**
* Navigates the current Tab back.
* @return Whether or not the current Tab did go back.
*/
boolean back() {
maybeUnfocusUrlBar();
return mToolbarTabController != null && mToolbarTabController.back();
}
/**
* Navigates the current Tab forward.
* @return Whether or not the current Tab did go forward.
*/
boolean forward() {
maybeUnfocusUrlBar();
return mToolbarTabController != null ? mToolbarTabController.forward() : false;
}
/**
* If the page is currently loading, this will trigger the tab to stop. If the page is fully
* loaded, this will trigger a refresh.
*
* <p>The buttons of the toolbar will be updated as a result of making this call.
*/
void stopOrReloadCurrentTab() {
maybeUnfocusUrlBar();
if (mToolbarTabController != null) mToolbarTabController.stopOrReloadCurrentTab();
}
/** Opens hompage in the current tab. */
void openHomepage() {
maybeUnfocusUrlBar();
if (mToolbarTabController != null) mToolbarTabController.openHomepage();
}
private void maybeUnfocusUrlBar() {
if (getLocationBar() != null && getLocationBar().getOmniboxStub() != null) {
getLocationBar()
.getOmniboxStub()
.setUrlBarFocus(false, null, OmniboxFocusReason.UNFOCUS);
}
}
/**
* Update the optional toolbar button, showing it if currently hidden.
* @param buttonData Display data for the button, e.g. the Drawable and content description.
*/
void updateOptionalButton(ButtonData buttonData) {}
/** Hide the optional toolbar button. */
void hideOptionalButton() {}
/**
* @return Optional button view.
*/
public View getOptionalButtonViewForTesting() {
return null;
}
/**
* @return Home button this {@link ToolbarLayout} contains, if any.
*/
public ImageView getHomeButton() {
return null;
}
/**
* @return {@link ToggleTabStackButton} this {@link ToolbarLayout} contains.
*/
public ToggleTabStackButton getTabSwitcherButton() {
return null;
}
/** Returns whether there are any ongoing animations. */
public boolean isAnimationRunningForTesting() {
return false;
}
/**
* Sets the toolbar hairline color, if the toolbar has a hairline below it.
*
* @param toolbarColor The toolbar color to base the hairline color on.
*/
protected void setToolbarHairlineColor(@ColorInt int toolbarColor) {
final ImageView shadow = getRootView().findViewById(R.id.toolbar_hairline);
// Tests don't always set this up. TODO(crbug.com/40866629): Refactor this dep.
if (shadow != null) {
mToolbarHairlineColor = computeToolbarHairlineColor(toolbarColor);
shadow.setImageTintList(ColorStateList.valueOf(mToolbarHairlineColor));
}
}
/** Returns the color of the hairline drawn underneath the toolbar. */
public @ColorInt int getToolbarHairlineColor() {
return mToolbarHairlineColor;
}
/**
* Returns the border color between the toolbar and WebContents area.
*
* @param toolbarColor Toolbar color
*/
private @ColorInt int computeToolbarHairlineColor(@ColorInt int toolbarColor) {
return ThemeUtils.getToolbarHairlineColor(
getContext(), toolbarColor, mToolbarDataProvider.isIncognitoBranded());
}
/**
* Sets the {@link BrowserStateBrowserControlsVisibilityDelegate} instance the toolbar should
* use to manipulate the visibility of browser controls; notably, "browser controls" includes
* the toolbar itself.
*/
public void setBrowserControlsVisibilityDelegate(
BrowserStateBrowserControlsVisibilityDelegate controlsVisibilityDelegate) {
mBrowserControlsVisibilityDelegate = controlsVisibilityDelegate;
}
// TODO(crbug.com/41484813): Rework the API if this method is called by multiple clients.
protected void keepControlsShownForAnimation() {
// isShown() being false implies that the toolbar isn't visible. We don't want to force it
// back into visibility just so that we can show an animation.
if (!isShown()) return;
if (mBrowserControlsVisibilityDelegate != null) {
mShowBrowserControlsToken =
mBrowserControlsVisibilityDelegate.showControlsPersistentAndClearOldToken(
mShowBrowserControlsToken);
}
if (mTabStripTransitionCoordinator != null
&& mTabStripTransitionToken == TokenHolder.INVALID_TOKEN) {
mTabStripTransitionToken =
mTabStripTransitionCoordinator.requestDeferTabStripTransitionToken();
}
}
protected void allowBrowserControlsHide() {
if (mBrowserControlsVisibilityDelegate != null) {
mBrowserControlsVisibilityDelegate.releasePersistentShowingToken(
mShowBrowserControlsToken);
mShowBrowserControlsToken = TokenHolder.INVALID_TOKEN;
}
if (mTabStripTransitionCoordinator != null) {
mTabStripTransitionCoordinator.releaseTabStripToken(mTabStripTransitionToken);
mTabStripTransitionToken = TokenHolder.INVALID_TOKEN;
}
}
/** Set the {@link TabStripTransitionCoordinator} that manages interactions around tab strip. */
public void setTabStripTransitionCoordinator(TabStripTransitionCoordinator coordinator) {
mTabStripTransitionCoordinator = coordinator;
}
/**
* Notify the observer that the toolbar color is changed and pass the toolbar color to the
* observer.
*/
protected void notifyToolbarColorChanged(@ColorInt int color) {
if (mToolbarColorObserver != null) {
mToolbarColorObserver.onToolbarColorChanged(color);
}
}
/**
* This method sets the toolbar hairline visibility.
* @param isHairlineVisible whether the toolbar hairline should be visible.
*/
public void setHairlineVisibility(boolean isHairlineVisible) {
ImageView shadow = getRootView().findViewById(R.id.toolbar_hairline);
if (shadow != null) {
shadow.setVisibility(isHairlineVisible ? VISIBLE : GONE);
}
}
/**
* To be called indirectly by
* {@link LayoutStateProvider.LayoutStateObserver#onStartedHiding(int, boolean, boolean)}.
*/
public void onTransitionStart() {}
/**
* To be called indirectly by
* {@link LayoutStateProvider.LayoutStateObserver#onFinishedShowing(int)}.
*/
public void onTransitionEnd() {}
/**
* Called when the home button is pressed. Will record the home button action if the NTP is
* visible. Used on both phones and tablets.
*/
protected void recordHomeModuleClickedIfNTPVisible() {
if (getToolbarDataProvider().getNewTabPageDelegate().isCurrentlyVisible()) {
// Record the clicking action on the home button.
BrowserUiUtils.recordModuleClickHistogram(ModuleTypeOnStartAndNtp.HOME_BUTTON);
}
}
}