| Index: chrome/android/java_staging/src/org/chromium/chrome/browser/ntp/NewTabPage.java
|
| diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/ntp/NewTabPage.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/ntp/NewTabPage.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..ecdb58617d327eb7c7e950fb77156bb5e9c28499
|
| --- /dev/null
|
| +++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/ntp/NewTabPage.java
|
| @@ -0,0 +1,488 @@
|
| +// 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.ntp;
|
| +
|
| +import android.app.Activity;
|
| +import android.content.Context;
|
| +import android.graphics.Canvas;
|
| +import android.graphics.Rect;
|
| +import android.view.ContextMenu;
|
| +import android.view.LayoutInflater;
|
| +import android.view.Menu;
|
| +import android.view.MenuItem.OnMenuItemClickListener;
|
| +import android.view.View;
|
| +
|
| +import com.google.android.apps.chrome.R;
|
| +
|
| +import org.chromium.base.VisibleForTesting;
|
| +import org.chromium.base.metrics.RecordHistogram;
|
| +import org.chromium.base.metrics.RecordUserAction;
|
| +import org.chromium.chrome.browser.LogoBridge;
|
| +import org.chromium.chrome.browser.LogoBridge.Logo;
|
| +import org.chromium.chrome.browser.LogoBridge.LogoObserver;
|
| +import org.chromium.chrome.browser.NativePage;
|
| +import org.chromium.chrome.browser.Tab;
|
| +import org.chromium.chrome.browser.UrlConstants;
|
| +import org.chromium.chrome.browser.compositor.layouts.content.InvalidationAwareThumbnailProvider;
|
| +import org.chromium.chrome.browser.enhancedbookmarks.EnhancedBookmarkUtils;
|
| +import org.chromium.chrome.browser.favicon.FaviconHelper;
|
| +import org.chromium.chrome.browser.favicon.FaviconHelper.FaviconImageCallback;
|
| +import org.chromium.chrome.browser.favicon.LargeIconBridge;
|
| +import org.chromium.chrome.browser.favicon.LargeIconBridge.LargeIconCallback;
|
| +import org.chromium.chrome.browser.ntp.NewTabPageView.NewTabPageManager;
|
| +import org.chromium.chrome.browser.preferences.PrefServiceBridge;
|
| +import org.chromium.chrome.browser.profiles.MostVisitedSites;
|
| +import org.chromium.chrome.browser.profiles.MostVisitedSites.MostVisitedURLsObserver;
|
| +import org.chromium.chrome.browser.profiles.MostVisitedSites.ThumbnailCallback;
|
| +import org.chromium.chrome.browser.profiles.Profile;
|
| +import org.chromium.chrome.browser.search_engines.TemplateUrlService;
|
| +import org.chromium.chrome.browser.search_engines.TemplateUrlService.TemplateUrlServiceObserver;
|
| +import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
|
| +import org.chromium.chrome.browser.tabmodel.TabModelSelector;
|
| +import org.chromium.content_public.browser.LoadUrlParams;
|
| +import org.chromium.ui.base.DeviceFormFactor;
|
| +import org.chromium.ui.base.PageTransition;
|
| +
|
| +import java.util.concurrent.TimeUnit;
|
| +
|
| +/**
|
| + * Provides functionality when the user interacts with the NTP.
|
| + */
|
| +public class NewTabPage
|
| + implements NativePage, InvalidationAwareThumbnailProvider, TemplateUrlServiceObserver {
|
| +
|
| + // MostVisitedItem Context menu item IDs.
|
| + static final int ID_OPEN_IN_NEW_TAB = 0;
|
| + static final int ID_OPEN_IN_INCOGNITO_TAB = 1;
|
| + static final int ID_REMOVE = 2;
|
| +
|
| + private static MostVisitedSites sMostVisitedSitesForTests;
|
| +
|
| + private final Tab mTab;
|
| + private final TabModelSelector mTabModelSelector;
|
| + private final Activity mActivity;
|
| +
|
| + private final Profile mProfile;
|
| + private final String mTitle;
|
| + private final int mBackgroundColor;
|
| + private final NewTabPageView mNewTabPageView;
|
| +
|
| + private MostVisitedSites mMostVisitedSites;
|
| + private FaviconHelper mFaviconHelper;
|
| + private LargeIconBridge mLargeIconBridge;
|
| + private LogoBridge mLogoBridge;
|
| + private boolean mSearchProviderHasLogo;
|
| + private String mOnLogoClickUrl;
|
| + private FakeboxDelegate mFakeboxDelegate;
|
| +
|
| + // The timestamp at which the constructor was called.
|
| + private final long mConstructedTimeNs;
|
| +
|
| + private boolean mIsLoaded;
|
| +
|
| + // Whether destroy() has been called.
|
| + private boolean mIsDestroyed;
|
| +
|
| + /**
|
| + * Allows clients to listen for updates to the scroll changes of the search box on the
|
| + * NTP.
|
| + */
|
| + public interface OnSearchBoxScrollListener {
|
| + /**
|
| + * Callback to be notified when the scroll position of the search box on the NTP has
|
| + * changed. A scroll percentage of 0, means the search box has no scroll applied and
|
| + * is in it's natural resting position. A value of 1 means the search box is scrolled
|
| + * entirely to the top of the screen viewport.
|
| + *
|
| + * @param scrollPercentage The percentage the search box has been scrolled off the page.
|
| + */
|
| + void onScrollChanged(float scrollPercentage);
|
| + }
|
| +
|
| + /**
|
| + * Handles user interaction with the fakebox (the URL bar in the NTP).
|
| + */
|
| + public interface FakeboxDelegate {
|
| + /**
|
| + * Shows the voice recognition dialog. Called when the user taps the microphone icon.
|
| + */
|
| + void startVoiceRecognition();
|
| +
|
| + /**
|
| + * Focuses the URL bar when the user taps the fakebox, types in the fakebox, or pastes text
|
| + * into the fakebox.
|
| + *
|
| + * @param pastedText The text that was pasted or typed into the fakebox, or null if the user
|
| + * just tapped the fakebox.
|
| + */
|
| + void requestUrlFocusFromFakebox(String pastedText);
|
| + }
|
| +
|
| + /**
|
| + * @param url The URL to check whether it is for the NTP.
|
| + * @return Whether the passed in URL is used to render the NTP.
|
| + */
|
| + public static boolean isNTPUrl(String url) {
|
| + return url != null && url.startsWith(UrlConstants.NTP_URL);
|
| + }
|
| +
|
| + @VisibleForTesting
|
| + static void setMostVisitedSitesForTests(MostVisitedSites mostVisitedSitesForTests) {
|
| + sMostVisitedSitesForTests = mostVisitedSitesForTests;
|
| + }
|
| +
|
| + private final NewTabPageManager mNewTabPageManager = new NewTabPageManager() {
|
| + @Override
|
| + public boolean isLocationBarShownInNTP() {
|
| + if (mIsDestroyed) return false;
|
| + Context context = mNewTabPageView.getContext();
|
| + return isInSingleUrlBarMode(context)
|
| + && !mNewTabPageView.urlFocusAnimationsDisabled();
|
| + }
|
| +
|
| + private void recordOpenedMostVisitedItem(MostVisitedItem item) {
|
| + if (mIsDestroyed) return;
|
| + NewTabPageUma.recordAction(NewTabPageUma.ACTION_OPENED_MOST_VISITED_ENTRY);
|
| + RecordHistogram.recordEnumeratedHistogram("NewTabPage.MostVisited", item.getIndex(),
|
| + NewTabPageView.MAX_MOST_VISITED_SITES);
|
| + mMostVisitedSites.recordOpenedMostVisitedItem(item.getIndex());
|
| + }
|
| +
|
| + @Override
|
| + public boolean shouldShowOptOutPromo() {
|
| + return false;
|
| + }
|
| +
|
| + @Override
|
| + public void optOutPromoShown() {}
|
| +
|
| + @Override
|
| + public void optOutPromoClicked(boolean settingsClicked) {
|
| + assert false : "Should never be called for this page";
|
| + }
|
| +
|
| + @Override
|
| + public void open(MostVisitedItem item) {
|
| + if (mIsDestroyed) return;
|
| + recordOpenedMostVisitedItem(item);
|
| + mTab.loadUrl(new LoadUrlParams(item.getUrl()));
|
| + }
|
| +
|
| + @Override
|
| + public void onCreateContextMenu(ContextMenu menu, OnMenuItemClickListener listener) {
|
| + if (mIsDestroyed) return;
|
| + menu.add(Menu.NONE, ID_OPEN_IN_NEW_TAB, Menu.NONE, R.string.contextmenu_open_in_new_tab)
|
| + .setOnMenuItemClickListener(listener);
|
| + if (PrefServiceBridge.getInstance().isIncognitoModeEnabled()) {
|
| + menu.add(Menu.NONE, ID_OPEN_IN_INCOGNITO_TAB, Menu.NONE,
|
| + R.string.contextmenu_open_in_incognito_tab).setOnMenuItemClickListener(
|
| + listener);
|
| + }
|
| + menu.add(Menu.NONE, ID_REMOVE, Menu.NONE, R.string.remove)
|
| + .setOnMenuItemClickListener(listener);
|
| + }
|
| +
|
| + @Override
|
| + public boolean onMenuItemClick(int menuId, MostVisitedItem item) {
|
| + if (mIsDestroyed) return false;
|
| + switch (menuId) {
|
| + case ID_OPEN_IN_NEW_TAB:
|
| + recordOpenedMostVisitedItem(item);
|
| + mTabModelSelector.openNewTab(new LoadUrlParams(item.getUrl()),
|
| + TabLaunchType.FROM_LONGPRESS_BACKGROUND, mTab, false);
|
| + return true;
|
| + case ID_OPEN_IN_INCOGNITO_TAB:
|
| + recordOpenedMostVisitedItem(item);
|
| + mTabModelSelector.openNewTab(new LoadUrlParams(item.getUrl()),
|
| + TabLaunchType.FROM_LONGPRESS_FOREGROUND, mTab, true);
|
| + return true;
|
| + case ID_REMOVE:
|
| + mMostVisitedSites.blacklistUrl(item.getUrl());
|
| + return true;
|
| + default:
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + @Override
|
| + public void navigateToBookmarks() {
|
| + if (mIsDestroyed) return;
|
| + RecordUserAction.record("MobileNTPSwitchToBookmarks");
|
| + if (!EnhancedBookmarkUtils.showEnhancedBookmarkIfEnabled(mActivity)) {
|
| + mTab.loadUrl(new LoadUrlParams(UrlConstants.BOOKMARKS_URL));
|
| + }
|
| + }
|
| +
|
| + @Override
|
| + public void navigateToRecentTabs() {
|
| + if (mIsDestroyed) return;
|
| + RecordUserAction.record("MobileNTPSwitchToOpenTabs");
|
| + mTab.loadUrl(new LoadUrlParams(UrlConstants.RECENT_TABS_URL));
|
| + }
|
| +
|
| + @Override
|
| + public void focusSearchBox(boolean beginVoiceSearch, String pastedText) {
|
| + if (mIsDestroyed) return;
|
| + if (mFakeboxDelegate != null) {
|
| + if (beginVoiceSearch) {
|
| + mFakeboxDelegate.startVoiceRecognition();
|
| + } else {
|
| + mFakeboxDelegate.requestUrlFocusFromFakebox(pastedText);
|
| + }
|
| + }
|
| + }
|
| +
|
| + @Override
|
| + public void setMostVisitedURLsObserver(MostVisitedURLsObserver observer, int numResults) {
|
| + if (mIsDestroyed) return;
|
| + mMostVisitedSites.setMostVisitedURLsObserver(observer, numResults);
|
| + }
|
| +
|
| + @Override
|
| + public void getURLThumbnail(String url, ThumbnailCallback thumbnailCallback) {
|
| + if (mIsDestroyed) return;
|
| + mMostVisitedSites.getURLThumbnail(url, thumbnailCallback);
|
| + }
|
| +
|
| + @Override
|
| + public void getLocalFaviconImageForURL(
|
| + String url, int size, FaviconImageCallback faviconCallback) {
|
| + if (mIsDestroyed) return;
|
| + if (mFaviconHelper == null) mFaviconHelper = new FaviconHelper();
|
| + mFaviconHelper.getLocalFaviconImageForURL(mProfile, url, FaviconHelper.FAVICON
|
| + | FaviconHelper.TOUCH_ICON | FaviconHelper.TOUCH_PRECOMPOSED_ICON, size,
|
| + faviconCallback);
|
| + }
|
| +
|
| + @Override
|
| + public void getLargeIconForUrl(String url, int size, LargeIconCallback callback) {
|
| + if (mIsDestroyed) return;
|
| + if (mLargeIconBridge == null) mLargeIconBridge = new LargeIconBridge();
|
| + mLargeIconBridge.getLargeIconForUrl(mProfile, url, size, callback);
|
| + }
|
| +
|
| + @Override
|
| + public void openLogoLink() {
|
| + if (mIsDestroyed) return;
|
| + if (mOnLogoClickUrl == null) return;
|
| + mTab.loadUrl(
|
| + new LoadUrlParams(mOnLogoClickUrl, PageTransition.LINK));
|
| + }
|
| +
|
| + @Override
|
| + public void getSearchProviderLogo(final LogoObserver logoObserver) {
|
| + if (mIsDestroyed) return;
|
| + LogoObserver wrapperCallback = new LogoObserver() {
|
| + @Override
|
| + public void onLogoAvailable(Logo logo, boolean fromCache) {
|
| + if (mIsDestroyed) return;
|
| + mOnLogoClickUrl = logo != null ? logo.onClickUrl : null;
|
| + logoObserver.onLogoAvailable(logo, fromCache);
|
| + }
|
| + };
|
| + mLogoBridge.getCurrentLogo(wrapperCallback);
|
| + }
|
| +
|
| + @Override
|
| + public void onLoadingComplete() {
|
| + long loadTimeMs = (System.nanoTime() - mConstructedTimeNs) / 1000000;
|
| + RecordHistogram.recordTimesHistogram(
|
| + "Tab.NewTabOnload", loadTimeMs, TimeUnit.MILLISECONDS);
|
| + mIsLoaded = true;
|
| +
|
| + if (mIsDestroyed) return;
|
| + mMostVisitedSites.onLoadingComplete();
|
| + }
|
| + };
|
| +
|
| + /**
|
| + * Constructs a NewTabPage.
|
| + * @param activity The activity used for context to create the new tab page's View.
|
| + * @param tab The Tab that is showing this new tab page.
|
| + * @param tabModelSelector The TabModelSelector used to open tabs.
|
| + */
|
| + public NewTabPage(Activity activity, Tab tab, TabModelSelector tabModelSelector) {
|
| + mConstructedTimeNs = System.nanoTime();
|
| +
|
| + mTab = tab;
|
| + mActivity = activity;
|
| + mTabModelSelector = tabModelSelector;
|
| + mProfile = tab.getProfile();
|
| +
|
| + mTitle = activity.getResources().getString(R.string.button_new_tab);
|
| + mBackgroundColor = activity.getResources().getColor(R.color.ntp_bg);
|
| + TemplateUrlService.getInstance().addObserver(this);
|
| +
|
| + mMostVisitedSites = buildMostVisitedSites(mProfile);
|
| + mLogoBridge = new LogoBridge(mProfile);
|
| + mSearchProviderHasLogo = TemplateUrlService.getInstance().isDefaultSearchEngineGoogle();
|
| +
|
| + LayoutInflater inflater = LayoutInflater.from(activity);
|
| + mNewTabPageView = (NewTabPageView) inflater.inflate(R.layout.new_tab_page, null);
|
| + mNewTabPageView.initialize(mNewTabPageManager, isInSingleUrlBarMode(activity),
|
| + mSearchProviderHasLogo);
|
| + }
|
| +
|
| + private static MostVisitedSites buildMostVisitedSites(Profile profile) {
|
| + if (sMostVisitedSitesForTests != null) {
|
| + return sMostVisitedSitesForTests;
|
| + } else {
|
| + return new MostVisitedSites(profile);
|
| + }
|
| + }
|
| +
|
| + /** @return The view container for the new tab page. */
|
| + @VisibleForTesting
|
| + NewTabPageView getNewTabPageView() {
|
| + return mNewTabPageView;
|
| + }
|
| +
|
| + /**
|
| + * Updates whether the NewTabPage should animate on URL focus changes.
|
| + * @param disable Whether to disable the animations.
|
| + */
|
| + public void setUrlFocusAnimationsDisabled(boolean disable) {
|
| + mNewTabPageView.setUrlFocusAnimationsDisabled(disable);
|
| + }
|
| +
|
| + private boolean isInSingleUrlBarMode(Context context) {
|
| + return mSearchProviderHasLogo && !DeviceFormFactor.isTablet(context);
|
| + }
|
| +
|
| + private void onSearchEngineUpdated() {
|
| + // TODO(newt): update this if other search providers provide logos.
|
| + mSearchProviderHasLogo = TemplateUrlService.getInstance().isDefaultSearchEngineGoogle();
|
| + mNewTabPageView.setSearchProviderHasLogo(mSearchProviderHasLogo);
|
| + }
|
| +
|
| + /**
|
| + * Specifies the percentage the URL is focused during an animation. 1.0 specifies that the URL
|
| + * bar has focus and has completed the focus animation. 0 is when the URL bar is does not have
|
| + * any focus.
|
| + *
|
| + * @param percent The percentage of the URL bar focus animation.
|
| + */
|
| + public void setUrlFocusChangeAnimationPercent(float percent) {
|
| + mNewTabPageView.setUrlFocusChangeAnimationPercent(percent);
|
| + }
|
| +
|
| + /**
|
| + * Get the bounds of the search box in relation to the top level NewTabPage view.
|
| + *
|
| + * @param originalBounds The bounding region of the search box without external transforms
|
| + * applied. The delta between this and the transformed bounds determines
|
| + * the amount of scroll applied to this view.
|
| + * @param transformedBounds The bounding region of the search box including any transforms
|
| + * applied by the parent view hierarchy up to the NewTabPage view.
|
| + * This more accurately reflects the current drawing location of the
|
| + * search box.
|
| + */
|
| + public void getSearchBoxBounds(Rect originalBounds, Rect transformedBounds) {
|
| + mNewTabPageView.getSearchBoxBounds(originalBounds, transformedBounds);
|
| + }
|
| +
|
| + /**
|
| + * @return Whether the location bar is shown in the NTP.
|
| + */
|
| + public boolean isLocationBarShownInNTP() {
|
| + return mNewTabPageManager.isLocationBarShownInNTP();
|
| + }
|
| +
|
| + /**
|
| + * Sets the listener for search box scroll changes.
|
| + * @param listener The listener to be notified on changes.
|
| + */
|
| + public void setSearchBoxScrollListener(OnSearchBoxScrollListener listener) {
|
| + mNewTabPageView.setSearchBoxScrollListener(listener);
|
| + }
|
| +
|
| + /**
|
| + * Sets the FakeboxDelegate that this pages interacts with.
|
| + */
|
| + public void setFakeboxDelegate(FakeboxDelegate fakeboxDelegate) {
|
| + mFakeboxDelegate = fakeboxDelegate;
|
| + }
|
| +
|
| + /**
|
| + * @return Whether the NTP has finished loaded.
|
| + */
|
| + @VisibleForTesting
|
| + public boolean isLoadedForTests() {
|
| + return mIsLoaded;
|
| + }
|
| +
|
| + // TemplateUrlServiceObserver overrides
|
| +
|
| + @Override
|
| + public void onTemplateURLServiceChanged() {
|
| + onSearchEngineUpdated();
|
| + }
|
| +
|
| + // NativePage overrides
|
| +
|
| + @Override
|
| + public void destroy() {
|
| + assert getView().getParent() == null : "Destroy called before removed from window";
|
| + if (mFaviconHelper != null) {
|
| + mFaviconHelper.destroy();
|
| + mFaviconHelper = null;
|
| + }
|
| + if (mLargeIconBridge != null) {
|
| + mLargeIconBridge.destroy();
|
| + mLargeIconBridge = null;
|
| + }
|
| + if (mMostVisitedSites != null) {
|
| + mMostVisitedSites.destroy();
|
| + mMostVisitedSites = null;
|
| + }
|
| + if (mLogoBridge != null) {
|
| + mLogoBridge.destroy();
|
| + mLogoBridge = null;
|
| + }
|
| + TemplateUrlService.getInstance().removeObserver(this);
|
| + mIsDestroyed = true;
|
| + }
|
| +
|
| + @Override
|
| + public String getUrl() {
|
| + return UrlConstants.NTP_URL;
|
| + }
|
| +
|
| + @Override
|
| + public String getTitle() {
|
| + return mTitle;
|
| + }
|
| +
|
| + @Override
|
| + public int getBackgroundColor() {
|
| + return mBackgroundColor;
|
| + }
|
| +
|
| + @Override
|
| + public View getView() {
|
| + return mNewTabPageView;
|
| + }
|
| +
|
| + @Override
|
| + public String getHost() {
|
| + return UrlConstants.NTP_HOST;
|
| + }
|
| +
|
| + @Override
|
| + public void updateForUrl(String url) {
|
| + }
|
| +
|
| + // InvalidationAwareThumbnailProvider
|
| +
|
| + @Override
|
| + public boolean shouldCaptureThumbnail() {
|
| + return mNewTabPageView.shouldCaptureThumbnail();
|
| + }
|
| +
|
| + @Override
|
| + public void captureThumbnail(Canvas canvas) {
|
| + mNewTabPageView.captureThumbnail(canvas);
|
| + }
|
| +}
|
|
|