1 // Copyright 2014 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.
5 package org.chromium.chrome.browser.omnibox;
7 import android.text.TextUtils;
9 import com.google.common.annotations.VisibleForTesting;
11 import org.chromium.base.CalledByNative;
12 import org.chromium.chrome.browser.profiles.Profile;
13 import org.chromium.content_public.browser.WebContents;
15 import java.util.ArrayList;
16 import java.util.List;
19 * Bridge to the native AutocompleteControllerAndroid.
21 public class AutocompleteController {
22 private long mNativeAutocompleteControllerAndroid;
23 private long mCurrentNativeAutocompleteResult;
24 private final OnSuggestionsReceivedListener mListener;
27 * Listener for receiving OmniboxSuggestions.
29 public static interface OnSuggestionsReceivedListener {
30 void onSuggestionsReceived(List<OmniboxSuggestion> suggestions,
31 String inlineAutocompleteText);
34 public AutocompleteController(OnSuggestionsReceivedListener listener) {
38 public AutocompleteController(Profile profile, OnSuggestionsReceivedListener listener) {
39 if (profile != null) {
40 mNativeAutocompleteControllerAndroid = nativeInit(profile);
46 * Resets the underlying autocomplete controller based on the specified profile.
48 * <p>This will implicitly stop the autocomplete suggestions, so
49 * {@link #start(Profile, String, String, boolean)} must be called again to start them flowing
50 * again. This should not be an issue as changing profiles should not normally occur while
51 * waiting on omnibox suggestions.
53 * @param profile The profile to reset the AutocompleteController with.
55 public void setProfile(Profile profile) {
57 if (profile == null) {
58 mNativeAutocompleteControllerAndroid = 0;
62 mNativeAutocompleteControllerAndroid = nativeInit(profile);
66 * Starts querying for omnibox suggestions for a given text.
68 * @param profile The profile to use for starting the AutocompleteController
69 * @param url The URL of the current tab, used to suggest query refinements.
70 * @param text The text to query autocomplete suggestions for.
71 * @param preventInlineAutocomplete Whether autocomplete suggestions should be prevented.
73 public void start(Profile profile, String url, String text, boolean preventInlineAutocomplete) {
74 if (profile == null || TextUtils.isEmpty(url)) return;
76 mNativeAutocompleteControllerAndroid = nativeInit(profile);
77 // Initializing the native counterpart might still fail.
78 if (mNativeAutocompleteControllerAndroid != 0) {
79 nativeStart(mNativeAutocompleteControllerAndroid, text, null, url,
80 preventInlineAutocomplete, false, false, true);
85 * Given some string |text| that the user wants to use for navigation, determines how it should
86 * be interpreted. This is a fallback in case the user didn't select a visible suggestion (e.g.
87 * the user pressed enter before omnibox suggestions had been shown).
89 * Note: this updates the internal state of the autocomplete controller just as start() does.
90 * Future calls that reference autocomplete results by index, e.g. onSuggestionSelected(),
91 * should reference the returned suggestion by index 0.
93 * @param text The user's input text to classify (i.e. what they typed in the omnibox)
94 * @return The OmniboxSuggestion specifying where to navigate, the transition type, etc. May
95 * be null if the input is invalid.
97 public OmniboxSuggestion classify(String text) {
98 return nativeClassify(mNativeAutocompleteControllerAndroid, text);
102 * Starts a query for suggestions before any input is available from the user.
104 * @param profile The profile to use for starting the AutocompleteController.
105 * @param omniboxText The text displayed in the omnibox.
106 * @param url The url of the currently loaded web page.
107 * @param isQueryInOmnibox Whether the location bar is currently showing a search query.
108 * @param focusedFromFakebox Whether the user entered the omnibox by tapping the fakebox on the
109 * native NTP. This should be false on all other pages.
111 public void startZeroSuggest(Profile profile, String omniboxText, String url,
112 boolean isQueryInOmnibox, boolean focusedFromFakebox) {
113 if (profile == null || TextUtils.isEmpty(url)) return;
114 mNativeAutocompleteControllerAndroid = nativeInit(profile);
115 if (mNativeAutocompleteControllerAndroid != 0) {
116 nativeStartZeroSuggest(mNativeAutocompleteControllerAndroid, omniboxText, url,
117 isQueryInOmnibox, focusedFromFakebox);
122 * Stops generating autocomplete suggestions for the currently specified text from
123 * {@link #start(Profile,String, String, boolean)}.
126 * Calling this method with {@code false}, will result in
127 * {@link #onSuggestionsReceived(List, String, long)} being called with an empty
130 * @param clear Whether to clear the most recent autocomplete results.
132 public void stop(boolean clear) {
133 mCurrentNativeAutocompleteResult = 0;
134 if (mNativeAutocompleteControllerAndroid != 0) {
135 nativeStop(mNativeAutocompleteControllerAndroid, clear);
140 * Resets session for autocomplete controller. This happens every time we start typing
141 * new input into the omnibox.
143 public void resetSession() {
144 if (mNativeAutocompleteControllerAndroid != 0) {
145 nativeResetSession(mNativeAutocompleteControllerAndroid);
150 * Deletes an omnibox suggestion, if possible.
151 * @param position The position at which the suggestion is located.
153 public void deleteSuggestion(int position) {
154 if (mNativeAutocompleteControllerAndroid != 0) {
155 nativeDeleteSuggestion(mNativeAutocompleteControllerAndroid, position);
160 * @return Native pointer to current autocomplete results.
163 public long getCurrentNativeAutocompleteResult() {
164 return mCurrentNativeAutocompleteResult;
168 protected void onSuggestionsReceived(
169 List<OmniboxSuggestion> suggestions,
170 String inlineAutocompleteText,
171 long currentNativeAutocompleteResult) {
172 mCurrentNativeAutocompleteResult = currentNativeAutocompleteResult;
174 // Notify callbacks of suggestions.
175 mListener.onSuggestionsReceived(suggestions, inlineAutocompleteText);
179 private void notifyNativeDestroyed() {
180 mNativeAutocompleteControllerAndroid = 0;
184 * Called whenever a navigation happens from the omnibox to record metrics about the user's
185 * interaction with the omnibox.
187 * @param selectedIndex The index of the suggestion that was selected.
188 * @param type The type of the selected suggestion.
189 * @param currentPageUrl The URL of the current page.
190 * @param focusedFromFakebox Whether the user entered the omnibox by tapping the fakebox on the
191 * native NTP. This should be false on all other pages.
192 * @param elapsedTimeSinceModified The number of ms that passed between the user first
193 * modifying text in the omnibox and selecting a suggestion.
194 * @param webContents The web contents for the tab where the selected suggestion will be shown.
196 protected void onSuggestionSelected(int selectedIndex, OmniboxSuggestion.Type type,
197 String currentPageUrl, boolean isQueryInOmnibox, boolean focusedFromFakebox,
198 long elapsedTimeSinceModified, WebContents webContents) {
199 nativeOnSuggestionSelected(mNativeAutocompleteControllerAndroid, selectedIndex,
200 currentPageUrl, isQueryInOmnibox, focusedFromFakebox, elapsedTimeSinceModified,
205 private static List<OmniboxSuggestion> createOmniboxSuggestionList(int size) {
206 return new ArrayList<OmniboxSuggestion>(size);
210 private static void addOmniboxSuggestionToList(List<OmniboxSuggestion> suggestionList,
211 OmniboxSuggestion suggestion) {
212 suggestionList.add(suggestion);
216 private static OmniboxSuggestion buildOmniboxSuggestion(int nativeType, int relevance,
217 int transition, String text, String description, String answerContents,
218 String answerType, String fillIntoEdit, String url, String formattedUrl,
219 boolean isStarred, boolean isDeletable) {
220 return new OmniboxSuggestion(nativeType, relevance, transition, text, description,
221 answerContents, answerType, fillIntoEdit, url, formattedUrl, isStarred,
226 * Updates aqs parameters on the selected match that we will navigate to and returns the
227 * updated URL. |selected_index| is the position of the selected match and
228 * |elapsed_time_since_input_change| is the time in ms between the first typed input and match
231 * @param selectedIndex The index of the autocomplete entry selected.
232 * @param elapsedTimeSinceInputChange The number of ms between the time the user started
233 * typing in the omnibox and the time the user has selected
235 * @return The url to navigate to for this match with aqs parameter updated, if we are
236 * making a Google search query.
238 public String updateMatchDestinationUrl(int selectedIndex, long elapsedTimeSinceInputChange) {
239 return nativeUpdateMatchDestinationURL(mNativeAutocompleteControllerAndroid, selectedIndex,
240 elapsedTimeSinceInputChange);
244 * @param query User input text.
245 * @return The top synchronous suggestion from the autocomplete controller.
247 public OmniboxSuggestion getTopSynchronousMatch(String query) {
248 return nativeGetTopSynchronousMatch(mNativeAutocompleteControllerAndroid, query);
252 protected native long nativeInit(Profile profile);
253 private native void nativeStart(long nativeAutocompleteControllerAndroid, String text,
254 String desiredTld, String currentUrl, boolean preventInlineAutocomplete,
255 boolean preferKeyword, boolean allowExactKeywordMatch, boolean wantAsynchronousMatches);
256 private native OmniboxSuggestion nativeClassify(long nativeAutocompleteControllerAndroid,
258 private native void nativeStop(long nativeAutocompleteControllerAndroid, boolean clearResults);
259 private native void nativeResetSession(long nativeAutocompleteControllerAndroid);
260 private native void nativeOnSuggestionSelected(long nativeAutocompleteControllerAndroid,
261 int selectedIndex, String currentPageUrl, boolean isQueryInOmnibox,
262 boolean focusedFromFakebox, long elapsedTimeSinceModified, WebContents webContents);
263 private native void nativeStartZeroSuggest(long nativeAutocompleteControllerAndroid,
264 String omniboxText, String currentUrl, boolean isQueryInOmnibox,
265 boolean focusedFromFakebox);
266 private native void nativeDeleteSuggestion(long nativeAutocompleteControllerAndroid,
268 private native String nativeUpdateMatchDestinationURL(long nativeAutocompleteControllerAndroid,
269 int selectedIndex, long elapsedTimeSinceInputChange);
270 private native OmniboxSuggestion nativeGetTopSynchronousMatch(
271 long nativeAutocompleteControllerAndroid, String query);
274 * Given a search query, this will attempt to see if the query appears to be portion of a
275 * properly formed URL. If it appears to be a URL, this will return the fully qualified
276 * version (i.e. including the scheme, etc...). If the query does not appear to be a URL,
277 * this will return null.
279 * @param query The query to be expanded into a fully qualified URL if appropriate.
280 * @return The fully qualified URL or null.
282 public static native String nativeQualifyPartialURLQuery(String query);
285 * Sends a zero suggest request to the server in order to pre-populate the result cache.
287 public static native void nativePrefetchZeroSuggestResults();