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,
53 ChromeShellToolbar toolbar) {
57 mAutocomplete = new AutocompleteController(this);
58 OnLayoutChangeListener listener = new OnLayoutChangeListener() {
60 public void onLayoutChange(View v, int left, int top, int right, int bottom,
61 int oldLeft, int oldTop, int oldRight, int oldBottom) {
62 if (mSuggestionsPopup == null || !mSuggestionsPopup.isShowing()) return;
63 mSuggestionsPopup.setWidth(mUrlField.getWidth());
64 mSuggestionsPopup.setHeight(getSuggestionPopupHeight());
65 mSuggestionsPopup.show();
68 mUrlField.addOnLayoutChangeListener(listener);
71 private void navigateToSuggestion(int position) {
72 mToolbar.getCurrentTab().loadUrlWithSanitization(
73 mSuggestionArrayAdapter.getItem(position).getUrl());
74 mUrlField.clearFocus();
75 mToolbar.setKeyboardVisibilityForUrl(false);
76 mToolbar.getCurrentTab().getView().requestFocus();
80 public void dismissPopup() {
81 if (mSuggestionsPopup != null) {
82 mSuggestionsPopup.dismiss();
83 mSuggestionsPopup = null;
88 * Stops the autocomplete controller and closes the suggestion popup.
90 public void hideSuggestions() {
91 stopAutocomplete(true);
96 * Signals the autocomplete controller to stop generating suggestions and
97 * cancels the queued task to start the autocomplete controller, if any.
99 * @param clear Whether to clear the most recent autocomplete results.
101 private void stopAutocomplete(boolean clear) {
102 if (mAutocomplete != null) mAutocomplete.stop(clear);
103 if (mRequestSuggestions != null) mRequestSuggestions = null;
106 private int getSuggestionPopupHeight() {
107 Rect appRect = new Rect();
108 ((ChromeShellActivity) mContext).getWindow().getDecorView().
109 getWindowVisibleDisplayFrame(appRect);
110 int dropDownItemHeight = mContext.getResources().
111 getDimensionPixelSize(R.dimen.dropdown_item_height);
112 // Applying margin height equal to |dropDownItemHeight| if constrained by app rect.
113 int popupHeight = appRect.height() - dropDownItemHeight;
114 if (mSuggestionsPopup != null) {
115 int height = mSuggestionsPopupItemsCount * dropDownItemHeight;
116 if (height < popupHeight)
117 popupHeight = height;
122 // OnSuggestionsReceivedListener implementation
124 public void onSuggestionsReceived(List<OmniboxSuggestion> suggestions,
125 String inlineAutocompleteText) {
126 if (!mUrlField.isFocused() || suggestions.isEmpty())
128 mSuggestionsPopupItemsCount = suggestions.size();
129 if (mSuggestionsPopup == null) {
130 mSuggestionsPopup = new ListPopupWindow(
131 mContext, null, android.R.attr.autoCompleteTextViewStyle);
132 mSuggestionsPopup.setOnDismissListener(new OnDismissListener() {
134 public void onDismiss() {
135 mHasStartedNewOmniboxEditSession = false;
136 mSuggestionArrayAdapter = null;
140 mSuggestionsPopup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NEEDED);
141 mSuggestionsPopup.setWidth(mUrlField.getWidth());
142 mSuggestionArrayAdapter =
143 new SuggestionArrayAdapter(mContext, R.layout.dropdown_item, suggestions,
145 mSuggestionsPopup.setHeight(getSuggestionPopupHeight());
146 mSuggestionsPopup.setAdapter(mSuggestionArrayAdapter);
147 mSuggestionsPopup.setAnchorView(mUrlField);
148 mSuggestionsPopup.setOnItemClickListener(new OnItemClickListener() {
150 public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
151 navigateToSuggestion(position);
154 mSuggestionsPopup.show();
157 // TextWatcher implementation
160 public void afterTextChanged(final Editable editableText) {
161 if (!mUrlField.hasFocus()) return;
162 if (!mHasStartedNewOmniboxEditSession) {
163 mAutocomplete.resetSession();
164 mHasStartedNewOmniboxEditSession = true;
167 stopAutocomplete(false);
168 if (TextUtils.isEmpty(editableText)) {
171 assert mRequestSuggestions == null : "Multiple omnibox requests in flight.";
172 mRequestSuggestions = new Runnable() {
175 mRequestSuggestions = null;
177 mToolbar.getCurrentTab().getProfile(),
178 mToolbar.getCurrentTab().getUrl(),
179 editableText.toString(), false);
182 new Handler().postDelayed(mRequestSuggestions, SUGGESTION_START_DELAY_MS);
187 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
188 mRequestSuggestions = null;
192 public void onTextChanged(CharSequence s, int start, int before, int count) {