Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1735)

Unified Diff: chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsManagerImpl.java

Issue 2623993007: 🏠 Extract the ContentSuggestionManager interface from NTP (Closed)
Patch Set: Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsManagerImpl.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsManagerImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsManagerImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..39492cbcebe7aeb5d1dc4490588c631b64d5d093
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsManagerImpl.java
@@ -0,0 +1,366 @@
+// Copyright 2017 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.suggestions;
+
+import android.app.Activity;
+import android.net.Uri;
+import android.os.SystemClock;
+
+import org.chromium.base.Callback;
+import org.chromium.base.ObserverList;
+import org.chromium.base.ThreadUtils;
+import org.chromium.base.metrics.RecordHistogram;
+import org.chromium.base.metrics.RecordUserAction;
+import org.chromium.chrome.browser.ChromeFeatureList;
+import org.chromium.chrome.browser.UrlConstants;
+import org.chromium.chrome.browser.bookmarks.BookmarkUtils;
+import org.chromium.chrome.browser.download.DownloadUtils;
+import org.chromium.chrome.browser.favicon.FaviconHelper;
+import org.chromium.chrome.browser.favicon.FaviconHelper.FaviconImageCallback;
+import org.chromium.chrome.browser.favicon.FaviconHelper.IconAvailabilityCallback;
+import org.chromium.chrome.browser.favicon.LargeIconBridge;
+import org.chromium.chrome.browser.favicon.LargeIconBridge.LargeIconCallback;
+import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
+import org.chromium.chrome.browser.ntp.NewTabPage.DestructionObserver;
+import org.chromium.chrome.browser.ntp.NewTabPageUma;
+import org.chromium.chrome.browser.ntp.snippets.KnownCategories;
+import org.chromium.chrome.browser.ntp.snippets.SnippetArticle;
+import org.chromium.chrome.browser.ntp.snippets.SnippetsBridge;
+import org.chromium.chrome.browser.ntp.snippets.SuggestionsSource;
+import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
+import org.chromium.chrome.browser.offlinepages.OfflinePageUtils;
+import org.chromium.chrome.browser.preferences.PrefServiceBridge;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.tab.Tab;
+import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
+import org.chromium.chrome.browser.tabmodel.TabModelSelector;
+import org.chromium.chrome.browser.tabmodel.TabModelUtils;
+import org.chromium.chrome.browser.tabmodel.document.TabDelegate;
+import org.chromium.content_public.browser.LoadUrlParams;
+import org.chromium.content_public.common.Referrer;
+import org.chromium.ui.base.PageTransition;
+import org.chromium.ui.mojom.WindowOpenDisposition;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * {@link ContentSuggestionsManager} implementation.
+ */
+public class SuggestionsManagerImpl implements ContentSuggestionsManager {
+ private static final String CHROME_CONTENT_SUGGESTIONS_REFERRER =
+ "https://www.googleapis.com/auth/chrome-content-suggestions";
+
+ private final ObserverList<DestructionObserver> mDestructionObservers = new ObserverList<>();
+ private final SuggestionsSource mSuggestionsSource;
+ private SnippetsBridge mSnippetsBridge;
+ private final Activity mActivity;
+ private final Profile mProfile;
+
+ private final Tab mTab;
+ private final TabModelSelector mTabModelSelector;
+
+ private FaviconHelper mFaviconHelper;
+ private LargeIconBridge mLargeIconBridge;
+
+ private boolean mIsDestroyed;
+
+ public SuggestionsManagerImpl(SuggestionsSource suggestionsSource, Activity activity,
+ Profile profile, Tab currentTab, TabModelSelector tabModelSelector) {
+ mSuggestionsSource = suggestionsSource;
+ mActivity = activity;
+ mProfile = profile;
+ mTab = currentTab;
+ mTabModelSelector = tabModelSelector;
+ mSnippetsBridge = (SnippetsBridge) suggestionsSource;
+ }
+
+ @Override
+ public boolean isOpenInNewWindowEnabled() {
+ return MultiWindowUtils.getInstance().isOpenInOtherWindowSupported(mActivity);
+ }
+
+ @Override
+ public boolean isOpenInIncognitoEnabled() {
+ return PrefServiceBridge.getInstance().isIncognitoModeEnabled();
+ }
+
+ @Override
+ public void navigateToBookmarks() {
+ if (mIsDestroyed) return;
+ RecordUserAction.record("MobileNTPSwitchToBookmarks");
+ BookmarkUtils.showBookmarkManager(mActivity);
+ }
+
+ @Override
+ public void navigateToRecentTabs() {
+ if (mIsDestroyed) return;
+ RecordUserAction.record("MobileNTPSwitchToOpenTabs");
+ mTab.loadUrl(new LoadUrlParams(UrlConstants.RECENT_TABS_URL));
+ }
+
+ @Override
+ public void navigateToDownloadManager() {
+ if (mIsDestroyed) return;
+ assert DownloadUtils.isDownloadHomeEnabled();
+
+ RecordUserAction.record("MobileNTPSwitchToDownloadManager");
+ DownloadUtils.showDownloadManager(mActivity, mTab);
+ }
+
+ @Override
+ public void onLearnMoreClicked() {
+ if (mIsDestroyed) return;
+ NewTabPageUma.recordAction(NewTabPageUma.ACTION_CLICKED_LEARN_MORE);
+ String url = "https://support.google.com/chrome/?p=new_tab";
+ // TODO(mastiz): Change this to LINK?
+ openUrl(WindowOpenDisposition.CURRENT_TAB,
+ new LoadUrlParams(url, PageTransition.AUTO_BOOKMARK));
+ }
+
+ @Override
+ public void openSnippet(int windowOpenDisposition, SnippetArticle article) {
+ mSnippetsBridge.onSuggestionOpened(article, windowOpenDisposition);
+ NewTabPageUma.recordAction(NewTabPageUma.ACTION_OPENED_SNIPPET);
+
+ if (article.mIsAssetDownload) {
+ assert windowOpenDisposition == WindowOpenDisposition.CURRENT_TAB
+ || windowOpenDisposition == WindowOpenDisposition.NEW_WINDOW
+ || windowOpenDisposition == WindowOpenDisposition.NEW_FOREGROUND_TAB;
+ DownloadUtils.openFile(
+ article.getAssetDownloadFile(), article.getAssetDownloadMimeType(), false);
+ return;
+ }
+
+ if (article.isRecentTab()) {
+ assert windowOpenDisposition == WindowOpenDisposition.CURRENT_TAB;
+ // TODO(vitaliii): Add a debug check that the result is true after crbug.com/662924
+ // is resolved.
+ openRecentTabSnippet(article);
+ return;
+ }
+
+ // TODO(treib): Also track other dispositions. crbug.com/665915
+ if (windowOpenDisposition == WindowOpenDisposition.CURRENT_TAB) {
+ NewTabPageUma.monitorContentSuggestionVisit(mTab, article.mCategory);
+ }
+
+ LoadUrlParams loadUrlParams;
+ // We explicitly open an offline page only for offline page downloads. For all other
+ // sections the URL is opened and it is up to Offline Pages whether to open its offline
+ // page (e.g. when offline).
+ if (article.isDownload() && !article.mIsAssetDownload) {
+ assert article.getOfflinePageOfflineId() != null;
+ assert windowOpenDisposition == WindowOpenDisposition.CURRENT_TAB
+ || windowOpenDisposition == WindowOpenDisposition.NEW_WINDOW
+ || windowOpenDisposition == WindowOpenDisposition.NEW_FOREGROUND_TAB;
+ loadUrlParams = OfflinePageUtils.getLoadUrlParamsForOpeningOfflineVersion(
+ article.mUrl, article.getOfflinePageOfflineId());
+ // Extra headers are not read in loadUrl, but verbatim headers are.
+ loadUrlParams.setVerbatimHeaders(loadUrlParams.getExtraHeadersString());
+ } else {
+ loadUrlParams = new LoadUrlParams(article.mUrl, PageTransition.AUTO_BOOKMARK);
+ }
+
+ // For article suggestions, we set the referrer. This is exploited
+ // to filter out these history entries for NTP tiles.
+ // TODO(mastiz): Extend this with support for other categories.
+ if (article.mCategory == KnownCategories.ARTICLES) {
+ loadUrlParams.setReferrer(new Referrer(
+ CHROME_CONTENT_SUGGESTIONS_REFERRER, Referrer.REFERRER_POLICY_ALWAYS));
+ }
+
+ openUrl(windowOpenDisposition, loadUrlParams);
+ }
+
+ @Override
+ public void openUrl(int windowOpenDisposition, LoadUrlParams loadUrlParams) {
+ assert !mIsDestroyed;
+ switch (windowOpenDisposition) {
+ case WindowOpenDisposition.CURRENT_TAB:
+ mTab.loadUrl(loadUrlParams);
+ break;
+ case WindowOpenDisposition.NEW_FOREGROUND_TAB:
+ openUrlInNewTab(loadUrlParams, false);
+ break;
+ case WindowOpenDisposition.OFF_THE_RECORD:
+ openUrlInNewTab(loadUrlParams, true);
+ break;
+ case WindowOpenDisposition.NEW_WINDOW:
+ openUrlInNewWindow(loadUrlParams);
+ break;
+ case WindowOpenDisposition.SAVE_TO_DISK:
+ saveUrlForOffline(loadUrlParams.getUrl());
+ break;
+ default:
+ assert false;
+ }
+ }
+
+ @Override
+ public void trackSnippetsPageImpression(int[] categories, int[] suggestionsPerCategory) {
+ mSnippetsBridge.onPageShown(categories, suggestionsPerCategory);
+ }
+
+ @Override
+ public void trackSnippetImpression(SnippetArticle article) {
+ mSnippetsBridge.onSuggestionShown(article);
+ }
+
+ @Override
+ public void trackSnippetMenuOpened(SnippetArticle article) {
+ mSnippetsBridge.onSuggestionMenuOpened(article);
+ }
+
+ @Override
+ public void trackSnippetCategoryActionImpression(int category, int position) {
+ mSnippetsBridge.onMoreButtonShown(category, position);
+ }
+
+ @Override
+ public void trackSnippetCategoryActionClick(int category, int position) {
+ mSnippetsBridge.onMoreButtonClicked(category, position);
+ switch (category) {
+ case KnownCategories.BOOKMARKS:
+ NewTabPageUma.recordAction(NewTabPageUma.ACTION_OPENED_BOOKMARKS_MANAGER);
+ break;
+ // MORE button in both categories leads to the recent tabs manager
+ case KnownCategories.FOREIGN_TABS:
+ case KnownCategories.RECENT_TABS:
+ NewTabPageUma.recordAction(NewTabPageUma.ACTION_OPENED_RECENT_TABS_MANAGER);
+ break;
+ case KnownCategories.DOWNLOADS:
+ NewTabPageUma.recordAction(NewTabPageUma.ACTION_OPENED_DOWNLOADS_MANAGER);
+ break;
+ default:
+ // No action associated
+ break;
+ }
+ }
+
+ @Override
+ public void getLocalFaviconImageForURL(
+ String url, int size, FaviconImageCallback faviconCallback) {
+ if (mIsDestroyed) return;
+ if (mFaviconHelper == null) mFaviconHelper = new FaviconHelper();
+ mFaviconHelper.getLocalFaviconImageForURL(mProfile, url, size, faviconCallback);
+ }
+
+ @Override
+ public void getLargeIconForUrl(String url, int size, LargeIconCallback callback) {
+ if (mIsDestroyed) return;
+ if (mLargeIconBridge == null) mLargeIconBridge = new LargeIconBridge(mProfile);
+ mLargeIconBridge.getLargeIconForUrl(url, size, callback);
+ }
+
+ @Override
+ public void ensureIconIsAvailable(String pageUrl, String iconUrl, boolean isLargeIcon,
+ boolean isTemporary, IconAvailabilityCallback callback) {
+ if (mIsDestroyed) return;
+ if (mFaviconHelper == null) mFaviconHelper = new FaviconHelper();
+ mFaviconHelper.ensureIconIsAvailable(mProfile, mTab.getWebContents(), pageUrl, iconUrl,
+ isLargeIcon, isTemporary, callback);
+ }
+
+ @Override
+ public void getUrlsAvailableOffline(
+ Set<String> pageUrls, final Callback<Set<String>> callback) {
+ final Set<String> urlsAvailableOffline = new HashSet<>();
+ if (mIsDestroyed || !isNtpOfflinePagesEnabled()) {
+ callback.onResult(urlsAvailableOffline);
+ return;
+ }
+
+ HashSet<String> urlsToCheckForOfflinePage = new HashSet<>();
+
+ for (String pageUrl : pageUrls) {
+ if (isLocalUrl(pageUrl)) {
+ urlsAvailableOffline.add(pageUrl);
+ } else {
+ urlsToCheckForOfflinePage.add(pageUrl);
+ }
+ }
+
+ final long offlineQueryStartTime = SystemClock.elapsedRealtime();
+
+ OfflinePageBridge offlinePageBridge = OfflinePageBridge.getForProfile(mProfile);
+
+ // TODO(dewittj): Remove this code by making the NTP badging available after the NTP is
+ // fully loaded.
+ if (offlinePageBridge == null || !offlinePageBridge.isOfflinePageModelLoaded()) {
+ // Posting a task to avoid potential re-entrancy issues.
+ ThreadUtils.postOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ callback.onResult(urlsAvailableOffline);
+ }
+ });
+ return;
+ }
+
+ offlinePageBridge.checkPagesExistOffline(
+ urlsToCheckForOfflinePage, new Callback<Set<String>>() {
+ @Override
+ public void onResult(Set<String> urlsWithOfflinePages) {
+ urlsAvailableOffline.addAll(urlsWithOfflinePages);
+ callback.onResult(urlsAvailableOffline);
+ RecordHistogram.recordTimesHistogram("NewTabPage.OfflineUrlsLoadTime",
+ SystemClock.elapsedRealtime() - offlineQueryStartTime,
+ TimeUnit.MILLISECONDS);
+ }
+ });
+ }
+
+ @Override
+ public SuggestionsSource getSuggestionsSource() {
+ return mSuggestionsSource;
+ }
+
+ @Override
+ public void addDestructionObserver(DestructionObserver destructionObserver) {
+ mDestructionObservers.addObserver(destructionObserver);
+ }
+
+ public void onDestroy() {
+ for (DestructionObserver observer : mDestructionObservers) {
+ observer.onDestroy();
+ }
+ mIsDestroyed = true;
+ }
+
+ private boolean openRecentTabSnippet(SnippetArticle article) {
+ TabModel tabModel = mTabModelSelector.getModel(false);
+ int tabId = Integer.parseInt(article.getRecentTabId());
+ int tabIndex = TabModelUtils.getTabIndexById(tabModel, tabId);
+ if (tabIndex == TabModel.INVALID_TAB_INDEX) return false;
+ TabModelUtils.setIndex(tabModel, tabIndex);
+ return true;
+ }
+
+ private void openUrlInNewWindow(LoadUrlParams loadUrlParams) {
+ TabDelegate tabDelegate = new TabDelegate(false);
+ tabDelegate.createTabInOtherWindow(loadUrlParams, mActivity, mTab.getParentId());
+ }
+
+ private void openUrlInNewTab(LoadUrlParams loadUrlParams, boolean incognito) {
+ mTabModelSelector.openNewTab(
+ loadUrlParams, TabLaunchType.FROM_LONGPRESS_BACKGROUND, mTab, incognito);
+ }
+
+ private void saveUrlForOffline(String url) {
+ OfflinePageBridge.getForProfile(mProfile).savePageLater(
+ url, "ntp_suggestions", true /* userRequested */);
+ }
+
+ private boolean isNtpOfflinePagesEnabled() {
+ return ChromeFeatureList.isEnabled(ChromeFeatureList.NTP_OFFLINE_PAGES_FEATURE_NAME);
+ }
+
+ private boolean isLocalUrl(String url) {
+ return "file".equals(Uri.parse(url).getScheme());
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698