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

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

Issue 2623993007: 🏠 Extract the ContentSuggestionManager interface from NTP (Closed)
Patch Set: Fix tests 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.chrome.browser.suggestions;
6
7 import android.app.Activity;
8 import android.net.Uri;
9 import android.os.SystemClock;
10
11 import org.chromium.base.Callback;
12 import org.chromium.base.ObserverList;
13 import org.chromium.base.ThreadUtils;
14 import org.chromium.base.metrics.RecordHistogram;
15 import org.chromium.base.metrics.RecordUserAction;
16 import org.chromium.chrome.browser.ChromeFeatureList;
17 import org.chromium.chrome.browser.UrlConstants;
18 import org.chromium.chrome.browser.bookmarks.BookmarkUtils;
19 import org.chromium.chrome.browser.download.DownloadUtils;
20 import org.chromium.chrome.browser.favicon.FaviconHelper;
21 import org.chromium.chrome.browser.favicon.FaviconHelper.FaviconImageCallback;
22 import org.chromium.chrome.browser.favicon.FaviconHelper.IconAvailabilityCallbac k;
23 import org.chromium.chrome.browser.favicon.LargeIconBridge;
24 import org.chromium.chrome.browser.favicon.LargeIconBridge.LargeIconCallback;
25 import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
26 import org.chromium.chrome.browser.ntp.NewTabPage.DestructionObserver;
27 import org.chromium.chrome.browser.ntp.NewTabPageUma;
28 import org.chromium.chrome.browser.ntp.snippets.KnownCategories;
29 import org.chromium.chrome.browser.ntp.snippets.SnippetArticle;
30 import org.chromium.chrome.browser.ntp.snippets.SnippetsBridge;
31 import org.chromium.chrome.browser.ntp.snippets.SuggestionsSource;
32 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
33 import org.chromium.chrome.browser.offlinepages.OfflinePageUtils;
34 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
35 import org.chromium.chrome.browser.profiles.Profile;
36 import org.chromium.chrome.browser.tab.Tab;
37 import org.chromium.chrome.browser.tabmodel.TabModel;
38 import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
39 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
40 import org.chromium.chrome.browser.tabmodel.TabModelUtils;
41 import org.chromium.chrome.browser.tabmodel.document.TabDelegate;
42 import org.chromium.content_public.browser.LoadUrlParams;
43 import org.chromium.content_public.common.Referrer;
44 import org.chromium.ui.base.PageTransition;
45 import org.chromium.ui.mojom.WindowOpenDisposition;
46
47 import java.util.HashSet;
48 import java.util.Set;
49 import java.util.concurrent.TimeUnit;
50
51 /**
52 * {@link ContentSuggestionsManager} implementation.
53 */
54 public class SuggestionsManagerImpl implements ContentSuggestionsManager {
55 private static final String CHROME_CONTENT_SUGGESTIONS_REFERRER =
56 "https://www.googleapis.com/auth/chrome-content-suggestions";
57
58 private final ObserverList<DestructionObserver> mDestructionObservers = new ObserverList<>();
59 private final SuggestionsSource mSuggestionsSource;
60 private SnippetsBridge mSnippetsBridge;
61 private final Activity mActivity;
62 private final Profile mProfile;
63
64 private final Tab mTab;
65 private final TabModelSelector mTabModelSelector;
66
67 private FaviconHelper mFaviconHelper;
68 private LargeIconBridge mLargeIconBridge;
69
70 private boolean mIsDestroyed;
71
72 public SuggestionsManagerImpl(SuggestionsSource suggestionsSource, Activity activity,
73 Profile profile, Tab currentTab, TabModelSelector tabModelSelector) {
74 mSuggestionsSource = suggestionsSource;
75 mActivity = activity;
76 mProfile = profile;
77 mTab = currentTab;
78 mTabModelSelector = tabModelSelector;
79 mSnippetsBridge = (SnippetsBridge) suggestionsSource;
80 }
81
82 @Override
83 public boolean isOpenInNewWindowEnabled() {
84 return MultiWindowUtils.getInstance().isOpenInOtherWindowSupported(mActi vity);
85 }
86
87 @Override
88 public boolean isOpenInIncognitoEnabled() {
89 return PrefServiceBridge.getInstance().isIncognitoModeEnabled();
90 }
91
92 @Override
93 public void navigateToBookmarks() {
94 if (mIsDestroyed) return;
95 RecordUserAction.record("MobileNTPSwitchToBookmarks");
96 BookmarkUtils.showBookmarkManager(mActivity);
97 }
98
99 @Override
100 public void navigateToRecentTabs() {
101 if (mIsDestroyed) return;
102 RecordUserAction.record("MobileNTPSwitchToOpenTabs");
103 mTab.loadUrl(new LoadUrlParams(UrlConstants.RECENT_TABS_URL));
104 }
105
106 @Override
107 public void navigateToDownloadManager() {
108 if (mIsDestroyed) return;
109 assert DownloadUtils.isDownloadHomeEnabled();
110
111 RecordUserAction.record("MobileNTPSwitchToDownloadManager");
112 DownloadUtils.showDownloadManager(mActivity, mTab);
113 }
114
115 @Override
116 public void onLearnMoreClicked() {
117 if (mIsDestroyed) return;
118 NewTabPageUma.recordAction(NewTabPageUma.ACTION_CLICKED_LEARN_MORE);
119 String url = "https://support.google.com/chrome/?p=new_tab";
120 // TODO(mastiz): Change this to LINK?
121 openUrl(WindowOpenDisposition.CURRENT_TAB,
122 new LoadUrlParams(url, PageTransition.AUTO_BOOKMARK));
123 }
124
125 @Override
126 public void openSnippet(int windowOpenDisposition, SnippetArticle article) {
127 mSnippetsBridge.onSuggestionOpened(article, windowOpenDisposition);
128 NewTabPageUma.recordAction(NewTabPageUma.ACTION_OPENED_SNIPPET);
129
130 if (article.mIsAssetDownload) {
131 assert windowOpenDisposition == WindowOpenDisposition.CURRENT_TAB
132 || windowOpenDisposition == WindowOpenDisposition.NEW_WINDOW
133 || windowOpenDisposition == WindowOpenDisposition.NEW_FOREGR OUND_TAB;
134 DownloadUtils.openFile(
135 article.getAssetDownloadFile(), article.getAssetDownloadMime Type(), false);
136 return;
137 }
138
139 if (article.isRecentTab()) {
140 assert windowOpenDisposition == WindowOpenDisposition.CURRENT_TAB;
141 // TODO(vitaliii): Add a debug check that the result is true after c rbug.com/662924
142 // is resolved.
143 openRecentTabSnippet(article);
144 return;
145 }
146
147 // TODO(treib): Also track other dispositions. crbug.com/665915
148 if (windowOpenDisposition == WindowOpenDisposition.CURRENT_TAB) {
149 NewTabPageUma.monitorContentSuggestionVisit(mTab, article.mCategory) ;
150 }
151
152 LoadUrlParams loadUrlParams;
153 // We explicitly open an offline page only for offline page downloads. F or all other
154 // sections the URL is opened and it is up to Offline Pages whether to o pen its offline
155 // page (e.g. when offline).
156 if (article.isDownload() && !article.mIsAssetDownload) {
157 assert article.getOfflinePageOfflineId() != null;
158 assert windowOpenDisposition == WindowOpenDisposition.CURRENT_TAB
159 || windowOpenDisposition == WindowOpenDisposition.NEW_WINDOW
160 || windowOpenDisposition == WindowOpenDisposition.NEW_FOREGR OUND_TAB;
161 loadUrlParams = OfflinePageUtils.getLoadUrlParamsForOpeningOfflineVe rsion(
162 article.mUrl, article.getOfflinePageOfflineId());
163 // Extra headers are not read in loadUrl, but verbatim headers are.
164 loadUrlParams.setVerbatimHeaders(loadUrlParams.getExtraHeadersString ());
165 } else {
166 loadUrlParams = new LoadUrlParams(article.mUrl, PageTransition.AUTO_ BOOKMARK);
167 }
168
169 // For article suggestions, we set the referrer. This is exploited
170 // to filter out these history entries for NTP tiles.
171 // TODO(mastiz): Extend this with support for other categories.
172 if (article.mCategory == KnownCategories.ARTICLES) {
173 loadUrlParams.setReferrer(new Referrer(
174 CHROME_CONTENT_SUGGESTIONS_REFERRER, Referrer.REFERRER_POLIC Y_ALWAYS));
175 }
176
177 openUrl(windowOpenDisposition, loadUrlParams);
178 }
179
180 @Override
181 public void openUrl(int windowOpenDisposition, LoadUrlParams loadUrlParams) {
182 assert !mIsDestroyed;
183 switch (windowOpenDisposition) {
184 case WindowOpenDisposition.CURRENT_TAB:
185 mTab.loadUrl(loadUrlParams);
186 break;
187 case WindowOpenDisposition.NEW_FOREGROUND_TAB:
188 openUrlInNewTab(loadUrlParams, false);
189 break;
190 case WindowOpenDisposition.OFF_THE_RECORD:
191 openUrlInNewTab(loadUrlParams, true);
192 break;
193 case WindowOpenDisposition.NEW_WINDOW:
194 openUrlInNewWindow(loadUrlParams);
195 break;
196 case WindowOpenDisposition.SAVE_TO_DISK:
197 saveUrlForOffline(loadUrlParams.getUrl());
198 break;
199 default:
200 assert false;
201 }
202 }
203
204 @Override
205 public void trackSnippetsPageImpression(int[] categories, int[] suggestionsP erCategory) {
206 mSnippetsBridge.onPageShown(categories, suggestionsPerCategory);
207 }
208
209 @Override
210 public void trackSnippetImpression(SnippetArticle article) {
211 mSnippetsBridge.onSuggestionShown(article);
212 }
213
214 @Override
215 public void trackSnippetMenuOpened(SnippetArticle article) {
216 mSnippetsBridge.onSuggestionMenuOpened(article);
217 }
218
219 @Override
220 public void trackSnippetCategoryActionImpression(int category, int position) {
221 mSnippetsBridge.onMoreButtonShown(category, position);
222 }
223
224 @Override
225 public void trackSnippetCategoryActionClick(int category, int position) {
226 mSnippetsBridge.onMoreButtonClicked(category, position);
227 switch (category) {
228 case KnownCategories.BOOKMARKS:
229 NewTabPageUma.recordAction(NewTabPageUma.ACTION_OPENED_BOOKMARKS _MANAGER);
230 break;
231 // MORE button in both categories leads to the recent tabs manager
232 case KnownCategories.FOREIGN_TABS:
233 case KnownCategories.RECENT_TABS:
234 NewTabPageUma.recordAction(NewTabPageUma.ACTION_OPENED_RECENT_TA BS_MANAGER);
235 break;
236 case KnownCategories.DOWNLOADS:
237 NewTabPageUma.recordAction(NewTabPageUma.ACTION_OPENED_DOWNLOADS _MANAGER);
238 break;
239 default:
240 // No action associated
241 break;
242 }
243 }
244
245 @Override
246 public void getLocalFaviconImageForURL(
247 String url, int size, FaviconImageCallback faviconCallback) {
248 if (mIsDestroyed) return;
249 if (mFaviconHelper == null) mFaviconHelper = new FaviconHelper();
250 mFaviconHelper.getLocalFaviconImageForURL(mProfile, url, size, faviconCa llback);
251 }
252
253 @Override
254 public void getLargeIconForUrl(String url, int size, LargeIconCallback callb ack) {
255 if (mIsDestroyed) return;
256 if (mLargeIconBridge == null) mLargeIconBridge = new LargeIconBridge(mPr ofile);
257 mLargeIconBridge.getLargeIconForUrl(url, size, callback);
258 }
259
260 @Override
261 public void ensureIconIsAvailable(String pageUrl, String iconUrl, boolean is LargeIcon,
262 boolean isTemporary, IconAvailabilityCallback callback) {
263 if (mIsDestroyed) return;
264 if (mFaviconHelper == null) mFaviconHelper = new FaviconHelper();
265 mFaviconHelper.ensureIconIsAvailable(mProfile, mTab.getWebContents(), pa geUrl, iconUrl,
266 isLargeIcon, isTemporary, callback);
267 }
268
269 @Override
270 public void getUrlsAvailableOffline(
271 Set<String> pageUrls, final Callback<Set<String>> callback) {
272 final Set<String> urlsAvailableOffline = new HashSet<>();
273 if (mIsDestroyed || !isNtpOfflinePagesEnabled()) {
274 callback.onResult(urlsAvailableOffline);
275 return;
276 }
277
278 HashSet<String> urlsToCheckForOfflinePage = new HashSet<>();
279
280 for (String pageUrl : pageUrls) {
281 if (isLocalUrl(pageUrl)) {
282 urlsAvailableOffline.add(pageUrl);
283 } else {
284 urlsToCheckForOfflinePage.add(pageUrl);
285 }
286 }
287
288 final long offlineQueryStartTime = SystemClock.elapsedRealtime();
289
290 OfflinePageBridge offlinePageBridge = OfflinePageBridge.getForProfile(mP rofile);
291
292 // TODO(dewittj): Remove this code by making the NTP badging available a fter the NTP is
293 // fully loaded.
294 if (offlinePageBridge == null || !offlinePageBridge.isOfflinePageModelLo aded()) {
295 // Posting a task to avoid potential re-entrancy issues.
296 ThreadUtils.postOnUiThread(new Runnable() {
297 @Override
298 public void run() {
299 callback.onResult(urlsAvailableOffline);
300 }
301 });
302 return;
303 }
304
305 offlinePageBridge.checkPagesExistOffline(
306 urlsToCheckForOfflinePage, new Callback<Set<String>>() {
307 @Override
308 public void onResult(Set<String> urlsWithOfflinePages) {
309 urlsAvailableOffline.addAll(urlsWithOfflinePages);
310 callback.onResult(urlsAvailableOffline);
311 RecordHistogram.recordTimesHistogram("NewTabPage.Offline UrlsLoadTime",
312 SystemClock.elapsedRealtime() - offlineQueryStar tTime,
313 TimeUnit.MILLISECONDS);
314 }
315 });
316 }
317
318 @Override
319 public SuggestionsSource getSuggestionsSource() {
320 return mSuggestionsSource;
321 }
322
323 @Override
324 public void addDestructionObserver(DestructionObserver destructionObserver) {
325 mDestructionObservers.addObserver(destructionObserver);
326 }
327
328 public void onDestroy() {
329 for (DestructionObserver observer : mDestructionObservers) {
330 observer.onDestroy();
331 }
332 mIsDestroyed = true;
333 }
334
335 private boolean openRecentTabSnippet(SnippetArticle article) {
336 TabModel tabModel = mTabModelSelector.getModel(false);
337 int tabId = Integer.parseInt(article.getRecentTabId());
338 int tabIndex = TabModelUtils.getTabIndexById(tabModel, tabId);
339 if (tabIndex == TabModel.INVALID_TAB_INDEX) return false;
340 TabModelUtils.setIndex(tabModel, tabIndex);
341 return true;
342 }
343
344 private void openUrlInNewWindow(LoadUrlParams loadUrlParams) {
345 TabDelegate tabDelegate = new TabDelegate(false);
346 tabDelegate.createTabInOtherWindow(loadUrlParams, mActivity, mTab.getPar entId());
347 }
348
349 private void openUrlInNewTab(LoadUrlParams loadUrlParams, boolean incognito) {
350 mTabModelSelector.openNewTab(
351 loadUrlParams, TabLaunchType.FROM_LONGPRESS_BACKGROUND, mTab, in cognito);
352 }
353
354 private void saveUrlForOffline(String url) {
355 OfflinePageBridge.getForProfile(mProfile).savePageLater(
356 url, "ntp_suggestions", true /* userRequested */);
357 }
358
359 private boolean isNtpOfflinePagesEnabled() {
360 return ChromeFeatureList.isEnabled(ChromeFeatureList.NTP_OFFLINE_PAGES_F EATURE_NAME);
361 }
362
363 private boolean isLocalUrl(String url) {
364 return "file".equals(Uri.parse(url).getScheme());
365 }
366 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698