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.shell.omnibox;
7 import android.content.Context;
8 import android.graphics.Rect;
9 import android.os.Handler;
10 import android.text.Editable;
11 import android.text.TextUtils;
12 import android.text.TextWatcher;
13 import android.view.View;
14 import android.view.View.OnLayoutChangeListener;
15 import android.widget.AdapterView;
16 import android.widget.AdapterView.OnItemClickListener;
17 import android.widget.EditText;
18 import android.widget.ListPopupWindow;
19 import android.widget.PopupWindow.OnDismissListener;
22 import org.chromium.chrome.browser.omnibox.AutocompleteController;
23 import org.chromium.chrome.browser.omnibox.AutocompleteController.OnSuggestionsReceivedListener;
24 import org.chromium.chrome.browser.omnibox.OmniboxSuggestion;
25 import org.chromium.chrome.shell.ChromeShellActivity;
26 import org.chromium.chrome.shell.ChromeShellToolbar;
27 import org.chromium.chrome.shell.R;
29 import java.util.List;
32 * Displays suggestions for the text that is entered to the ChromeShell URL field.
34 public class SuggestionPopup implements OnSuggestionsReceivedListener, TextWatcher {
35 private static final long SUGGESTION_START_DELAY_MS = 30;
37 private final Context mContext;
38 private final EditText mUrlField;
39 private final ChromeShellToolbar mToolbar;
40 private final AutocompleteController mAutocomplete;
42 private boolean mHasStartedNewOmniboxEditSession;
43 private Runnable mRequestSuggestions;
44 private ListPopupWindow mSuggestionsPopup;
45 private SuggestionArrayAdapter mSuggestionArrayAdapter;
46 private int mSuggestionsPopupItemsCount;
49 * Initializes a suggestion popup that will track urlField value and display suggestions based
52 public SuggestionPopup(Context context, EditText urlField, ChromeShellToolbar toolbar) {
56 mAutocomplete = new AutocompleteController(this);
57 OnLayoutChangeListener listener = new OnLayoutChangeListener() {
59 public void onLayoutChange(View v, int left, int top, int right, int bottom,
60 int oldLeft, int oldTop, int oldRight, int oldBottom) {
61 if (mSuggestionsPopup == null || !mSuggestionsPopup.isShowing()) return;
62 mSuggestionsPopup.setWidth(mToolbar.getWidth());
63 mSuggestionsPopup.setHeight(getSuggestionPopupHeight());
64 mSuggestionsPopup.show();
67 mUrlField.addOnLayoutChangeListener(listener);
70 private void navigateToSuggestion(int position) {
71 mToolbar.getCurrentTab().loadUrlWithSanitization(
72 mSuggestionArrayAdapter.getItem(position).getUrl());
73 mUrlField.clearFocus();
74 mToolbar.setKeyboardVisibilityForUrl(false);
75 mToolbar.getCurrentTab().getView().requestFocus();
79 public void dismissPopup() {
80 if (mSuggestionsPopup != null) {
81 mSuggestionsPopup.dismiss();
82 mSuggestionsPopup = null;
87 * Stops the autocomplete controller and closes the suggestion popup.
89 public void hideSuggestions() {
90 stopAutocomplete(true);
95 * Signals the autocomplete controller to stop generating suggestions and
96 * cancels the queued task to start the autocomplete controller, if any.
98 * @param clear Whether to clear the most recent autocomplete results.
100 private void stopAutocomplete(boolean clear) {
101 if (mAutocomplete != null) mAutocomplete.stop(clear);
102 if (mRequestSuggestions != null) mRequestSuggestions = null;
105 private int getSuggestionPopupHeight() {
106 Rect appRect = new Rect();
107 ((ChromeShellActivity) mContext).getWindow().getDecorView()
108 .getWindowVisibleDisplayFrame(appRect);
109 int dropDownItemHeight = mContext.getResources()
110 .getDimensionPixelSize(R.dimen.dropdown_item_height);
111 // Applying margin height equal to |dropDownItemHeight| if constrained by app rect.
112 int popupHeight = appRect.height() - dropDownItemHeight;
113 if (mSuggestionsPopup != null) {
114 int height = mSuggestionsPopupItemsCount * dropDownItemHeight;
115 if (height < popupHeight)
116 popupHeight = height;
121 // OnSuggestionsReceivedListener implementation
123 public void onSuggestionsReceived(List<OmniboxSuggestion> suggestions,
124 String inlineAutocompleteText) {
125 if (!mUrlField.isFocused() || suggestions.isEmpty())
127 mSuggestionsPopupItemsCount = suggestions.size();
128 if (mSuggestionsPopup == null) {
129 mSuggestionsPopup = new ListPopupWindow(
130 mContext, null, android.R.attr.autoCompleteTextViewStyle);
131 mSuggestionsPopup.setOnDismissListener(new OnDismissListener() {
133 public void onDismiss() {
134 mHasStartedNewOmniboxEditSession = false;
135 mSuggestionArrayAdapter = null;
139 mSuggestionsPopup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NEEDED);
140 mSuggestionsPopup.setWidth(mToolbar.getWidth());
141 mSuggestionArrayAdapter =
142 new SuggestionArrayAdapter(mContext, R.layout.dropdown_item, suggestions,
144 mSuggestionsPopup.setHeight(getSuggestionPopupHeight());
145 mSuggestionsPopup.setAdapter(mSuggestionArrayAdapter);
146 mSuggestionsPopup.setAnchorView(mToolbar);
147 mSuggestionsPopup.setOnItemClickListener(new OnItemClickListener() {
149 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
150 navigateToSuggestion(position);
153 mSuggestionsPopup.show();
156 // TextWatcher implementation
159 public void afterTextChanged(final Editable editableText) {
160 if (!mUrlField.hasFocus()) return;
161 if (!mHasStartedNewOmniboxEditSession) {
162 mAutocomplete.resetSession();
163 mHasStartedNewOmniboxEditSession = true;
166 stopAutocomplete(false);
167 if (TextUtils.isEmpty(editableText)) {
170 assert mRequestSuggestions == null : "Multiple omnibox requests in flight.";
171 mRequestSuggestions = new Runnable() {
174 // TODO(aurimas): Create new tab if none exists.
175 if (mToolbar.getCurrentTab() == null) return;
176 mRequestSuggestions = null;
178 mToolbar.getCurrentTab().getProfile(),
179 mToolbar.getCurrentTab().getUrl(),
180 editableText.toString(), false);
183 new Handler().postDelayed(mRequestSuggestions, SUGGESTION_START_DELAY_MS);
188 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
189 mRequestSuggestions = null;
193 public void onTextChanged(CharSequence s, int start, int before, int count) {