| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // 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; |
| |
| import android.content.res.Configuration; |
| import android.content.res.Resources; |
| import android.graphics.drawable.Drawable; |
| import android.os.Handler; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.os.SystemClock; |
| import android.support.annotation.DrawableRes; |
| import android.support.annotation.IntDef; |
| import android.support.annotation.Nullable; |
| import android.support.annotation.StringRes; |
| import android.support.v7.app.ActionBar; |
| import android.text.TextUtils; |
| import android.view.View; |
| import android.view.View.OnAttachStateChangeListener; |
| import android.view.View.OnClickListener; |
| |
| import org.chromium.base.ApiCompatibilityUtils; |
| import org.chromium.base.Callback; |
| import org.chromium.base.VisibleForTesting; |
| import org.chromium.base.metrics.CachedMetrics.ActionEvent; |
| import org.chromium.base.metrics.CachedMetrics.EnumeratedHistogramSample; |
| import org.chromium.base.metrics.RecordHistogram; |
| import org.chromium.base.metrics.RecordUserAction; |
| import org.chromium.base.task.PostTask; |
| import org.chromium.chrome.R; |
| import org.chromium.chrome.browser.ChromeActivity; |
| import org.chromium.chrome.browser.ChromeTabbedActivity; |
| import org.chromium.chrome.browser.TabLoadStatus; |
| import org.chromium.chrome.browser.ThemeColorProvider; |
| import org.chromium.chrome.browser.ThemeColorProvider.ThemeColorObserver; |
| import org.chromium.chrome.browser.UrlConstants; |
| import org.chromium.chrome.browser.WindowDelegate; |
| import org.chromium.chrome.browser.appmenu.AppMenuButtonHelper; |
| import org.chromium.chrome.browser.appmenu.AppMenuHandler; |
| import org.chromium.chrome.browser.appmenu.AppMenuObserver; |
| import org.chromium.chrome.browser.appmenu.AppMenuPropertiesDelegate; |
| import org.chromium.chrome.browser.bookmarks.BookmarkBridge; |
| import org.chromium.chrome.browser.compositor.Invalidator; |
| import org.chromium.chrome.browser.compositor.layouts.EmptyOverviewModeObserver; |
| import org.chromium.chrome.browser.compositor.layouts.Layout; |
| import org.chromium.chrome.browser.compositor.layouts.LayoutManager; |
| import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior; |
| import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior.OverviewModeObserver; |
| import org.chromium.chrome.browser.compositor.layouts.SceneChangeObserver; |
| import org.chromium.chrome.browser.download.DownloadUtils; |
| import org.chromium.chrome.browser.feature_engagement.ScreenshotTabObserver; |
| import org.chromium.chrome.browser.feature_engagement.TrackerFactory; |
| import org.chromium.chrome.browser.fullscreen.BrowserStateBrowserControlsVisibilityDelegate; |
| import org.chromium.chrome.browser.fullscreen.FullscreenManager; |
| import org.chromium.chrome.browser.fullscreen.FullscreenOptions; |
| import org.chromium.chrome.browser.metrics.OmniboxStartupMetrics; |
| import org.chromium.chrome.browser.native_page.NativePage; |
| import org.chromium.chrome.browser.native_page.NativePageFactory; |
| import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings; |
| import org.chromium.chrome.browser.ntp.IncognitoNewTabPage; |
| import org.chromium.chrome.browser.ntp.NewTabPage; |
| import org.chromium.chrome.browser.offlinepages.OfflinePageBridge; |
| import org.chromium.chrome.browser.omaha.UpdateMenuItemHelper; |
| import org.chromium.chrome.browser.omnibox.LocationBar; |
| import org.chromium.chrome.browser.omnibox.UrlFocusChangeListener; |
| import org.chromium.chrome.browser.partnercustomizations.HomepageManager; |
| import org.chromium.chrome.browser.previews.PreviewsAndroidBridge; |
| import org.chromium.chrome.browser.previews.PreviewsUma; |
| import org.chromium.chrome.browser.profiles.Profile; |
| import org.chromium.chrome.browser.search_engines.TemplateUrl; |
| import org.chromium.chrome.browser.search_engines.TemplateUrlService; |
| import org.chromium.chrome.browser.search_engines.TemplateUrlService.TemplateUrlServiceObserver; |
| import org.chromium.chrome.browser.tab.EmptyTabObserver; |
| import org.chromium.chrome.browser.tab.SadTab; |
| import org.chromium.chrome.browser.tab.Tab; |
| import org.chromium.chrome.browser.tab.TabObserver; |
| import org.chromium.chrome.browser.tab.TabThemeColorHelper; |
| import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver; |
| import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver; |
| import org.chromium.chrome.browser.tabmodel.TabModel; |
| import org.chromium.chrome.browser.tabmodel.TabModelObserver; |
| import org.chromium.chrome.browser.tabmodel.TabModelSelector; |
| import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver; |
| import org.chromium.chrome.browser.tabmodel.TabSelectionType; |
| import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarCoordinator; |
| import org.chromium.chrome.browser.toolbar.top.ActionModeController; |
| import org.chromium.chrome.browser.toolbar.top.ActionModeController.ActionBarDelegate; |
| import org.chromium.chrome.browser.toolbar.top.Toolbar; |
| import org.chromium.chrome.browser.toolbar.top.ToolbarActionModeCallback; |
| import org.chromium.chrome.browser.toolbar.top.ToolbarControlContainer; |
| import org.chromium.chrome.browser.toolbar.top.ToolbarLayout; |
| import org.chromium.chrome.browser.toolbar.top.TopToolbarCoordinator; |
| import org.chromium.chrome.browser.toolbar.top.ViewShiftingActionBarDelegate; |
| import org.chromium.chrome.browser.translate.TranslateBridge; |
| import org.chromium.chrome.browser.util.FeatureUtilities; |
| import org.chromium.chrome.browser.widget.ScrimView; |
| import org.chromium.chrome.browser.widget.ScrimView.ScrimObserver; |
| import org.chromium.chrome.browser.widget.ScrimView.ScrimParams; |
| import org.chromium.chrome.browser.widget.ViewHighlighter; |
| import org.chromium.chrome.browser.widget.findinpage.FindToolbarManager; |
| import org.chromium.chrome.browser.widget.findinpage.FindToolbarObserver; |
| import org.chromium.chrome.browser.widget.textbubble.TextBubble; |
| import org.chromium.components.feature_engagement.EventConstants; |
| import org.chromium.components.feature_engagement.FeatureConstants; |
| import org.chromium.components.feature_engagement.Tracker; |
| import org.chromium.content_public.browser.LoadUrlParams; |
| import org.chromium.content_public.browser.NavigationController; |
| import org.chromium.content_public.browser.NavigationEntry; |
| import org.chromium.content_public.browser.UiThreadTaskTraits; |
| import org.chromium.content_public.browser.WebContents; |
| import org.chromium.ui.AsyncViewProvider; |
| import org.chromium.ui.base.DeviceFormFactor; |
| import org.chromium.ui.base.PageTransition; |
| import org.chromium.ui.widget.ViewRectProvider; |
| |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| import java.util.List; |
| import java.util.concurrent.TimeUnit; |
| |
| /** |
| * Contains logic for managing the toolbar visual component. This class manages the interactions |
| * with the rest of the application to ensure the toolbar is always visually up to date. |
| */ |
| public class ToolbarManager |
| implements ScrimObserver, ToolbarTabController, UrlFocusChangeListener, ThemeColorObserver { |
| /** |
| * Handle UI updates of menu icons. Only applicable for phones. |
| */ |
| public interface MenuDelegatePhone { |
| /** |
| * Called when current tab's loading status changes. |
| * |
| * @param isLoading Whether the current tab is loading. |
| */ |
| void updateReloadButtonState(boolean isLoading); |
| } |
| |
| /** A means of tracking which mechanism is being used to focus the omnibox. */ |
| @IntDef({OmniboxFocusReason.OMNIBOX_TAP, OmniboxFocusReason.OMNIBOX_LONG_PRESS, |
| OmniboxFocusReason.FAKE_BOX_TAP, OmniboxFocusReason.FAKE_BOX_LONG_PRESS, |
| OmniboxFocusReason.ACCELERATOR_TAP}) |
| @Retention(RetentionPolicy.SOURCE) |
| public @interface OmniboxFocusReason { |
| int OMNIBOX_TAP = 0; |
| int OMNIBOX_LONG_PRESS = 1; |
| int FAKE_BOX_TAP = 2; |
| int FAKE_BOX_LONG_PRESS = 3; |
| int ACCELERATOR_TAP = 4; |
| int NUM_ENTRIES = 5; |
| } |
| private static final EnumeratedHistogramSample ENUMERATED_FOCUS_REASON = |
| new EnumeratedHistogramSample( |
| "Android.OmniboxFocusReason", OmniboxFocusReason.NUM_ENTRIES); |
| private static final ActionEvent ACCELERATOR_BUTTON_TAP_ACTION = |
| new ActionEvent("MobileToolbarOmniboxAcceleratorTap"); |
| |
| /** |
| * The number of ms to wait before reporting to UMA omnibox interaction metrics. |
| */ |
| private static final int RECORD_UMA_PERFORMANCE_METRICS_DELAY_MS = 30000; |
| |
| /** |
| * The minimum load progress that can be shown when a page is loading. This is not 0 so that |
| * it's obvious to the user that something is attempting to load. |
| */ |
| private static final int MINIMUM_LOAD_PROGRESS = 5; |
| |
| private final AsyncViewProvider<ToolbarLayout> mToolbarProvider; |
| private final IncognitoStateProvider mIncognitoStateProvider; |
| private final TabCountProvider mTabCountProvider; |
| private final ThemeColorProvider mThemeColorProvider; |
| private TopToolbarCoordinator mToolbar; |
| private final ToolbarControlContainer mControlContainer; |
| |
| private BottomToolbarCoordinator mBottomToolbarCoordinator; |
| private TabModelSelector mTabModelSelector; |
| private TabModelSelectorObserver mTabModelSelectorObserver; |
| private TabModelObserver mTabModelObserver; |
| private MenuDelegatePhone mMenuDelegatePhone; |
| private final LocationBarModel mLocationBarModel; |
| private Profile mCurrentProfile; |
| private BookmarkBridge mBookmarkBridge; |
| private TemplateUrlServiceObserver mTemplateUrlObserver; |
| @Nullable |
| private LocationBar mLocationBar; |
| private FindToolbarManager mFindToolbarManager; |
| private AppMenuPropertiesDelegate mAppMenuPropertiesDelegate; |
| private OverviewModeBehavior mOverviewModeBehavior; |
| private LayoutManager mLayoutManager; |
| |
| private TabObserver mTabObserver; |
| private BookmarkBridge.BookmarkModelObserver mBookmarksObserver; |
| private FindToolbarObserver mFindToolbarObserver; |
| private OverviewModeObserver mOverviewModeObserver; |
| private SceneChangeObserver mSceneChangeObserver; |
| private final ActionBarDelegate mActionBarDelegate; |
| private ActionModeController mActionModeController; |
| private final ToolbarActionModeCallback mToolbarActionModeCallback; |
| private LoadProgressSimulator mLoadProgressSimulator; |
| private final Callback<Boolean> mUrlFocusChangedCallback; |
| private final Handler mHandler = new Handler(); |
| private final ChromeActivity mActivity; |
| private UrlFocusChangeListener mLocationBarFocusObserver; |
| |
| private BrowserStateBrowserControlsVisibilityDelegate mControlsVisibilityDelegate; |
| private int mFullscreenFocusToken = FullscreenManager.INVALID_TOKEN; |
| private int mFullscreenFindInPageToken = FullscreenManager.INVALID_TOKEN; |
| private int mFullscreenMenuToken = FullscreenManager.INVALID_TOKEN; |
| private int mFullscreenHighlightToken = FullscreenManager.INVALID_TOKEN; |
| |
| private int mPreselectedTabId = Tab.INVALID_TAB_ID; |
| |
| private boolean mNativeLibraryReady; |
| private boolean mTabRestoreCompleted; |
| |
| private AppMenuButtonHelper mAppMenuButtonHelper; |
| |
| private TextBubble mTextBubble; |
| |
| private boolean mToolbarInflationComplete; |
| private boolean mInitializedWithNative; |
| |
| private boolean mShouldUpdateToolbarPrimaryColor = true; |
| private int mCurrentThemeColor; |
| |
| private OmniboxStartupMetrics mOmniboxStartupMetrics; |
| |
| /** |
| * Creates a ToolbarManager object. |
| * |
| * @param controlContainer The container of the toolbar. |
| * @param menuHandler The handler for interacting with the menu. |
| * @param appMenuPropertiesDelegate Delegate for interactions with the app level menu. |
| * @param invalidator Handler for synchronizing invalidations across UI elements. |
| * @param urlFocusChangedCallback The callback to be notified when the URL focus changes. |
| */ |
| public ToolbarManager(ChromeActivity activity, ToolbarControlContainer controlContainer, |
| final AppMenuHandler menuHandler, AppMenuPropertiesDelegate appMenuPropertiesDelegate, |
| Invalidator invalidator, Callback<Boolean> urlFocusChangedCallback, |
| ThemeColorProvider themeColorProvider) { |
| mActivity = activity; |
| mActionBarDelegate = new ViewShiftingActionBarDelegate(activity, controlContainer); |
| |
| mLocationBarModel = new LocationBarModel(activity); |
| mControlContainer = controlContainer; |
| assert mControlContainer != null; |
| mUrlFocusChangedCallback = urlFocusChangedCallback; |
| |
| mToolbarActionModeCallback = new ToolbarActionModeCallback(); |
| |
| mLocationBarFocusObserver = new UrlFocusChangeListener() { |
| /** The params used to control how the scrim behaves when shown for the omnibox. */ |
| private ScrimParams mScrimParams; |
| |
| /** The light color to use for the scrim on the NTP. */ |
| private int mLightScrimColor; |
| |
| @Override |
| public void onUrlFocusChange(boolean hasFocus) { |
| if (mScrimParams == null) { |
| Resources res = mActivity.getResources(); |
| int topMargin = res.getDimensionPixelSize(R.dimen.tab_strip_height); |
| mLightScrimColor = ApiCompatibilityUtils.getColor( |
| res, R.color.omnibox_focused_fading_background_color_light); |
| View scrimTarget = mActivity.getCompositorViewHolder(); |
| mScrimParams = new ScrimView.ScrimParams( |
| scrimTarget, true, false, topMargin, ToolbarManager.this); |
| } |
| |
| boolean isTablet = DeviceFormFactor.isNonMultiDisplayContextOnTablet(mActivity); |
| mScrimParams.backgroundColor = |
| !isTablet && !mLocationBarModel.isIncognito() ? mLightScrimColor : null; |
| |
| if (hasFocus && !showScrimAfterAnimationCompletes()) { |
| mActivity.getScrim().showScrim(mScrimParams); |
| } else if (!hasFocus) { |
| mActivity.getScrim().hideScrim(true); |
| } |
| } |
| |
| @Override |
| public void onUrlAnimationFinished(boolean hasFocus) { |
| if (hasFocus && showScrimAfterAnimationCompletes()) { |
| mActivity.getScrim().showScrim(mScrimParams); |
| } |
| } |
| |
| /** |
| * @return Whether the scrim should wait to be shown until after the omnibox is done |
| * animating. |
| */ |
| private boolean showScrimAfterAnimationCompletes() { |
| if (mLocationBarModel.getNewTabPageForCurrentTab() == null) return false; |
| return mLocationBarModel.getNewTabPageForCurrentTab().isLocationBarShownInNTP(); |
| } |
| }; |
| |
| mIncognitoStateProvider = new IncognitoStateProvider(mActivity); |
| mTabCountProvider = new TabCountProvider(); |
| mThemeColorProvider = themeColorProvider; |
| mThemeColorProvider.addThemeColorObserver(this); |
| |
| mToolbarProvider = AsyncViewProvider.of(controlContainer, R.id.toolbar_stub, R.id.toolbar); |
| mToolbar = new TopToolbarCoordinator(controlContainer, mToolbarProvider); |
| mToolbarProvider.whenLoaded((toolbar) |
| -> onToolbarInflationComplete(menuHandler, |
| appMenuPropertiesDelegate, invalidator)); |
| } |
| |
| @Override |
| public void onScrimClick() { |
| setUrlBarFocus(false); |
| } |
| |
| @Override |
| public void onScrimVisibilityChanged(boolean visible) { |
| if (visible) { |
| mActivity.addViewObscuringAllTabs(mActivity.getScrim()); |
| } else { |
| mActivity.removeViewObscuringAllTabs(mActivity.getScrim()); |
| } |
| } |
| |
| private void onToolbarInflationComplete(final AppMenuHandler menuHandler, |
| AppMenuPropertiesDelegate appMenuPropertiesDelegate, Invalidator invalidator) { |
| mToolbarInflationComplete = true; |
| |
| mActionModeController = new ActionModeController(mActivity, mActionBarDelegate); |
| mActionModeController.setCustomSelectionActionModeCallback(mToolbarActionModeCallback); |
| |
| MenuDelegatePhone menuDelegate = new MenuDelegatePhone() { |
| @Override |
| public void updateReloadButtonState(boolean isLoading) { |
| if (mAppMenuPropertiesDelegate != null) { |
| mAppMenuPropertiesDelegate.loadingStateChanged(isLoading); |
| menuHandler.menuItemContentChanged(R.id.icon_row_menu_id); |
| } |
| } |
| }; |
| setMenuDelegatePhone(menuDelegate); |
| |
| mToolbar.setPaintInvalidator(invalidator); |
| mActionModeController.setTabStripHeight(mToolbar.getTabStripHeight()); |
| mLocationBar = mToolbar.getLocationBar(); |
| mLocationBar.setToolbarDataProvider(mLocationBarModel); |
| mLocationBar.addUrlFocusChangeListener(this); |
| mLocationBar.setDefaultTextEditActionModeCallback( |
| mActionModeController.getActionModeCallback()); |
| mLocationBar.initializeControls(new WindowDelegate(mActivity.getWindow()), |
| mActivity.getWindowAndroid(), mActivity.getActivityTabProvider()); |
| mLocationBar.addUrlFocusChangeListener(mLocationBarFocusObserver); |
| |
| setMenuHandler(menuHandler); |
| mToolbar.initialize(mLocationBarModel, this, mAppMenuButtonHelper); |
| |
| mAppMenuPropertiesDelegate = appMenuPropertiesDelegate; |
| |
| mOmniboxStartupMetrics = new OmniboxStartupMetrics(mActivity); |
| |
| mTabModelSelectorObserver = new EmptyTabModelSelectorObserver() { |
| @Override |
| public void onTabModelSelected(TabModel newModel, TabModel oldModel) { |
| refreshSelectedTab(); |
| } |
| |
| @Override |
| public void onTabStateInitialized() { |
| mTabRestoreCompleted = true; |
| handleTabRestoreCompleted(); |
| } |
| }; |
| |
| mTabModelObserver = new EmptyTabModelObserver() { |
| @Override |
| public void didSelectTab(Tab tab, @TabSelectionType int type, int lastId) { |
| mPreselectedTabId = Tab.INVALID_TAB_ID; |
| refreshSelectedTab(); |
| } |
| |
| @Override |
| public void tabClosureUndone(Tab tab) { |
| refreshSelectedTab(); |
| } |
| |
| @Override |
| public void didCloseTab(int tabId, boolean incognito) { |
| mLocationBar.setTitleToPageTitle(); |
| refreshSelectedTab(); |
| } |
| |
| @Override |
| public void tabPendingClosure(Tab tab) { |
| refreshSelectedTab(); |
| } |
| |
| @Override |
| public void allTabsPendingClosure(List<Tab> tabs) { |
| refreshSelectedTab(); |
| } |
| |
| @Override |
| public void tabRemoved(Tab tab) { |
| refreshSelectedTab(); |
| } |
| }; |
| |
| mTabObserver = new EmptyTabObserver() { |
| @Override |
| public void onSSLStateUpdated(Tab tab) { |
| if (mLocationBarModel.getTab() == null) return; |
| |
| assert tab == mLocationBarModel.getTab(); |
| mLocationBar.updateStatusIcon(); |
| mLocationBar.setUrlToPageUrl(); |
| } |
| |
| @Override |
| public void didReloadLoFiImages(Tab tab) { |
| mLocationBar.updateStatusIcon(); |
| } |
| |
| @Override |
| public void onTitleUpdated(Tab tab) { |
| mLocationBar.setTitleToPageTitle(); |
| } |
| |
| @Override |
| public void onUrlUpdated(Tab tab) { |
| // Update the SSL security state as a result of this notification as it will |
| // sometimes be the only update we receive. |
| updateTabLoadingState(true); |
| |
| // A URL update is a decent enough indicator that the toolbar widget is in |
| // a stable state to capture its bitmap for use in fullscreen. |
| mControlContainer.setReadyForBitmapCapture(true); |
| } |
| |
| @Override |
| public void onShown(Tab tab, @TabSelectionType int type) { |
| if (TextUtils.isEmpty(tab.getUrl())) return; |
| mControlContainer.setReadyForBitmapCapture(true); |
| } |
| |
| @Override |
| public void onCrash(Tab tab) { |
| updateTabLoadingState(false); |
| updateButtonStatus(); |
| finishLoadProgress(false); |
| } |
| |
| @Override |
| public void onPageLoadFinished(Tab tab, String url) { |
| if (tab.isShowingErrorPage()) { |
| handleIPHForErrorPageShown(tab); |
| return; |
| } |
| |
| // TODO(crbug.com/896476): Remove this. |
| if (tab.isPreview()) { |
| // Some previews (like Client LoFi) are not fully decided until the page |
| // finishes loading. If this is a preview, update the security icon which will |
| // also update the verbose status view to make sure the "Lite" badge is |
| // displayed. |
| mLocationBar.updateStatusIcon(); |
| PreviewsUma.recordLitePageAtLoadFinish( |
| PreviewsAndroidBridge.getInstance().getPreviewsType( |
| tab.getWebContents())); |
| } |
| |
| handleIPHForSuccessfulPageLoad(tab); |
| } |
| |
| @Override |
| public void onLoadStarted(Tab tab, boolean toDifferentDocument) { |
| if (!toDifferentDocument) return; |
| updateButtonStatus(); |
| updateTabLoadingState(true); |
| } |
| |
| @Override |
| public void onLoadStopped(Tab tab, boolean toDifferentDocument) { |
| if (!toDifferentDocument) return; |
| updateTabLoadingState(true); |
| |
| // If we made some progress, fast-forward to complete, otherwise just dismiss any |
| // MINIMUM_LOAD_PROGRESS that had been set. |
| if (tab.getProgress() > MINIMUM_LOAD_PROGRESS && tab.getProgress() < 100) { |
| updateLoadProgress(100); |
| } |
| finishLoadProgress(true); |
| } |
| |
| @Override |
| public void onLoadProgressChanged(Tab tab, int progress) { |
| if (NativePageFactory.isNativePageUrl(tab.getUrl(), tab.isIncognito())) return; |
| |
| // TODO(kkimlabs): Investigate using float progress all the way up to Blink. |
| updateLoadProgress(progress); |
| } |
| |
| @Override |
| public void onEnterFullscreenMode(Tab tab, FullscreenOptions options) { |
| if (mFindToolbarManager != null) { |
| mFindToolbarManager.hideToolbar(); |
| } |
| } |
| |
| @Override |
| public void onContentChanged(Tab tab) { |
| if (tab.isNativePage()) TabThemeColorHelper.get(tab).updateIfNeeded(false); |
| mToolbar.onTabContentViewChanged(); |
| if (shouldShowCursorInLocationBar()) { |
| mLocationBar.showUrlBarCursorWithoutFocusAnimations(); |
| } |
| } |
| |
| @Override |
| public void onWebContentsSwapped(Tab tab, boolean didStartLoad, boolean didFinishLoad) { |
| if (!didStartLoad) return; |
| mLocationBar.updateLoadingState(true); |
| if (didFinishLoad) { |
| mLoadProgressSimulator.start(); |
| } |
| } |
| |
| @Override |
| public void onLoadUrl(Tab tab, LoadUrlParams params, int loadType) { |
| NewTabPage ntp = mLocationBarModel.getNewTabPageForCurrentTab(); |
| if (ntp == null) return; |
| if (!NewTabPage.isNTPUrl(params.getUrl()) |
| && loadType != TabLoadStatus.PAGE_LOAD_FAILED) { |
| ntp.setUrlFocusAnimationsDisabled(true); |
| mToolbar.onTabOrModelChanged(); |
| } |
| } |
| |
| private boolean hasPendingNonNtpNavigation(Tab tab) { |
| WebContents webContents = tab.getWebContents(); |
| if (webContents == null) return false; |
| |
| NavigationController navigationController = webContents.getNavigationController(); |
| if (navigationController == null) return false; |
| |
| NavigationEntry pendingEntry = navigationController.getPendingEntry(); |
| if (pendingEntry == null) return false; |
| |
| return !NewTabPage.isNTPUrl(pendingEntry.getUrl()); |
| } |
| |
| @Override |
| public void onContextualActionBarVisibilityChanged(Tab tab, boolean visible) { |
| if (visible) RecordUserAction.record("MobileActionBarShown"); |
| ActionBar actionBar = mActionBarDelegate.getSupportActionBar(); |
| if (!visible && actionBar != null) actionBar.hide(); |
| if (mActivity.isTablet()) { |
| if (visible) { |
| mActionModeController.startShowAnimation(); |
| } else { |
| mActionModeController.startHideAnimation(); |
| } |
| } |
| } |
| |
| @Override |
| public void onDidStartNavigation(Tab tab, String url, boolean isInMainFrame, |
| boolean isSameDocument, long navigationHandleProxy) { |
| if (!isInMainFrame) return; |
| // Update URL as soon as it becomes available when it's a new tab. |
| // But we want to update only when it's a new tab. So we check whether the current |
| // navigation entry is initial, meaning whether it has the same target URL as the |
| // initial URL of the tab. |
| if (tab.getWebContents() != null |
| && tab.getWebContents().getNavigationController() != null |
| && tab.getWebContents().getNavigationController().isInitialNavigation()) { |
| mLocationBar.setUrlToPageUrl(); |
| } |
| |
| if (isSameDocument) return; |
| // This event is used as the primary trigger for the progress bar because it |
| // is the earliest indication that a load has started for a particular frame. In |
| // the case of the progress bar, it should only traverse the screen a single time |
| // per page load. So if this event states the main frame has started loading the |
| // progress bar is started. |
| |
| if (NativePageFactory.isNativePageUrl(url, tab.isIncognito())) { |
| finishLoadProgress(false); |
| return; |
| } |
| |
| mLoadProgressSimulator.cancel(); |
| startLoadProgress(); |
| updateLoadProgress(tab.getProgress()); |
| } |
| |
| @Override |
| public void onDidFinishNavigation(Tab tab, String url, boolean isInMainFrame, |
| boolean isErrorPage, boolean hasCommitted, boolean isSameDocument, |
| boolean isFragmentNavigation, Integer pageTransition, int errorCode, |
| int httpStatusCode) { |
| if (hasCommitted && isInMainFrame && !isSameDocument) { |
| mToolbar.onNavigatedToDifferentPage(); |
| } |
| |
| if (hasCommitted && tab.isPreview()) { |
| // Some previews are not fully decided until the page commits. If this |
| // is a preview, update the security icon which will also update the verbose |
| // status view to make sure the "Lite" badge is displayed. |
| mLocationBar.updateStatusIcon(); |
| PreviewsUma.recordLitePageAtCommit( |
| PreviewsAndroidBridge.getInstance().getPreviewsType( |
| tab.getWebContents()), |
| isInMainFrame); |
| } |
| |
| // If the load failed due to a different navigation, there is no need to reset the |
| // location bar animations. |
| if (errorCode != 0 && isInMainFrame && !hasPendingNonNtpNavigation(tab)) { |
| NewTabPage ntp = mLocationBarModel.getNewTabPageForCurrentTab(); |
| if (ntp == null) return; |
| |
| ntp.setUrlFocusAnimationsDisabled(false); |
| mToolbar.onTabOrModelChanged(); |
| if (mToolbar.getProgressBar() != null) mToolbar.getProgressBar().finish(false); |
| } |
| } |
| |
| @Override |
| public void onNavigationEntriesDeleted(Tab tab) { |
| if (tab == mLocationBarModel.getTab()) { |
| updateButtonStatus(); |
| } |
| } |
| |
| private void handleIPHForSuccessfulPageLoad(final Tab tab) { |
| if (mTextBubble != null) { |
| mTextBubble.dismiss(); |
| mTextBubble = null; |
| return; |
| } |
| |
| showDownloadPageTextBubble(tab, FeatureConstants.DOWNLOAD_PAGE_FEATURE); |
| showTranslateMenuButtonTextBubble( |
| tab, FeatureConstants.TRANSLATE_MENU_BUTTON_FEATURE); |
| } |
| |
| private void handleIPHForErrorPageShown(Tab tab) { |
| if (!(mActivity instanceof ChromeTabbedActivity) || mActivity.isTablet()) { |
| return; |
| } |
| |
| OfflinePageBridge bridge = OfflinePageBridge.getForProfile(tab.getProfile()); |
| if (bridge == null |
| || !bridge.isShowingDownloadButtonInErrorPage(tab.getWebContents())) { |
| return; |
| } |
| |
| Tracker tracker = TrackerFactory.getTrackerForProfile(tab.getProfile()); |
| tracker.notifyEvent(EventConstants.USER_HAS_SEEN_DINO); |
| } |
| }; |
| |
| mBookmarksObserver = new BookmarkBridge.BookmarkModelObserver() { |
| @Override |
| public void bookmarkModelChanged() { |
| updateBookmarkButtonStatus(); |
| } |
| }; |
| |
| mFindToolbarObserver = new FindToolbarObserver() { |
| @Override |
| public void onFindToolbarShown() { |
| mToolbar.handleFindLocationBarStateChange(true); |
| if (mControlsVisibilityDelegate != null) { |
| mFullscreenFindInPageToken = |
| mControlsVisibilityDelegate.showControlsPersistentAndClearOldToken( |
| mFullscreenFindInPageToken); |
| } |
| } |
| |
| @Override |
| public void onFindToolbarHidden() { |
| mToolbar.handleFindLocationBarStateChange(false); |
| if (mControlsVisibilityDelegate != null) { |
| mControlsVisibilityDelegate.releasePersistentShowingToken( |
| mFullscreenFindInPageToken); |
| } |
| } |
| }; |
| |
| mOverviewModeObserver = new EmptyOverviewModeObserver() { |
| @Override |
| public void onOverviewModeStartedShowing(boolean showToolbar) { |
| mToolbar.setTabSwitcherMode(true, showToolbar, false); |
| updateButtonStatus(); |
| } |
| |
| @Override |
| public void onOverviewModeStartedHiding(boolean showToolbar, boolean delayAnimation) { |
| mToolbar.setTabSwitcherMode(false, showToolbar, delayAnimation); |
| updateButtonStatus(); |
| } |
| |
| @Override |
| public void onOverviewModeFinishedHiding() { |
| mToolbar.onTabSwitcherTransitionFinished(); |
| } |
| }; |
| |
| mSceneChangeObserver = new SceneChangeObserver() { |
| @Override |
| public void onTabSelectionHinted(int tabId) { |
| mPreselectedTabId = tabId; |
| refreshSelectedTab(); |
| |
| if (mToolbar.setForceTextureCapture(true)) { |
| mControlContainer.invalidateBitmap(); |
| } |
| } |
| |
| @Override |
| public void onSceneChange(Layout layout) { |
| mToolbar.setContentAttached(layout.shouldDisplayContentOverlay()); |
| } |
| }; |
| |
| mLoadProgressSimulator = new LoadProgressSimulator(this); |
| |
| mToolbar.setTabCountProvider(mTabCountProvider); |
| mToolbar.setIncognitoStateProvider(mIncognitoStateProvider); |
| mToolbar.setThemeColorProvider(mThemeColorProvider); |
| } |
| |
| /** |
| * @return Whether the UrlBar currently has focus. |
| */ |
| public boolean isUrlBarFocused() { |
| return mLocationBar == null ? false : mLocationBar.isUrlBarFocused(); |
| } |
| |
| /** |
| * @param reason A {@link OmniboxFocusReason} that the omnibox was focused. |
| */ |
| public static void recordOmniboxFocusReason(@OmniboxFocusReason int reason) { |
| ENUMERATED_FOCUS_REASON.record(reason); |
| } |
| |
| /** |
| * Enable the bottom toolbar. |
| */ |
| public void enableBottomToolbar() { |
| // TODO(amaralp): Move creation of these listeners to bottom toolbar component. |
| final OnClickListener homeButtonListener = v -> { |
| recordBottomToolbarUseForIPH(); |
| openHomepage(); |
| }; |
| |
| final OnClickListener searchAcceleratorListener = v -> { |
| recordBottomToolbarUseForIPH(); |
| recordOmniboxFocusReason(OmniboxFocusReason.ACCELERATOR_TAP); |
| ACCELERATOR_BUTTON_TAP_ACTION.record(); |
| setUrlBarFocus(true); |
| }; |
| |
| final OnClickListener shareButtonListener = v -> { |
| recordBottomToolbarUseForIPH(); |
| boolean isIncognito = false; |
| if (mTabModelSelector != null) { |
| isIncognito = mTabModelSelector.getCurrentTab().isIncognito(); |
| } |
| mActivity.onShareMenuItemSelected(false, isIncognito); |
| }; |
| |
| mBottomToolbarCoordinator = new BottomToolbarCoordinator(mActivity.getFullscreenManager(), |
| mActivity.findViewById(R.id.bottom_toolbar_stub), |
| mActivity.getActivityTabProvider(), homeButtonListener, searchAcceleratorListener, |
| shareButtonListener); |
| if (mAppMenuButtonHelper != null) mAppMenuButtonHelper.setMenuShowsFromBottom(true); |
| } |
| |
| /** Record that homepage button was used for IPH reasons */ |
| private void recordToolbarUseForIPH(String toolbarIPHEvent) { |
| if (mTabModelSelector != null && mTabModelSelector.getCurrentTab() != null) { |
| Tab tab = mTabModelSelector.getCurrentTab(); |
| Tracker tracker = TrackerFactory.getTrackerForProfile(tab.getProfile()); |
| tracker.notifyEvent(toolbarIPHEvent); |
| } |
| } |
| |
| /** Record that the bottom toolbar was used for IPH reasons. */ |
| private void recordBottomToolbarUseForIPH() { |
| recordToolbarUseForIPH(EventConstants.CHROME_DUET_USED_BOTTOM_TOOLBAR); |
| } |
| |
| /** |
| * Add bottom toolbar IPH tracking to an existing click listener. |
| * @param listener The listener to add bottom toolbar tracking to. |
| */ |
| private OnClickListener wrapBottomToolbarClickListenerForIPH(OnClickListener listener) { |
| return (v) -> { |
| recordBottomToolbarUseForIPH(); |
| listener.onClick(v); |
| }; |
| } |
| |
| /** |
| * @return Whether the bottom toolbar is currently enabled (an activity may or may not enable |
| * this feature). |
| */ |
| public boolean isBottomToolbarEnabled() { |
| return mBottomToolbarCoordinator != null; |
| } |
| |
| /** |
| * @return The coordinator for the bottom toolbar if it exists. |
| */ |
| public BottomToolbarCoordinator getBottomToolbarCoordinator() { |
| return mBottomToolbarCoordinator; |
| } |
| |
| private void showMenuIPHTextBubble(ChromeActivity activity, Tracker tracker, String featureName, |
| @StringRes int stringId, @StringRes int accessibilityStringId, |
| Integer highlightItemId) { |
| ViewRectProvider rectProvider = new ViewRectProvider(getMenuButton()); |
| int yInsetPx = mActivity.getResources().getDimensionPixelOffset( |
| R.dimen.text_bubble_menu_anchor_y_inset); |
| rectProvider.setInsetPx(0, 0, 0, yInsetPx); |
| mTextBubble = new TextBubble( |
| mActivity, getMenuButton(), stringId, accessibilityStringId, rectProvider); |
| mTextBubble.setDismissOnTouchInteraction(true); |
| mTextBubble.addOnDismissListener(() -> { |
| mHandler.postDelayed(() -> { |
| tracker.dismissed(featureName); |
| activity.getAppMenuHandler().setMenuHighlight(null); |
| }, ViewHighlighter.IPH_MIN_DELAY_BETWEEN_TWO_HIGHLIGHTS); |
| }); |
| activity.getAppMenuHandler().setMenuHighlight(highlightItemId); |
| mTextBubble.show(); |
| } |
| |
| /** |
| * Show the download page in-product-help bubble. Also used by download page screenshot IPH. |
| * @param tab The current tab. |
| * @param featureName The associated feature name. |
| */ |
| public void showDownloadPageTextBubble(final Tab tab, String featureName) { |
| if (tab == null || !mToolbarInflationComplete) return; |
| |
| // TODO(shaktisahu): Find out if the download menu button is enabled (crbug/712438). |
| ChromeActivity activity = tab.getActivity(); |
| if (!(activity instanceof ChromeTabbedActivity) || activity.isTablet() |
| || activity.isInOverviewMode() || !DownloadUtils.isAllowedToDownloadPage(tab)) { |
| return; |
| } |
| |
| final Tracker tracker = TrackerFactory.getTrackerForProfile(tab.getProfile()); |
| if (!tracker.shouldTriggerHelpUI(featureName)) return; |
| |
| showMenuIPHTextBubble(activity, tracker, featureName, |
| R.string.iph_download_page_for_offline_usage_text, |
| R.string.iph_download_page_for_offline_usage_accessibility_text, |
| R.id.offline_page_id); |
| |
| // Record metrics if we show Download IPH after a screenshot of the page. |
| ChromeTabbedActivity chromeActivity = ((ChromeTabbedActivity) activity); |
| ScreenshotTabObserver tabObserver = |
| ScreenshotTabObserver.from(chromeActivity.getActivityTab()); |
| if (tabObserver != null) { |
| tabObserver.onActionPerformedAfterScreenshot( |
| ScreenshotTabObserver.SCREENSHOT_ACTION_DOWNLOAD_IPH); |
| } |
| } |
| |
| /** |
| * Show the translate manual trigger in-product-help bubble. |
| * @param tab The current tab. |
| * @param featureName The associated feature name. |
| */ |
| public void showTranslateMenuButtonTextBubble(final Tab tab, String featureName) { |
| if (tab == null || !mToolbarInflationComplete) return; |
| ChromeActivity activity = tab.getActivity(); |
| |
| if (!mAppMenuPropertiesDelegate.isTranslateMenuItemVisible(tab)) return; |
| if (!TranslateBridge.shouldShowManualTranslateIPH(tab)) return; |
| |
| // Find out if the help UI should appear. |
| final Tracker tracker = TrackerFactory.getTrackerForProfile(tab.getProfile()); |
| if (!tracker.shouldTriggerHelpUI(featureName)) return; |
| |
| showMenuIPHTextBubble(activity, tracker, featureName, |
| R.string.iph_translate_menu_button_text, |
| R.string.iph_translate_menu_button_accessibility_text, R.id.translate_id); |
| } |
| |
| /** |
| * Initialize the manager with the components that had native initialization dependencies. |
| * <p> |
| * Calling this must occur after the native library have completely loaded. |
| * |
| * @param tabModelSelector The selector that handles tab management. |
| * @param controlsVisibilityDelegate The delegate to handle visibility of browser controls. |
| * @param findToolbarManager The manager for find in page. |
| * @param overviewModeBehavior The overview mode manager. |
| * @param layoutManager A {@link LayoutManager} instance used to watch for scene |
| * changes. |
| */ |
| public void initializeWithNative(TabModelSelector tabModelSelector, |
| BrowserStateBrowserControlsVisibilityDelegate controlsVisibilityDelegate, |
| FindToolbarManager findToolbarManager, OverviewModeBehavior overviewModeBehavior, |
| LayoutManager layoutManager, OnClickListener tabSwitcherClickHandler, |
| OnClickListener newTabClickHandler, OnClickListener bookmarkClickHandler, |
| OnClickListener customTabsBackClickHandler) { |
| assert !mInitializedWithNative; |
| |
| mToolbarProvider.whenLoaded((toolbar) -> { |
| mTabModelSelector = tabModelSelector; |
| |
| mToolbar.initializeWithNative(tabModelSelector, controlsVisibilityDelegate, |
| layoutManager, tabSwitcherClickHandler, newTabClickHandler, |
| bookmarkClickHandler, customTabsBackClickHandler); |
| |
| toolbar.addOnAttachStateChangeListener(new OnAttachStateChangeListener() { |
| @Override |
| public void onViewDetachedFromWindow(View v) {} |
| |
| @Override |
| public void onViewAttachedToWindow(View v) { |
| // As we have only just registered for notifications, any that were sent prior |
| // to this may have been missed. Calling refreshSelectedTab in case we missed |
| // the initial selection notification. |
| refreshSelectedTab(); |
| } |
| }); |
| |
| mLocationBarModel.initializeWithNative(); |
| |
| mFindToolbarManager = findToolbarManager; |
| |
| assert controlsVisibilityDelegate != null; |
| mControlsVisibilityDelegate = controlsVisibilityDelegate; |
| |
| mNativeLibraryReady = false; |
| |
| mFindToolbarManager.addObserver(mFindToolbarObserver); |
| |
| if (overviewModeBehavior != null) { |
| mOverviewModeBehavior = overviewModeBehavior; |
| mOverviewModeBehavior.addOverviewModeObserver(mOverviewModeObserver); |
| } |
| if (layoutManager != null) { |
| mLayoutManager = layoutManager; |
| mLayoutManager.addSceneChangeObserver(mSceneChangeObserver); |
| } |
| |
| if (mBottomToolbarCoordinator != null) { |
| final OnClickListener closeTabsClickListener = v -> { |
| recordBottomToolbarUseForIPH(); |
| final boolean isIncognito = mTabModelSelector.isIncognitoSelected(); |
| if (isIncognito) { |
| RecordUserAction.record("MobileToolbarCloseAllIncognitoTabsButtonTap"); |
| } else { |
| RecordUserAction.record("MobileToolbarCloseAllRegularTabsButtonTap"); |
| } |
| |
| mTabModelSelector.getModel(isIncognito).closeAllTabs(); |
| }; |
| mAppMenuButtonHelper.setOnClickRunnable(() -> recordBottomToolbarUseForIPH()); |
| mBottomToolbarCoordinator.initializeWithNative( |
| mActivity.getCompositorViewHolder().getResourceManager(), |
| mActivity.getCompositorViewHolder().getLayoutManager(), |
| wrapBottomToolbarClickListenerForIPH(tabSwitcherClickHandler), |
| wrapBottomToolbarClickListenerForIPH(newTabClickHandler), |
| closeTabsClickListener, mAppMenuButtonHelper, mTabModelSelector, |
| mOverviewModeBehavior, mActivity.getWindowAndroid(), mTabCountProvider, |
| mIncognitoStateProvider); |
| |
| // Allow the bottom toolbar to be focused in accessibility after the top toolbar. |
| ApiCompatibilityUtils.setAccessibilityTraversalBefore( |
| mLocationBar.getContainerView(), R.id.bottom_toolbar); |
| } |
| |
| onNativeLibraryReady(); |
| mInitializedWithNative = true; |
| }); |
| } |
| |
| /** |
| * Show the update badge in both the top and bottom toolbar. |
| * TODO(amaralp): Only the top or bottom menu should be visible. |
| */ |
| public void showAppMenuUpdateBadge() { |
| mToolbar.showAppMenuUpdateBadge(); |
| if (mBottomToolbarCoordinator != null) { |
| mBottomToolbarCoordinator.showAppMenuUpdateBadge(); |
| } |
| } |
| |
| /** |
| * Remove the update badge in both the top and bottom toolbar. |
| * TODO(amaralp): Only the top or bottom menu should be visible. |
| */ |
| public void removeAppMenuUpdateBadge(boolean animate) { |
| mToolbar.removeAppMenuUpdateBadge(animate); |
| if (mBottomToolbarCoordinator != null) { |
| mBottomToolbarCoordinator.removeAppMenuUpdateBadge(); |
| } |
| } |
| |
| /** |
| * @return Whether the badge is showing (either in the top or bottom toolbar). |
| * TODO(amaralp): Only the top or bottom menu should be visible. |
| */ |
| public boolean isShowingAppMenuUpdateBadge() { |
| if (mBottomToolbarCoordinator != null |
| && mBottomToolbarCoordinator.isShowingAppMenuUpdateBadge()) { |
| return true; |
| } |
| return mToolbar.isShowingAppMenuUpdateBadge(); |
| } |
| |
| /** |
| * Enable the experimental toolbar button. |
| * @param onClickListener The {@link OnClickListener} to be called when the button is clicked. |
| * @param drawableResId The resource id of the drawable to display for the button. |
| * @param contentDescriptionResId The resource id of the content description for the button. |
| */ |
| public void enableExperimentalButton(OnClickListener onClickListener, |
| @DrawableRes int drawableResId, @StringRes int contentDescriptionResId) { |
| mToolbar.enableExperimentalButton(onClickListener, drawableResId, contentDescriptionResId); |
| } |
| |
| /** |
| * Disable the experimental toolbar button. |
| */ |
| public void disableExperimentalButton() { |
| mToolbar.disableExperimentalButton(); |
| } |
| |
| /** |
| * @return The experimental toolbar button if it exists. |
| */ |
| public @Nullable View getExperimentalButtonView() { |
| return mToolbar.getExperimentalButtonView(); |
| } |
| |
| /** |
| * @return The bookmarks bridge. |
| */ |
| public BookmarkBridge getBookmarkBridge() { |
| return mBookmarkBridge; |
| } |
| |
| /** |
| * @return The toolbar interface that this manager handles. |
| */ |
| @Nullable |
| public Toolbar getToolbar() { |
| return mToolbar; |
| } |
| |
| /** |
| * @return The callback for toolbar action mode controller. |
| */ |
| public ToolbarActionModeCallback getActionModeControllerCallback() { |
| return mToolbarActionModeCallback; |
| } |
| |
| /** |
| * @return Whether the UI has been initialized. |
| */ |
| public boolean isInitialized() { |
| return mInitializedWithNative; |
| } |
| |
| /** |
| * @return The view containing the pop up menu button. |
| */ |
| public @Nullable View getMenuButton() { |
| if (mBottomToolbarCoordinator != null) { |
| return mBottomToolbarCoordinator.getMenuButtonWrapper().getImageButton(); |
| } |
| return mToolbar.getMenuButton(); |
| } |
| |
| /** |
| * TODO(twellington): Try to remove this method. It's only used to return an in-product help |
| * bubble anchor view... which should be moved out of tab and perhaps into |
| * the status bar icon component. |
| * @return The view containing the security icon. |
| */ |
| public View getSecurityIconView() { |
| if (mLocationBar == null) return null; |
| return mLocationBar.getSecurityIconView(); |
| } |
| |
| /** |
| * Adds a custom action button to the {@link Toolbar}, if it is supported. |
| * @param drawable The {@link Drawable} to use as the background for the button. |
| * @param description The content description for the custom action button. |
| * @param listener The {@link OnClickListener} to use for clicks to the button. |
| * @see #updateCustomActionButton |
| */ |
| public void addCustomActionButton( |
| Drawable drawable, String description, OnClickListener listener) { |
| mToolbar.addCustomActionButton(drawable, description, listener); |
| } |
| |
| /** |
| * Updates the visual appearance of a custom action button in the {@link Toolbar}, |
| * if it is supported. |
| * @param index The index of the button to update. |
| * @param drawable The {@link Drawable} to use as the background for the button. |
| * @param description The content description for the custom action button. |
| * @see #addCustomActionButton |
| */ |
| public void updateCustomActionButton(int index, Drawable drawable, String description) { |
| mToolbar.updateCustomActionButton(index, drawable, description); |
| } |
| |
| /** |
| * Call to tear down all of the toolbar dependencies. |
| */ |
| public void destroy() { |
| if (mInitializedWithNative) { |
| mFindToolbarManager.removeObserver(mFindToolbarObserver); |
| } |
| if (mTabModelSelector != null) { |
| mTabModelSelector.removeObserver(mTabModelSelectorObserver); |
| for (TabModel model : mTabModelSelector.getModels()) { |
| model.removeObserver(mTabModelObserver); |
| } |
| } |
| if (mBookmarkBridge != null) { |
| mBookmarkBridge.destroy(); |
| mBookmarkBridge = null; |
| } |
| if (mTemplateUrlObserver != null) { |
| TemplateUrlService.getInstance().removeObserver(mTemplateUrlObserver); |
| mTemplateUrlObserver = null; |
| } |
| if (mOverviewModeBehavior != null) { |
| mOverviewModeBehavior.removeOverviewModeObserver(mOverviewModeObserver); |
| mOverviewModeBehavior = null; |
| } |
| if (mLayoutManager != null) { |
| mLayoutManager.removeSceneChangeObserver(mSceneChangeObserver); |
| mLayoutManager = null; |
| } |
| |
| if (mBottomToolbarCoordinator != null) { |
| mBottomToolbarCoordinator.destroy(); |
| mBottomToolbarCoordinator = null; |
| } |
| |
| if (mOmniboxStartupMetrics != null) { |
| // Record the histogram before destroying, if we have the data. |
| if (mInitializedWithNative) mOmniboxStartupMetrics.maybeRecordHistograms(); |
| mOmniboxStartupMetrics.destroy(); |
| mOmniboxStartupMetrics = null; |
| } |
| |
| if (mLocationBar != null) { |
| mLocationBar.removeUrlFocusChangeListener(this); |
| } |
| mToolbar.destroy(); |
| |
| if (mTabObserver != null) { |
| Tab currentTab = mLocationBarModel.getTab(); |
| if (currentTab != null) currentTab.removeObserver(mTabObserver); |
| mTabObserver = null; |
| } |
| |
| mIncognitoStateProvider.destroy(); |
| mTabCountProvider.destroy(); |
| |
| mLocationBarModel.destroy(); |
| mHandler.removeCallbacksAndMessages(null); // Cancel delayed tasks. |
| if (mLocationBar != null) { |
| mLocationBar.removeUrlFocusChangeListener(mLocationBarFocusObserver); |
| mLocationBarFocusObserver = null; |
| } |
| |
| if (mThemeColorProvider != null) mThemeColorProvider.removeThemeColorObserver(this); |
| } |
| |
| /** |
| * Called when the orientation of the activity has changed. |
| */ |
| public void onOrientationChange() { |
| if (mActionModeController == null) return; |
| mActionModeController.showControlsOnOrientationChange(); |
| } |
| |
| /** |
| * Called when the accessibility enabled state changes. |
| * @param enabled Whether accessibility is enabled. |
| */ |
| public void onAccessibilityStatusChanged(boolean enabled) { |
| mToolbar.onAccessibilityStatusChanged(enabled); |
| } |
| |
| private void registerTemplateUrlObserver() { |
| final TemplateUrlService templateUrlService = TemplateUrlService.getInstance(); |
| assert mTemplateUrlObserver == null; |
| mTemplateUrlObserver = new TemplateUrlServiceObserver() { |
| private TemplateUrl mSearchEngine = |
| templateUrlService.getDefaultSearchEngineTemplateUrl(); |
| |
| @Override |
| public void onTemplateURLServiceChanged() { |
| TemplateUrl searchEngine = templateUrlService.getDefaultSearchEngineTemplateUrl(); |
| if ((mSearchEngine == null && searchEngine == null) |
| || (mSearchEngine != null && mSearchEngine.equals(searchEngine))) { |
| return; |
| } |
| |
| mSearchEngine = searchEngine; |
| mToolbar.onDefaultSearchEngineChanged(); |
| } |
| }; |
| templateUrlService.addObserver(mTemplateUrlObserver); |
| } |
| |
| private void onNativeLibraryReady() { |
| mNativeLibraryReady = true; |
| |
| final TemplateUrlService templateUrlService = TemplateUrlService.getInstance(); |
| TemplateUrlService.LoadListener mTemplateServiceLoadListener = |
| new TemplateUrlService.LoadListener() { |
| @Override |
| public void onTemplateUrlServiceLoaded() { |
| registerTemplateUrlObserver(); |
| templateUrlService.unregisterLoadListener(this); |
| } |
| }; |
| templateUrlService.registerLoadListener(mTemplateServiceLoadListener); |
| if (templateUrlService.isLoaded()) { |
| mTemplateServiceLoadListener.onTemplateUrlServiceLoaded(); |
| } else { |
| templateUrlService.load(); |
| } |
| |
| mTabModelSelector.addObserver(mTabModelSelectorObserver); |
| for (TabModel model : mTabModelSelector.getModels()) model.addObserver(mTabModelObserver); |
| |
| refreshSelectedTab(); |
| if (mTabModelSelector.isTabStateInitialized()) mTabRestoreCompleted = true; |
| handleTabRestoreCompleted(); |
| mTabCountProvider.setTabModelSelector(mTabModelSelector); |
| mIncognitoStateProvider.setTabModelSelector(mTabModelSelector); |
| } |
| |
| private void handleTabRestoreCompleted() { |
| if (!mTabRestoreCompleted || !mNativeLibraryReady) return; |
| mToolbar.onStateRestored(); |
| } |
| |
| /** |
| * Sets the handler for any special case handling related with the menu button. |
| * @param menuHandler The handler to be used. |
| */ |
| private void setMenuHandler(AppMenuHandler menuHandler) { |
| menuHandler.addObserver(new AppMenuObserver() { |
| @Override |
| public void onMenuVisibilityChanged(boolean isVisible) { |
| if (isVisible) { |
| // Defocus here to avoid handling focus in multiple places, e.g., when the |
| // forward button is pressed. (see crbug.com/414219) |
| setUrlBarFocus(false); |
| } |
| |
| if (mControlsVisibilityDelegate != null) { |
| if (isVisible) { |
| mFullscreenMenuToken = |
| mControlsVisibilityDelegate.showControlsPersistentAndClearOldToken( |
| mFullscreenMenuToken); |
| } else { |
| mControlsVisibilityDelegate.releasePersistentShowingToken( |
| mFullscreenMenuToken); |
| } |
| } |
| |
| MenuButton menuButton = getMenuButtonWrapper(); |
| if (isVisible && menuButton != null && menuButton.isShowingAppMenuUpdateBadge()) { |
| UpdateMenuItemHelper.getInstance().onMenuButtonClicked(); |
| } |
| } |
| |
| @Override |
| public void onMenuHighlightChanged(boolean highlighting) { |
| final MenuButton menuButton = getMenuButtonWrapper(); |
| if (menuButton != null) menuButton.setMenuButtonHighlight(highlighting); |
| |
| if (mControlsVisibilityDelegate == null) return; |
| if (highlighting) { |
| mFullscreenHighlightToken = |
| mControlsVisibilityDelegate.showControlsPersistentAndClearOldToken( |
| mFullscreenHighlightToken); |
| } else { |
| mControlsVisibilityDelegate.releasePersistentShowingToken( |
| mFullscreenHighlightToken); |
| } |
| } |
| }); |
| mAppMenuButtonHelper = new AppMenuButtonHelper(menuHandler); |
| mAppMenuButtonHelper.setMenuShowsFromBottom(mBottomToolbarCoordinator != null); |
| mAppMenuButtonHelper.setOnAppMenuShownListener(() -> { |
| RecordUserAction.record("MobileToolbarShowMenu"); |
| mToolbar.onMenuShown(); |
| |
| // Assume data saver footer is shown only if data reduction proxy is enabled and |
| // Chrome home is not |
| if (DataReductionProxySettings.getInstance().isDataReductionProxyEnabled()) { |
| Tracker tracker = TrackerFactory.getTrackerForProfile(Profile.getLastUsedProfile()); |
| tracker.notifyEvent(EventConstants.OVERFLOW_OPENED_WITH_DATA_SAVER_SHOWN); |
| } |
| }); |
| } |
| |
| @Nullable |
| private MenuButton getMenuButtonWrapper() { |
| if (mBottomToolbarCoordinator != null) { |
| return mBottomToolbarCoordinator.getMenuButtonWrapper(); |
| } |
| |
| return mToolbar.getMenuButtonWrapper(); |
| } |
| |
| /** |
| * Set the delegate that will handle updates from toolbar driven state changes. |
| * @param menuDelegatePhone The menu delegate to be updated (only applicable to phones). |
| */ |
| private void setMenuDelegatePhone(MenuDelegatePhone menuDelegatePhone) { |
| mMenuDelegatePhone = menuDelegatePhone; |
| } |
| |
| @Override |
| public boolean back() { |
| Tab tab = mLocationBarModel.getTab(); |
| if (tab != null && tab.canGoBack()) { |
| tab.goBack(); |
| updateButtonStatus(); |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean forward() { |
| Tab tab = mLocationBarModel.getTab(); |
| if (tab != null && tab.canGoForward()) { |
| tab.goForward(); |
| updateButtonStatus(); |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| public void stopOrReloadCurrentTab() { |
| Tab currentTab = mLocationBarModel.getTab(); |
| if (currentTab != null) { |
| if (currentTab.isLoading()) { |
| currentTab.stopLoading(); |
| RecordUserAction.record("MobileToolbarStop"); |
| } else { |
| currentTab.reload(); |
| RecordUserAction.record("MobileToolbarReload"); |
| } |
| } |
| updateButtonStatus(); |
| } |
| |
| @Override |
| public void openHomepage() { |
| RecordUserAction.record("Home"); |
| |
| Tab currentTab = mLocationBarModel.getTab(); |
| if (currentTab == null) return; |
| String homePageUrl = HomepageManager.getHomepageUri(); |
| boolean isNewTabPageButtonEnabled = FeatureUtilities.isNewTabPageButtonEnabled(); |
| if (TextUtils.isEmpty(homePageUrl) || isNewTabPageButtonEnabled) { |
| homePageUrl = UrlConstants.NTP_URL; |
| } |
| if (isNewTabPageButtonEnabled) { |
| recordToolbarUseForIPH(EventConstants.CLEAR_TAB_BUTTON_CLICKED); |
| } else { |
| recordToolbarUseForIPH(EventConstants.HOMEPAGE_BUTTON_CLICKED); |
| } |
| currentTab.loadUrl(new LoadUrlParams(homePageUrl, PageTransition.HOME_PAGE)); |
| } |
| |
| /** |
| * Triggered when the URL input field has gained or lost focus. |
| * @param hasFocus Whether the URL field has gained focus. |
| */ |
| @Override |
| public void onUrlFocusChange(boolean hasFocus) { |
| assert mToolbarInflationComplete; |
| |
| mToolbar.onUrlFocusChange(hasFocus); |
| |
| if (hasFocus) mOmniboxStartupMetrics.onUrlBarFocused(); |
| |
| if (mFindToolbarManager != null && hasFocus) mFindToolbarManager.hideToolbar(); |
| |
| if (mControlsVisibilityDelegate == null) return; |
| if (hasFocus) { |
| mFullscreenFocusToken = |
| mControlsVisibilityDelegate.showControlsPersistentAndClearOldToken( |
| mFullscreenFocusToken); |
| } else { |
| mControlsVisibilityDelegate.releasePersistentShowingToken(mFullscreenFocusToken); |
| } |
| |
| mUrlFocusChangedCallback.onResult(hasFocus); |
| } |
| |
| /** |
| * Updates the primary color used by the model to the given color. |
| * @param color The primary color for the current tab. |
| * @param shouldAnimate Whether the change of color should be animated. |
| */ |
| @Override |
| public void onThemeColorChanged(int color, boolean shouldAnimate) { |
| if (!mShouldUpdateToolbarPrimaryColor) return; |
| |
| boolean colorChanged = mCurrentThemeColor != color; |
| if (!colorChanged) return; |
| |
| mCurrentThemeColor = color; |
| mLocationBarModel.setPrimaryColor(color); |
| mToolbar.onPrimaryColorChanged(shouldAnimate); |
| } |
| |
| /** |
| * @param shouldUpdate Whether we should be updating the toolbar primary color based on updates |
| * from the Tab. |
| */ |
| public void setShouldUpdateToolbarPrimaryColor(boolean shouldUpdate) { |
| mShouldUpdateToolbarPrimaryColor = shouldUpdate; |
| } |
| |
| /** |
| * @return Whether we should be updating the toolbar primary color based on updates from the |
| * Tab. |
| */ |
| public boolean getShouldUpdateToolbarPrimaryColor() { |
| return mShouldUpdateToolbarPrimaryColor; |
| } |
| |
| /** |
| * @return The primary toolbar color. |
| */ |
| public int getPrimaryColor() { |
| return mLocationBarModel.getPrimaryColor(); |
| } |
| |
| /** |
| * Gets the visibility of the Toolbar shadow. |
| * @return One of View.VISIBLE, View.INVISIBLE, or View.GONE. |
| */ |
| public int getToolbarShadowVisibility() { |
| View toolbarShadow = mControlContainer.findViewById(R.id.toolbar_shadow); |
| return (toolbarShadow != null) ? toolbarShadow.getVisibility() : View.GONE; |
| } |
| |
| /** |
| * Sets the visibility of the Toolbar shadow. |
| */ |
| public void setToolbarShadowVisibility(int visibility) { |
| View toolbarShadow = mControlContainer.findViewById(R.id.toolbar_shadow); |
| if (toolbarShadow != null) toolbarShadow.setVisibility(visibility); |
| } |
| |
| /** |
| * Gets the visibility of the Toolbar. |
| * @return One of View.VISIBLE, View.INVISIBLE, or View.GONE. |
| */ |
| public int getToolbarVisibility() { |
| View toolbar = getToolbarView(); |
| return (toolbar != null) ? toolbar.getVisibility() : View.GONE; |
| } |
| |
| /** |
| * Sets the visibility of the Toolbar. |
| */ |
| public void setToolbarVisibility(int visibility) { |
| View toolbar = getToolbarView(); |
| if (toolbar != null) toolbar.setVisibility(visibility); |
| } |
| |
| /** |
| * Gets the Toolbar view. |
| */ |
| @Nullable |
| public View getToolbarView() { |
| return mControlContainer.findViewById(R.id.toolbar); |
| } |
| |
| /** |
| * Sets the drawable that the close button shows, or hides it if {@code drawable} is |
| * {@code null}. |
| */ |
| public void setCloseButtonDrawable(Drawable drawable) { |
| mToolbar.setCloseButtonImageResource(drawable); |
| } |
| |
| /** |
| * Sets whether a title should be shown within the Toolbar. |
| * @param showTitle Whether a title should be shown. |
| */ |
| public void setShowTitle(boolean showTitle) { |
| mToolbar.setShowTitle(showTitle); |
| } |
| |
| /** |
| * @see ToolbarLayout#setUrlBarHidden(boolean) |
| */ |
| public void setUrlBarHidden(boolean hidden) { |
| mToolbar.setUrlBarHidden(hidden); |
| } |
| |
| /** |
| * @see ToolbarLayout#getContentPublisher() |
| */ |
| public String getContentPublisher() { |
| return mToolbar.getContentPublisher(); |
| } |
| |
| /** |
| * Focuses or unfocuses the URL bar. |
| * |
| * If you request focus and the UrlBar was already focused, this will select all of the text. |
| * |
| * @param focused Whether URL bar should be focused. |
| */ |
| public void setUrlBarFocus(boolean focused) { |
| if (!isInitialized()) return; |
| boolean wasFocused = mLocationBar.isUrlBarFocused(); |
| mLocationBar.setUrlBarFocus(focused); |
| if (wasFocused && focused) { |
| mLocationBar.selectAll(); |
| } |
| } |
| |
| /** |
| * Reverts any pending edits of the location bar and reset to the page state. This does not |
| * change the focus state of the location bar. |
| */ |
| public void revertLocationBarChanges() { |
| if (mLocationBar == null) return; |
| mLocationBar.revertChanges(); |
| } |
| |
| /** |
| * Handle all necessary tasks that can be delayed until initialization completes. |
| * @param activityCreationTimeMs The time of creation for the activity this toolbar belongs to. |
| * @param activityName Simple class name for the activity this toolbar belongs to. |
| */ |
| public void onDeferredStartup(final long activityCreationTimeMs, final String activityName) { |
| // Record startup performance statistics |
| long elapsedTime = SystemClock.elapsedRealtime() - activityCreationTimeMs; |
| if (elapsedTime < RECORD_UMA_PERFORMANCE_METRICS_DELAY_MS) { |
| PostTask.postDelayedTask(UiThreadTaskTraits.DEFAULT, () -> { |
| onDeferredStartup(activityCreationTimeMs, activityName); |
| }, RECORD_UMA_PERFORMANCE_METRICS_DELAY_MS - elapsedTime); |
| return; |
| } |
| RecordHistogram.recordTimesHistogram("MobileStartup.ToolbarFirstDrawTime2." + activityName, |
| mToolbar.getFirstDrawTime() - activityCreationTimeMs, TimeUnit.MILLISECONDS); |
| |
| // mOmniboxStartupMetrics might be null. ie. ToolbarManager is destroyed. See |
| // https://crbug.com/860449 |
| if (mOmniboxStartupMetrics != null) mOmniboxStartupMetrics.maybeRecordHistograms(); |
| } |
| |
| /** |
| * Finish any toolbar animations. |
| */ |
| public void finishAnimations() { |
| if (isInitialized()) mToolbar.finishAnimations(); |
| } |
| |
| /** |
| * See {@link LocationBar#updateVisualsForState()} |
| */ |
| public void updateLocationBarVisualsForState() { |
| mLocationBar.updateVisualsForState(); |
| } |
| |
| /** |
| * Updates the current button states and calls appropriate abstract visibility methods, giving |
| * inheriting classes the chance to update the button visuals as well. |
| */ |
| private void updateButtonStatus() { |
| assert mToolbarInflationComplete; |
| Tab currentTab = mLocationBarModel.getTab(); |
| boolean tabCrashed = currentTab != null && SadTab.isShowing(currentTab); |
| |
| mToolbar.updateButtonVisibility(); |
| mToolbar.updateBackButtonVisibility(currentTab != null && currentTab.canGoBack()); |
| mToolbar.updateForwardButtonVisibility(currentTab != null && currentTab.canGoForward()); |
| updateReloadState(tabCrashed); |
| updateBookmarkButtonStatus(); |
| if (mToolbar.getMenuButtonWrapper() != null) { |
| mToolbar.getMenuButtonWrapper().setVisibility(View.VISIBLE); |
| } |
| } |
| |
| private void updateBookmarkButtonStatus() { |
| assert mToolbarInflationComplete; |
| Tab currentTab = mLocationBarModel.getTab(); |
| boolean isBookmarked = |
| currentTab != null && currentTab.getBookmarkId() != Tab.INVALID_BOOKMARK_ID; |
| boolean editingAllowed = currentTab == null || mBookmarkBridge == null |
| || mBookmarkBridge.isEditBookmarksEnabled(); |
| mToolbar.updateBookmarkButton(isBookmarked, editingAllowed); |
| } |
| |
| private void updateReloadState(boolean tabCrashed) { |
| assert mToolbarInflationComplete; |
| Tab currentTab = mLocationBarModel.getTab(); |
| boolean isLoading = false; |
| if (!tabCrashed) { |
| isLoading = (currentTab != null && currentTab.isLoading()) || !mNativeLibraryReady; |
| } |
| mToolbar.updateReloadButtonVisibility(isLoading); |
| if (mMenuDelegatePhone != null) mMenuDelegatePhone.updateReloadButtonState(isLoading); |
| } |
| |
| /** |
| * Triggered when the selected tab has changed. |
| */ |
| private void refreshSelectedTab() { |
| assert mToolbarInflationComplete; |
| Tab tab = null; |
| if (mPreselectedTabId != Tab.INVALID_TAB_ID) { |
| tab = mTabModelSelector.getTabById(mPreselectedTabId); |
| } |
| if (tab == null) tab = mTabModelSelector.getCurrentTab(); |
| |
| boolean wasIncognito = mLocationBarModel.isIncognito(); |
| Tab previousTab = mLocationBarModel.getTab(); |
| |
| boolean isIncognito = |
| tab != null ? tab.isIncognito() : mTabModelSelector.isIncognitoSelected(); |
| mLocationBarModel.setTab(tab, isIncognito); |
| |
| updateCurrentTabDisplayStatus(); |
| |
| // This method is called prior to action mode destroy callback for incognito <-> normal |
| // tab switch. Makes sure the action mode toolbar is hidden before selecting the new tab. |
| if (previousTab != null && wasIncognito != isIncognito && mActivity.isTablet()) { |
| mActionModeController.startHideAnimation(); |
| } |
| if (previousTab != tab || wasIncognito != isIncognito) { |
| if (previousTab != tab) { |
| if (previousTab != null) { |
| previousTab.removeObserver(mTabObserver); |
| previousTab.setIsAllowedToReturnToExternalApp(false); |
| } |
| if (tab != null) tab.addObserver(mTabObserver); |
| } |
| |
| mToolbar.onTabOrModelChanged(); |
| |
| if (tab != null && tab.getWebContents() != null |
| && tab.getWebContents().isLoadingToDifferentDocument()) { |
| mToolbar.onNavigatedToDifferentPage(); |
| } |
| |
| // Ensure the URL bar loses focus if the tab it was interacting with is changed from |
| // underneath it. |
| setUrlBarFocus(false); |
| |
| // Place the cursor in the Omnibox if applicable. We always clear the focus above to |
| // ensure the shield placed over the content is dismissed when switching tabs. But if |
| // needed, we will refocus the omnibox and make the cursor visible here. |
| if (shouldShowCursorInLocationBar()) { |
| mLocationBar.showUrlBarCursorWithoutFocusAnimations(); |
| } |
| } |
| |
| Profile profile = mTabModelSelector.getModel(isIncognito).getProfile(); |
| |
| if (mCurrentProfile != profile) { |
| if (mBookmarkBridge != null) { |
| mBookmarkBridge.destroy(); |
| mBookmarkBridge = null; |
| } |
| if (profile != null) { |
| mBookmarkBridge = new BookmarkBridge(profile); |
| mBookmarkBridge.addObserver(mBookmarksObserver); |
| mAppMenuPropertiesDelegate.setBookmarkBridge(mBookmarkBridge); |
| mLocationBar.setAutocompleteProfile(profile); |
| } |
| mCurrentProfile = profile; |
| } |
| |
| updateButtonStatus(); |
| } |
| |
| private void updateCurrentTabDisplayStatus() { |
| assert mLocationBar != null; |
| |
| Tab tab = mLocationBarModel.getTab(); |
| mLocationBar.setUrlToPageUrl(); |
| |
| updateTabLoadingState(true); |
| |
| if (tab == null) { |
| finishLoadProgress(false); |
| return; |
| } |
| |
| mLoadProgressSimulator.cancel(); |
| |
| if (tab.isLoading()) { |
| if (NativePageFactory.isNativePageUrl(tab.getUrl(), tab.isIncognito())) { |
| finishLoadProgress(false); |
| } else { |
| startLoadProgress(); |
| updateLoadProgress(tab.getProgress()); |
| } |
| } else { |
| finishLoadProgress(false); |
| } |
| } |
| |
| private void updateTabLoadingState(boolean updateUrl) { |
| assert mLocationBar != null; |
| mLocationBar.updateLoadingState(updateUrl); |
| if (updateUrl) updateButtonStatus(); |
| } |
| |
| private void updateLoadProgress(int progress) { |
| assert mToolbarInflationComplete; |
| // If it's a native page, progress bar is already hidden or being hidden, so don't update |
| // the value. |
| // TODO(kkimlabs): Investigate back/forward navigation with native page & web content and |
| // figure out the correct progress bar presentation. |
| Tab tab = mLocationBarModel.getTab(); |
| if (tab == null || NativePageFactory.isNativePageUrl(tab.getUrl(), tab.isIncognito())) { |
| return; |
| } |
| |
| progress = Math.max(progress, MINIMUM_LOAD_PROGRESS); |
| mToolbar.setLoadProgress(progress / 100f); |
| if (progress == 100) finishLoadProgress(true); |
| } |
| |
| private void finishLoadProgress(boolean delayed) { |
| assert mToolbarInflationComplete; |
| mLoadProgressSimulator.cancel(); |
| mToolbar.finishLoadProgress(delayed); |
| } |
| |
| /** |
| * Only start showing the progress bar if it is not already started. |
| */ |
| private void startLoadProgress() { |
| assert mToolbarInflationComplete; |
| if (mToolbar.isProgressStarted()) return; |
| mToolbar.startLoadProgress(); |
| } |
| |
| /** |
| * @param enabled Whether the progress bar is enabled. |
| */ |
| public void setProgressBarEnabled(boolean enabled) { |
| mToolbar.setProgressBarEnabled(enabled); |
| } |
| |
| /** |
| * @param anchor The view to use as an anchor. |
| */ |
| public void setProgressBarAnchorView(@Nullable View anchor) { |
| mToolbar.setProgressBarAnchorView(anchor); |
| } |
| |
| private boolean shouldShowCursorInLocationBar() { |
| Tab tab = mLocationBarModel.getTab(); |
| if (tab == null) return false; |
| NativePage nativePage = tab.getNativePage(); |
| if (!(nativePage instanceof NewTabPage) && !(nativePage instanceof IncognitoNewTabPage)) { |
| return false; |
| } |
| |
| return mActivity.isTablet() |
| && mActivity.getResources().getConfiguration().keyboard |
| == Configuration.KEYBOARD_QWERTY; |
| } |
| |
| private static class LoadProgressSimulator { |
| private static final int MSG_ID_UPDATE_PROGRESS = 1; |
| |
| private static final int PROGRESS_INCREMENT = 10; |
| private static final int PROGRESS_INCREMENT_DELAY_MS = 10; |
| |
| private final ToolbarManager mToolbarManager; |
| private final Handler mHandler; |
| |
| private int mProgress; |
| |
| public LoadProgressSimulator(ToolbarManager toolbar) { |
| mToolbarManager = toolbar; |
| mHandler = new Handler(Looper.getMainLooper()) { |
| @Override |
| public void handleMessage(Message msg) { |
| assert msg.what == MSG_ID_UPDATE_PROGRESS; |
| mProgress = Math.min(100, mProgress += PROGRESS_INCREMENT); |
| mToolbarManager.updateLoadProgress(mProgress); |
| |
| if (mProgress == 100) { |
| mToolbarManager.mToolbar.finishLoadProgress(true); |
| return; |
| } |
| sendEmptyMessageDelayed(MSG_ID_UPDATE_PROGRESS, PROGRESS_INCREMENT_DELAY_MS); |
| } |
| }; |
| } |
| |
| /** |
| * Start simulating load progress from a baseline of 0. |
| */ |
| public void start() { |
| mProgress = 0; |
| mToolbarManager.mToolbar.startLoadProgress(); |
| mToolbarManager.updateLoadProgress(mProgress); |
| mHandler.sendEmptyMessage(MSG_ID_UPDATE_PROGRESS); |
| } |
| |
| /** |
| * Cancels simulating load progress. |
| */ |
| public void cancel() { |
| mHandler.removeMessages(MSG_ID_UPDATE_PROGRESS); |
| } |
| } |
| |
| /** Return the location bar model for testing purposes. */ |
| @VisibleForTesting |
| public LocationBarModel getLocationBarModelForTesting() { |
| return mLocationBarModel; |
| } |
| |
| /** |
| * @return The {@link ToolbarLayout} that constitutes the toolbar. |
| */ |
| @VisibleForTesting |
| public ToolbarLayout getToolbarLayoutForTesting() { |
| return mToolbar.getToolbarLayoutForTesting(); |
| } |
| } |