• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.shell.omnibox;
6 
7 import android.content.Context;
8 import android.os.Handler;
9 import android.text.Editable;
10 import android.text.TextUtils;
11 import android.text.TextWatcher;
12 import android.view.View;
13 import android.view.View.OnLayoutChangeListener;
14 import android.widget.AdapterView;
15 import android.widget.AdapterView.OnItemClickListener;
16 import android.widget.ListPopupWindow;
17 import android.widget.PopupWindow.OnDismissListener;
18 import android.widget.TextView;
19 
20 import org.chromium.chrome.browser.omnibox.AutocompleteController;
21 import org.chromium.chrome.browser.omnibox.AutocompleteController.OnSuggestionsReceivedListener;
22 import org.chromium.chrome.browser.omnibox.OmniboxSuggestion;
23 import org.chromium.chrome.shell.ChromeShellToolbar;
24 import org.chromium.chrome.shell.R;
25 
26 import java.util.List;
27 
28 /**
29  * Displays suggestions for the text that is entered to the ChromeShell URL field.
30  */
31 public class SuggestionPopup implements OnSuggestionsReceivedListener, TextWatcher {
32     private static final long SUGGESTION_START_DELAY_MS = 30;
33 
34     private final Context mContext;
35     private final TextView mUrlField;
36     private final ChromeShellToolbar mToolbar;
37     private final AutocompleteController mAutocomplete;
38 
39     private boolean mHasStartedNewOmniboxEditSession;
40     private Runnable mRequestSuggestions;
41     private ListPopupWindow mSuggestionsPopup;
42     private SuggestionArrayAdapter mSuggestionArrayAdapter;
43 
44     /**
45      * Initializes a suggestion popup that will track urlField value and display suggestions based
46      * on that value.
47      */
SuggestionPopup(Context context, TextView urlField, ChromeShellToolbar toolbar)48     public SuggestionPopup(Context context, TextView urlField,
49             ChromeShellToolbar toolbar) {
50         mContext = context;
51         mUrlField = urlField;
52         mToolbar = toolbar;
53         mAutocomplete = new AutocompleteController(this);
54         OnLayoutChangeListener listener = new OnLayoutChangeListener() {
55             @Override
56             public void onLayoutChange(View v, int left, int top, int right, int bottom,
57                     int oldLeft, int oldTop, int oldRight, int oldBottom) {
58                 if (mSuggestionsPopup == null || !mSuggestionsPopup.isShowing()) return;
59                 mSuggestionsPopup.setWidth(mUrlField.getWidth());
60                 mSuggestionsPopup.show();
61             }
62         };
63         mUrlField.addOnLayoutChangeListener(listener);
64     }
65 
navigateToSuggestion(int position)66     private void navigateToSuggestion(int position) {
67         mToolbar.getCurrentTab().loadUrlWithSanitization(
68                 mSuggestionArrayAdapter.getItem(position).getUrl());
69         mUrlField.clearFocus();
70         mToolbar.setKeyboardVisibilityForUrl(false);
71         mToolbar.getCurrentTab().getView().requestFocus();
72         dismissPopup();
73     }
74 
dismissPopup()75     public void dismissPopup() {
76         if (mSuggestionsPopup != null) {
77             mSuggestionsPopup.dismiss();
78             mSuggestionsPopup = null;
79         }
80     }
81 
82     /**
83      * Stops the autocomplete controller and closes the suggestion popup.
84      */
hideSuggestions()85     public void hideSuggestions() {
86         stopAutocomplete(true);
87         dismissPopup();
88     }
89 
90     /**
91      * Signals the autocomplete controller to stop generating suggestions and
92      * cancels the queued task to start the autocomplete controller, if any.
93      *
94      * @param clear Whether to clear the most recent autocomplete results.
95      */
stopAutocomplete(boolean clear)96     private void stopAutocomplete(boolean clear) {
97         if (mAutocomplete != null) mAutocomplete.stop(clear);
98         if (mRequestSuggestions != null) mRequestSuggestions = null;
99     }
100 
101     // OnSuggestionsReceivedListener implementation
102 
103     @Override
onSuggestionsReceived(List<OmniboxSuggestion> suggestions, String inlineAutocompleteText)104     public void onSuggestionsReceived(List<OmniboxSuggestion> suggestions,
105             String inlineAutocompleteText) {
106         if (!mUrlField.isFocused() || suggestions.isEmpty())
107             return;
108         if (mSuggestionsPopup == null) {
109             mSuggestionsPopup = new ListPopupWindow(
110                     mContext, null, android.R.attr.autoCompleteTextViewStyle);
111             mSuggestionsPopup.setOnDismissListener(new OnDismissListener() {
112                 @Override
113                 public void onDismiss() {
114                     mHasStartedNewOmniboxEditSession = false;
115                     mSuggestionArrayAdapter = null;
116                 }
117             });
118         }
119         mSuggestionsPopup.setWidth(mUrlField.getWidth());
120         mSuggestionArrayAdapter =
121                 new SuggestionArrayAdapter(mContext, R.layout.dropdown_item, suggestions);
122         mSuggestionsPopup.setAdapter(mSuggestionArrayAdapter);
123         mSuggestionsPopup.setAnchorView(mUrlField);
124         mSuggestionsPopup.setOnItemClickListener(new OnItemClickListener() {
125             @Override
126             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
127                 navigateToSuggestion(position);
128             }
129         });
130         mSuggestionsPopup.show();
131     }
132 
133     // TextWatcher implementation
134 
135     @Override
afterTextChanged(final Editable editableText)136     public void afterTextChanged(final Editable editableText) {
137         if (!mUrlField.hasFocus()) return;
138         if (!mHasStartedNewOmniboxEditSession) {
139             mAutocomplete.resetSession();
140             mHasStartedNewOmniboxEditSession = true;
141         }
142 
143         stopAutocomplete(false);
144         if (TextUtils.isEmpty(editableText)) {
145             dismissPopup();
146         } else {
147             assert mRequestSuggestions == null : "Multiple omnibox requests in flight.";
148             mRequestSuggestions = new Runnable() {
149                 @Override
150                 public void run() {
151                     mRequestSuggestions = null;
152                     mAutocomplete.start(
153                             mToolbar.getCurrentTab().getProfile(),
154                             mToolbar.getCurrentTab().getUrl(),
155                             editableText.toString(), false);
156                 }
157             };
158             new Handler().postDelayed(mRequestSuggestions, SUGGESTION_START_DELAY_MS);
159         }
160     }
161 
162     @Override
beforeTextChanged(CharSequence s, int start, int count, int after)163     public void beforeTextChanged(CharSequence s, int start, int count, int after) {
164         mRequestSuggestions = null;
165     }
166 
167     @Override
onTextChanged(CharSequence s, int start, int before, int count)168     public void onTextChanged(CharSequence s, int start, int before, int count) {
169     }
170 }
171