2835c612904ac874983f808944c923e87f8e1f31
[platform/framework/web/crosswalk.git] / src / chrome / android / java / src / org / chromium / chrome / browser / omnibox / AutocompleteController.java
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.
4
5 package org.chromium.chrome.browser.omnibox;
6
7 import android.text.TextUtils;
8
9 import com.google.common.annotations.VisibleForTesting;
10
11 import org.chromium.base.CalledByNative;
12 import org.chromium.chrome.browser.profiles.Profile;
13 import org.chromium.content_public.browser.WebContents;
14
15 import java.util.ArrayList;
16 import java.util.List;
17
18 /**
19  * Bridge to the native AutocompleteControllerAndroid.
20  */
21 public class AutocompleteController {
22     private long mNativeAutocompleteControllerAndroid;
23     private long mCurrentNativeAutocompleteResult;
24     private final OnSuggestionsReceivedListener mListener;
25
26     /**
27      * Listener for receiving OmniboxSuggestions.
28      */
29     public static interface OnSuggestionsReceivedListener {
30         void onSuggestionsReceived(List<OmniboxSuggestion> suggestions,
31                 String inlineAutocompleteText);
32     }
33
34     public AutocompleteController(OnSuggestionsReceivedListener listener) {
35         this(null, listener);
36     }
37
38     public AutocompleteController(Profile profile, OnSuggestionsReceivedListener listener) {
39         if (profile != null) {
40             mNativeAutocompleteControllerAndroid = nativeInit(profile);
41         }
42         mListener = listener;
43     }
44
45     /**
46      * Resets the underlying autocomplete controller based on the specified profile.
47      *
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.
52      *
53      * @param profile The profile to reset the AutocompleteController with.
54      */
55     public void setProfile(Profile profile) {
56         stop(true);
57         if (profile == null) {
58             mNativeAutocompleteControllerAndroid = 0;
59             return;
60         }
61
62         mNativeAutocompleteControllerAndroid = nativeInit(profile);
63     }
64
65     /**
66      * Starts querying for omnibox suggestions for a given text.
67      *
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.
72      */
73     public void start(Profile profile, String url, String text, boolean preventInlineAutocomplete) {
74         if (profile == null || TextUtils.isEmpty(url)) return;
75
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);
81         }
82     }
83
84     /**
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).
88      *
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.
92      *
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.
96      */
97     public OmniboxSuggestion classify(String text) {
98         return nativeClassify(mNativeAutocompleteControllerAndroid, text);
99     }
100
101     /**
102      * Starts a query for suggestions before any input is available from the user.
103      *
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.
110      */
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);
118         }
119     }
120
121     /**
122      * Stops generating autocomplete suggestions for the currently specified text from
123      * {@link #start(Profile,String, String, boolean)}.
124      *
125      * <p>
126      * Calling this method with {@code false}, will result in
127      * {@link #onSuggestionsReceived(List, String, long)} being called with an empty
128      * result set.
129      *
130      * @param clear Whether to clear the most recent autocomplete results.
131      */
132     public void stop(boolean clear) {
133         mCurrentNativeAutocompleteResult = 0;
134         if (mNativeAutocompleteControllerAndroid != 0) {
135             nativeStop(mNativeAutocompleteControllerAndroid, clear);
136         }
137     }
138
139     /**
140      * Resets session for autocomplete controller. This happens every time we start typing
141      * new input into the omnibox.
142      */
143     public void resetSession() {
144         if (mNativeAutocompleteControllerAndroid != 0) {
145             nativeResetSession(mNativeAutocompleteControllerAndroid);
146         }
147     }
148
149     /**
150      * Deletes an omnibox suggestion, if possible.
151      * @param position The position at which the suggestion is located.
152      */
153     public void deleteSuggestion(int position) {
154         if (mNativeAutocompleteControllerAndroid != 0) {
155             nativeDeleteSuggestion(mNativeAutocompleteControllerAndroid, position);
156         }
157     }
158
159     /**
160      * @return Native pointer to current autocomplete results.
161      */
162     @VisibleForTesting
163     public long getCurrentNativeAutocompleteResult() {
164         return mCurrentNativeAutocompleteResult;
165     }
166
167     @CalledByNative
168     protected void onSuggestionsReceived(
169             List<OmniboxSuggestion> suggestions,
170             String inlineAutocompleteText,
171             long currentNativeAutocompleteResult) {
172         mCurrentNativeAutocompleteResult = currentNativeAutocompleteResult;
173
174         // Notify callbacks of suggestions.
175         mListener.onSuggestionsReceived(suggestions, inlineAutocompleteText);
176     }
177
178     @CalledByNative
179     private void notifyNativeDestroyed() {
180         mNativeAutocompleteControllerAndroid = 0;
181     }
182
183     /**
184      * Called whenever a navigation happens from the omnibox to record metrics about the user's
185      * interaction with the omnibox.
186      *
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.
195      */
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,
201                 webContents);
202     }
203
204     @CalledByNative
205     private static List<OmniboxSuggestion> createOmniboxSuggestionList(int size) {
206         return new ArrayList<OmniboxSuggestion>(size);
207     }
208
209     @CalledByNative
210     private static void addOmniboxSuggestionToList(List<OmniboxSuggestion> suggestionList,
211             OmniboxSuggestion suggestion) {
212         suggestionList.add(suggestion);
213     }
214
215     @CalledByNative
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,
222                 isDeletable);
223     }
224
225     /**
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
229      * selection.
230      *
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
234      *                                    a suggestion.
235      * @return The url to navigate to for this match with aqs parameter updated, if we are
236      *         making a Google search query.
237      */
238     public String updateMatchDestinationUrl(int selectedIndex, long elapsedTimeSinceInputChange) {
239         return nativeUpdateMatchDestinationURL(mNativeAutocompleteControllerAndroid, selectedIndex,
240                 elapsedTimeSinceInputChange);
241     }
242
243     /**
244      * @param query User input text.
245      * @return The top synchronous suggestion from the autocomplete controller.
246      */
247     public OmniboxSuggestion getTopSynchronousMatch(String query) {
248         return nativeGetTopSynchronousMatch(mNativeAutocompleteControllerAndroid, query);
249     }
250
251     @VisibleForTesting
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,
257             String text);
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,
267             int selectedIndex);
268     private native String nativeUpdateMatchDestinationURL(long nativeAutocompleteControllerAndroid,
269             int selectedIndex, long elapsedTimeSinceInputChange);
270     private native OmniboxSuggestion nativeGetTopSynchronousMatch(
271             long nativeAutocompleteControllerAndroid, String query);
272
273     /**
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.
278      *
279      * @param query The query to be expanded into a fully qualified URL if appropriate.
280      * @return The fully qualified URL or null.
281      */
282     public static native String nativeQualifyPartialURLQuery(String query);
283
284     /**
285      * Sends a zero suggest request to the server in order to pre-populate the result cache.
286      */
287     public static native void nativePrefetchZeroSuggestResults();
288 }