Commit f68dd98e authored by Theresa's avatar Theresa Committed by Commit Bot

[EoC] Use ContextualSuggestionsBridge in Java-layer

Start talking to the new, component-specific bridge rather than the
SnippetsBridge.

Also, make ContextualSuggestionsBridge and ContextualSuggestionsCluster
package protected to enforce ContextualSuggestionsCoordinator being
the only Java class visible outside of the package.

BUG=822945,827654

Change-Id: Ib599a1ec9f6af53046c71c8b762b64398cb306cc
Reviewed-on: https://chromium-review.googlesource.com/996404
Commit-Queue: Theresa <twellington@chromium.org>
Reviewed-by: default avatarFilip Gorski <fgorski@chromium.org>
Cr-Commit-Position: refs/heads/master@{#548242}
parent ad260293
...@@ -21,7 +21,7 @@ import java.util.List; ...@@ -21,7 +21,7 @@ import java.util.List;
* Provides access to contextual suggestions. * Provides access to contextual suggestions.
*/ */
@JNINamespace("contextual_suggestions") @JNINamespace("contextual_suggestions")
public class ContextualSuggestionsBridge { class ContextualSuggestionsBridge {
private long mNativeContextualSuggestionsBridge; private long mNativeContextualSuggestionsBridge;
/** Result of fetching contextual suggestions. */ /** Result of fetching contextual suggestions. */
...@@ -49,12 +49,12 @@ public class ContextualSuggestionsBridge { ...@@ -49,12 +49,12 @@ public class ContextualSuggestionsBridge {
* *
* @param profile Profile of the user that we will retrieve snippets for. * @param profile Profile of the user that we will retrieve snippets for.
*/ */
public ContextualSuggestionsBridge(Profile profile) { ContextualSuggestionsBridge(Profile profile) {
mNativeContextualSuggestionsBridge = nativeInit(profile); mNativeContextualSuggestionsBridge = nativeInit(profile);
} }
/** Destroys the brige. */ /** Destroys the bridge. */
public void destroy() { void destroy() {
assert mNativeContextualSuggestionsBridge != 0; assert mNativeContextualSuggestionsBridge != 0;
nativeDestroy(mNativeContextualSuggestionsBridge); nativeDestroy(mNativeContextualSuggestionsBridge);
mNativeContextualSuggestionsBridge = 0; mNativeContextualSuggestionsBridge = 0;
...@@ -65,27 +65,27 @@ public class ContextualSuggestionsBridge { ...@@ -65,27 +65,27 @@ public class ContextualSuggestionsBridge {
* @param url URL for which to fetch suggestions. * @param url URL for which to fetch suggestions.
* @param callback Callback used to return suggestions for a given URL. * @param callback Callback used to return suggestions for a given URL.
*/ */
public void fetchSuggestions(String url, Callback<ContextualSuggestionsResult> callback) { void fetchSuggestions(String url, Callback<ContextualSuggestionsResult> callback) {
assert mNativeContextualSuggestionsBridge != 0; assert mNativeContextualSuggestionsBridge != 0;
nativeFetchSuggestions(mNativeContextualSuggestionsBridge, url, callback); nativeFetchSuggestions(mNativeContextualSuggestionsBridge, url, callback);
} }
/** Fetches a thumbnail for the suggestion. */ /** Fetches a thumbnail for the suggestion. */
public void fetchSuggestionImage(SnippetArticle suggestion, Callback<Bitmap> callback) { void fetchSuggestionImage(SnippetArticle suggestion, Callback<Bitmap> callback) {
assert mNativeContextualSuggestionsBridge != 0; assert mNativeContextualSuggestionsBridge != 0;
nativeFetchSuggestionImage( nativeFetchSuggestionImage(
mNativeContextualSuggestionsBridge, suggestion.mIdWithinCategory, callback); mNativeContextualSuggestionsBridge, suggestion.mIdWithinCategory, callback);
} }
/** Fetches a favicon for the suggestion. */ /** Fetches a favicon for the suggestion. */
public void fetchSuggestionFavicon(SnippetArticle suggestion, Callback<Bitmap> callback) { void fetchSuggestionFavicon(SnippetArticle suggestion, Callback<Bitmap> callback) {
assert mNativeContextualSuggestionsBridge != 0; assert mNativeContextualSuggestionsBridge != 0;
nativeFetchSuggestionFavicon( nativeFetchSuggestionFavicon(
mNativeContextualSuggestionsBridge, suggestion.mIdWithinCategory, callback); mNativeContextualSuggestionsBridge, suggestion.mIdWithinCategory, callback);
} }
/** Requests the backend to clear state related to this bridge. */ /** Requests the backend to clear state related to this bridge. */
public void clearState() { void clearState() {
assert mNativeContextualSuggestionsBridge != 0; assert mNativeContextualSuggestionsBridge != 0;
nativeClearState(mNativeContextualSuggestionsBridge); nativeClearState(mNativeContextualSuggestionsBridge);
} }
...@@ -96,7 +96,7 @@ public class ContextualSuggestionsBridge { ...@@ -96,7 +96,7 @@ public class ContextualSuggestionsBridge {
* @param webContents Web contents with the document for which event is reported. * @param webContents Web contents with the document for which event is reported.
* @param eventId Id of the reported event. * @param eventId Id of the reported event.
*/ */
public void reportEvent(WebContents webContents, int eventId) { void reportEvent(WebContents webContents, int eventId) {
assert mNativeContextualSuggestionsBridge != 0; assert mNativeContextualSuggestionsBridge != 0;
assert webContents != null && !webContents.isDestroyed(); assert webContents != null && !webContents.isDestroyed();
...@@ -114,10 +114,11 @@ public class ContextualSuggestionsBridge { ...@@ -114,10 +114,11 @@ public class ContextualSuggestionsBridge {
} }
@CalledByNative @CalledByNative
private static void addSuggestionToLastCluster(List<ContextualSuggestionsCluster> clusters, private static void addSuggestionToLastCluster(ContextualSuggestionsResult result, String id,
String id, String title, String publisher, String url) { String title, String publisher, String url) {
assert clusters.size() > 0; assert result.getClusters().size() > 0;
clusters.get(clusters.size() - 1) result.getClusters()
.get(result.getClusters().size() - 1)
.getSuggestions() .getSuggestions()
.add(new SnippetArticle(KnownCategories.CONTEXTUAL, id, title, publisher, url, .add(new SnippetArticle(KnownCategories.CONTEXTUAL, id, title, publisher, url,
/*publishTimestamp=*/0, /*score=*/0f, /*fetchTimestamp=*/0, /*publishTimestamp=*/0, /*score=*/0f, /*fetchTimestamp=*/0,
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
package org.chromium.chrome.browser.contextual_suggestions; package org.chromium.chrome.browser.contextual_suggestions;
import android.text.TextUtils;
import org.chromium.base.Callback; import org.chromium.base.Callback;
import org.chromium.chrome.browser.ntp.cards.ChildNode; import org.chromium.chrome.browser.ntp.cards.ChildNode;
import org.chromium.chrome.browser.ntp.cards.InnerNode; import org.chromium.chrome.browser.ntp.cards.InnerNode;
...@@ -22,38 +24,34 @@ import java.util.List; ...@@ -22,38 +24,34 @@ import java.util.List;
import java.util.Set; import java.util.Set;
/** A node in a tree that groups contextual suggestions in a cluster of related items. */ /** A node in a tree that groups contextual suggestions in a cluster of related items. */
public class ContextualSuggestionsCluster extends InnerNode { class ContextualSuggestionsCluster extends InnerNode {
private final String mTitle; private final String mTitle;
private final boolean mShouldShowTitle;
private final List<SnippetArticle> mSuggestions = new ArrayList<>(); private final List<SnippetArticle> mSuggestions = new ArrayList<>();
private SectionHeader mHeader; private SectionHeader mHeader;
private boolean mShouldShowTitle = true;
/** Creates a new contextual suggestions cluster with provided title. */ /** Creates a new contextual suggestions cluster with provided title. */
public ContextualSuggestionsCluster(String title) { ContextualSuggestionsCluster(String title) {
mTitle = title; mTitle = title;
mShouldShowTitle = !TextUtils.isEmpty(title);
} }
/** @return A title related to this cluster */ /** @return A title related to this cluster */
public String getTitle() { String getTitle() {
return mTitle; return mTitle;
} }
/** @return A list of suggestions in this cluster */ /** @return A list of suggestions in this cluster */
public List<SnippetArticle> getSuggestions() { List<SnippetArticle> getSuggestions() {
return mSuggestions; return mSuggestions;
} }
/** @param showTitle Whether the cluster title should be shown. */
public void setShouldShowTitle(boolean showTitle) {
mShouldShowTitle = showTitle;
}
/** /**
* Called to build the tree node's children. Should be called after all suggestions have been * Called to build the tree node's children. Should be called after all suggestions have been
* added. * added.
*/ */
public void buildChildren() { void buildChildren() {
if (mShouldShowTitle) { if (mShouldShowTitle) {
mHeader = new SectionHeader(mTitle); mHeader = new SectionHeader(mTitle);
addChild(mHeader); addChild(mHeader);
......
...@@ -9,11 +9,9 @@ import android.support.annotation.Nullable; ...@@ -9,11 +9,9 @@ import android.support.annotation.Nullable;
import android.text.TextUtils; import android.text.TextUtils;
import org.chromium.base.ContextUtils; import org.chromium.base.ContextUtils;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager; import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager.FullscreenListener; import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager.FullscreenListener;
import org.chromium.chrome.browser.ntp.snippets.SnippetArticle; import org.chromium.chrome.browser.ntp.snippets.SnippetArticle;
import org.chromium.chrome.browser.ntp.snippets.SnippetsBridge;
import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.tabmodel.TabModelSelector; import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.browser.util.MathUtils; import org.chromium.chrome.browser.util.MathUtils;
...@@ -40,7 +38,7 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet ...@@ -40,7 +38,7 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet
private final EnabledStateMonitor mEnabledStateMonitor; private final EnabledStateMonitor mEnabledStateMonitor;
private final ChromeFullscreenManager mFullscreenManager; private final ChromeFullscreenManager mFullscreenManager;
private @Nullable SnippetsBridge mBridge; private @Nullable ContextualSuggestionsSource mSuggestionsSource;
private @Nullable FetchHelper mFetchHelper; private @Nullable FetchHelper mFetchHelper;
private @Nullable String mCurrentRequestUrl; private @Nullable String mCurrentRequestUrl;
private @Nullable BottomSheetObserver mSheetObserver; private @Nullable BottomSheetObserver mSheetObserver;
...@@ -82,7 +80,7 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet ...@@ -82,7 +80,7 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet
// remain hidden since their offset from the bottom of the screen is determined by // remain hidden since their offset from the bottom of the screen is determined by
// the top controls. // the top controls.
if (!mDidSuggestionsShowForTab && mModel.hasSuggestions() if (!mDidSuggestionsShowForTab && mModel.hasSuggestions()
&& areBrowserControlsHidden() && mBridge != null) { && areBrowserControlsHidden() && mSuggestionsSource != null) {
showContentInSheet(); showContentInSheet();
} }
} }
...@@ -104,9 +102,9 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet ...@@ -104,9 +102,9 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet
mFetchHelper = null; mFetchHelper = null;
} }
if (mBridge != null) { if (mSuggestionsSource != null) {
mBridge.destroy(); mSuggestionsSource.destroy();
mBridge = null; mSuggestionsSource = null;
} }
} }
...@@ -121,7 +119,7 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet ...@@ -121,7 +119,7 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet
@Override @Override
public void onEnabledStateChanged(boolean enabled) { public void onEnabledStateChanged(boolean enabled) {
if (enabled) { if (enabled) {
mBridge = new SnippetsBridge(mProfile); mSuggestionsSource = new ContextualSuggestionsSource(mProfile);
mFetchHelper = new FetchHelper(this, mTabModelSelector); mFetchHelper = new FetchHelper(this, mTabModelSelector);
} else { } else {
clearSuggestions(); clearSuggestions();
...@@ -131,9 +129,9 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet ...@@ -131,9 +129,9 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet
mFetchHelper = null; mFetchHelper = null;
} }
if (mBridge != null) { if (mSuggestionsSource != null) {
mBridge.destroy(); mSuggestionsSource.destroy();
mBridge = null; mSuggestionsSource = null;
} }
} }
} }
...@@ -141,21 +139,28 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet ...@@ -141,21 +139,28 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet
@Override @Override
public void requestSuggestions(String url) { public void requestSuggestions(String url) {
mCurrentRequestUrl = url; mCurrentRequestUrl = url;
mBridge.fetchContextualSuggestions(url, (suggestions) -> { mSuggestionsSource.getBridge().fetchSuggestions(url, (suggestionsResult) -> {
if (mBridge == null) return; if (mSuggestionsSource == null) return;
// Avoiding double fetches causing suggestions for incorrect context. // Avoiding double fetches causing suggestions for incorrect context.
if (!TextUtils.equals(url, mCurrentRequestUrl)) return; if (!TextUtils.equals(url, mCurrentRequestUrl)) return;
Toast.makeText(ContextUtils.getApplicationContext(), List<ContextualSuggestionsCluster> clusters = suggestionsResult.getClusters();
suggestions.size() + " suggestions fetched", Toast.LENGTH_SHORT)
.show();
if (suggestions.size() > 0) { if (clusters.size() > 0 && clusters.get(0).getSuggestions().size() > 0) {
preloadContentInSheet(generateClusterList(suggestions), suggestions.get(0).mTitle); Toast.makeText(ContextUtils.getApplicationContext(),
clusters.size() + " clusters fetched", Toast.LENGTH_SHORT)
.show();
preloadContentInSheet(
generateClusterList(clusters), suggestionsResult.getPeekText());
// If the controls are already off-screen, show the suggestions immediately so they // If the controls are already off-screen, show the suggestions immediately so they
// are available on reverse scroll. // are available on reverse scroll.
if (areBrowserControlsHidden()) showContentInSheet(); if (areBrowserControlsHidden()) showContentInSheet();
} else {
Toast.makeText(ContextUtils.getApplicationContext(), "No suggestions",
Toast.LENGTH_SHORT)
.show();
} }
}); });
} }
...@@ -185,11 +190,11 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet ...@@ -185,11 +190,11 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet
} }
private void preloadContentInSheet(ClusterList clusters, String title) { private void preloadContentInSheet(ClusterList clusters, String title) {
if (mBridge == null) return; if (mSuggestionsSource == null) return;
mModel.setClusterList(clusters); mModel.setClusterList(clusters);
mModel.setCloseButtonOnClickListener(view -> { clearSuggestions(); }); mModel.setCloseButtonOnClickListener(view -> { clearSuggestions(); });
mModel.setTitle(mContext.getString(R.string.contextual_suggestions_toolbar_title, title)); mModel.setTitle(title);
mCoordinator.preloadContentInSheet(); mCoordinator.preloadContentInSheet();
} }
...@@ -199,7 +204,7 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet ...@@ -199,7 +204,7 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet
mSheetObserver = new EmptyBottomSheetObserver() { mSheetObserver = new EmptyBottomSheetObserver() {
@Override @Override
public void onSheetOpened(@StateChangeReason int reason) { public void onSheetOpened(@StateChangeReason int reason) {
mCoordinator.showSuggestions(mBridge); mCoordinator.showSuggestions(mSuggestionsSource);
mCoordinator.removeBottomSheetObserver(this); mCoordinator.removeBottomSheetObserver(this);
mSheetObserver = null; mSheetObserver = null;
} }
...@@ -210,38 +215,46 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet ...@@ -210,38 +215,46 @@ class ContextualSuggestionsMediator implements EnabledStateMonitor.Observer, Fet
} }
// TODO(twellington): Remove after clusters are returned from the backend. // TODO(twellington): Remove after clusters are returned from the backend.
private ClusterList generateClusterList(List<SnippetArticle> suggestions) { private ClusterList generateClusterList(List<ContextualSuggestionsCluster> clusters) {
List<ContextualSuggestionsCluster> clusters = new ArrayList<>(); if (clusters.size() != 1) {
for (ContextualSuggestionsCluster cluster : clusters) {
cluster.buildChildren();
}
return new ClusterList(clusters);
}
List<SnippetArticle> suggestions = clusters.get(0).getSuggestions();
List<ContextualSuggestionsCluster> newClusters = new ArrayList<>();
int clusterSize = suggestions.size() >= 6 ? 3 : 2; int clusterSize = suggestions.size() >= 6 ? 3 : 2;
int numClusters = suggestions.size() < 4 ? 1 : suggestions.size() / clusterSize; int numClusters = suggestions.size() < 4 ? 1 : suggestions.size() / clusterSize;
int currentSuggestion = 0; int currentSuggestion = 0;
// Construct a list of clusters. // Construct a list of clusters.
for (int i = 0; i < numClusters; i++) { for (int i = 0; i < numClusters; i++) {
ContextualSuggestionsCluster cluster = ContextualSuggestionsCluster cluster = new ContextualSuggestionsCluster(
new ContextualSuggestionsCluster(suggestions.get(currentSuggestion).mTitle); i != 0 ? suggestions.get(currentSuggestion).mTitle : "");
if (i == 0) cluster.setShouldShowTitle(false);
for (int j = 0; j < clusterSize; j++) { for (int j = 0; j < clusterSize; j++) {
cluster.getSuggestions().add(suggestions.get(currentSuggestion)); cluster.getSuggestions().add(suggestions.get(currentSuggestion));
currentSuggestion++; currentSuggestion++;
} }
clusters.add(cluster); newClusters.add(cluster);
} }
// Add the remaining suggestions to the last cluster. // Add the remaining suggestions to the last cluster.
while (currentSuggestion < suggestions.size()) { while (currentSuggestion < suggestions.size()) {
clusters.get(clusters.size() - 1) newClusters.get(newClusters.size() - 1)
.getSuggestions() .getSuggestions()
.add(suggestions.get(currentSuggestion)); .add(suggestions.get(currentSuggestion));
currentSuggestion++; currentSuggestion++;
} }
for (ContextualSuggestionsCluster cluster : clusters) { for (ContextualSuggestionsCluster cluster : newClusters) {
cluster.buildChildren(); cluster.buildChildren();
} }
return new ClusterList(clusters); return new ClusterList(newClusters);
} }
} }
// Copyright 2018 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.contextual_suggestions;
import android.graphics.Bitmap;
import org.chromium.base.Callback;
import org.chromium.chrome.browser.ntp.cards.SuggestionsCategoryInfo;
import org.chromium.chrome.browser.ntp.snippets.SnippetArticle;
import org.chromium.chrome.browser.ntp.snippets.SuggestionsSource;
import org.chromium.chrome.browser.profiles.Profile;
import java.util.List;
/**
* Provides content for contextual suggestions.
*/
class ContextualSuggestionsSource implements SuggestionsSource {
private final ContextualSuggestionsBridge mBridge;
/**
* Creates a ContextualSuggestionsSource for getting contextual suggestions for the current
* user.
*
* @param profile Profile of the user.
*/
ContextualSuggestionsSource(Profile profile) {
mBridge = new ContextualSuggestionsBridge(profile);
}
@Override
public void destroy() {
mBridge.destroy();
}
@Override
public void fetchSuggestionImage(SnippetArticle suggestion, Callback<Bitmap> callback) {
mBridge.fetchSuggestionImage(suggestion, callback);
}
@Override
public void fetchContextualSuggestionImage(
SnippetArticle suggestion, Callback<Bitmap> callback) {
mBridge.fetchSuggestionImage(suggestion, callback);
}
@Override
public void fetchSuggestionFavicon(SnippetArticle suggestion, int minimumSizePx,
int desiredSizePx, Callback<Bitmap> callback) {
mBridge.fetchSuggestionFavicon(suggestion, callback);
}
/**
* @return The {@link ContextualSuggestionsBridge} used communicate with the contextual
* suggestions C++ component.
*/
ContextualSuggestionsBridge getBridge() {
return mBridge;
}
// The following methods are not applicable to contextual suggestions.
// TODO(twellington): The NTP classes used to display suggestion cards rely
// on the SuggestionsSource implementation. Refactor to limit reliance to the
// subset of methods actually used to render cards.
@Override
public void fetchRemoteSuggestions() {}
@Override
public boolean areRemoteSuggestionsEnabled() {
return false;
}
@Override
public int[] getCategories() {
return null;
}
@Override
public int getCategoryStatus(int category) {
return 0;
}
@Override
public SuggestionsCategoryInfo getCategoryInfo(int category) {
return null;
}
@Override
public List<SnippetArticle> getSuggestionsForCategory(int category) {
return null;
}
@Override
public void fetchSuggestions(int category, String[] displayedSuggestionIds,
Callback<List<SnippetArticle>> successCallback, Runnable failureRunnable) {}
@Override
public void fetchContextualSuggestions(String url, Callback<List<SnippetArticle>> callback) {}
@Override
public void dismissSuggestion(SnippetArticle suggestion) {}
@Override
public void dismissCategory(int category) {}
@Override
public void restoreDismissedCategories() {}
@Override
public void addObserver(Observer observer) {}
@Override
public void removeObserver(Observer observer) {}
}
...@@ -2447,9 +2447,6 @@ Google may use your browsing activity, content on some sites you visit, and othe ...@@ -2447,9 +2447,6 @@ Google may use your browsing activity, content on some sites you visit, and othe
<message name="IDS_NTP_ALL_DISMISSED_REFRESH" desc="Text label for button to refresh the New Tab Page when all suggested content has been dismissed. [CHAR-LIMIT=20]"> <message name="IDS_NTP_ALL_DISMISSED_REFRESH" desc="Text label for button to refresh the New Tab Page when all suggested content has been dismissed. [CHAR-LIMIT=20]">
Refresh Refresh
</message> </message>
<message name="IDS_CONTEXTUAL_SUGGESTIONS_TOOLBAR_TITLE" desc="The title displayed above a list of content suggestions based on the user's current webpage. The title tells the user that the list contains more suggestions related to the topic they are currently viewing.">
More about <ph name="TOPIC">%1$s<ex>Game of Thrones</ex></ph>
</message>
<!-- Toolbar button strings --> <!-- Toolbar button strings -->
<message name="IDS_OPEN_TABS" desc="Text for button to enter the tab switcher and show tabs that are open on this device"> <message name="IDS_OPEN_TABS" desc="Text for button to enter the tab switcher and show tabs that are open on this device">
......
...@@ -301,6 +301,7 @@ chrome_java_sources = [ ...@@ -301,6 +301,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsCoordinator.java", "java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsCoordinator.java",
"java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsMediator.java", "java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsMediator.java",
"java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsModel.java", "java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsModel.java",
"java/src/org/chromium/chrome/browser/contextual_suggestions/ContextualSuggestionsSource.java",
"java/src/org/chromium/chrome/browser/contextual_suggestions/DummyEventReporter.java", "java/src/org/chromium/chrome/browser/contextual_suggestions/DummyEventReporter.java",
"java/src/org/chromium/chrome/browser/contextual_suggestions/EnabledStateMonitor.java", "java/src/org/chromium/chrome/browser/contextual_suggestions/EnabledStateMonitor.java",
"java/src/org/chromium/chrome/browser/contextual_suggestions/FetchHelper.java", "java/src/org/chromium/chrome/browser/contextual_suggestions/FetchHelper.java",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment