blob: e1f568279c247ac7eda513c6db4d71b01780ba57 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.shell.omnibox;
import android.content.Context;
import android.graphics.Rect;
import android.os.Handler;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.View;
import android.view.View.OnLayoutChangeListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.EditText;
import android.widget.ListPopupWindow;
import android.widget.PopupWindow.OnDismissListener;
import org.chromium.chrome.browser.omnibox.AutocompleteController;
import org.chromium.chrome.browser.omnibox.AutocompleteController.OnSuggestionsReceivedListener;
import org.chromium.chrome.browser.omnibox.OmniboxSuggestion;
import org.chromium.chrome.shell.ChromeShellActivity;
import org.chromium.chrome.shell.ChromeShellToolbar;
import org.chromium.chrome.shell.R;
import java.util.List;
/**
* Displays suggestions for the text that is entered to the ChromeShell URL field.
*/
public class SuggestionPopup implements OnSuggestionsReceivedListener, TextWatcher {
private static final long SUGGESTION_START_DELAY_MS = 30;
private final Context mContext;
private final EditText mUrlField;
private final ChromeShellToolbar mToolbar;
private final AutocompleteController mAutocomplete;
private boolean mHasStartedNewOmniboxEditSession;
private Runnable mRequestSuggestions;
private ListPopupWindow mSuggestionsPopup;
private SuggestionArrayAdapter mSuggestionArrayAdapter;
private int mSuggestionsPopupItemsCount;
/**
* Initializes a suggestion popup that will track urlField value and display suggestions based
* on that value.
*/
public SuggestionPopup(Context context, EditText urlField, ChromeShellToolbar toolbar) {
mContext = context;
mUrlField = urlField;
mToolbar = toolbar;
mAutocomplete = new AutocompleteController(this);
OnLayoutChangeListener listener = new OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
if (mSuggestionsPopup == null || !mSuggestionsPopup.isShowing()) return;
mSuggestionsPopup.setWidth(mUrlField.getWidth());
mSuggestionsPopup.setHeight(getSuggestionPopupHeight());
mSuggestionsPopup.show();
}
};
mUrlField.addOnLayoutChangeListener(listener);
}
private void navigateToSuggestion(int position) {
mToolbar.getCurrentTab().loadUrlWithSanitization(
mSuggestionArrayAdapter.getItem(position).getUrl());
mUrlField.clearFocus();
mToolbar.setKeyboardVisibilityForUrl(false);
mToolbar.getCurrentTab().getView().requestFocus();
dismissPopup();
}
public void dismissPopup() {
if (mSuggestionsPopup != null) {
mSuggestionsPopup.dismiss();
mSuggestionsPopup = null;
}
}
/**
* Stops the autocomplete controller and closes the suggestion popup.
*/
public void hideSuggestions() {
stopAutocomplete(true);
dismissPopup();
}
/**
* Signals the autocomplete controller to stop generating suggestions and
* cancels the queued task to start the autocomplete controller, if any.
*
* @param clear Whether to clear the most recent autocomplete results.
*/
private void stopAutocomplete(boolean clear) {
if (mAutocomplete != null) mAutocomplete.stop(clear);
if (mRequestSuggestions != null) mRequestSuggestions = null;
}
private int getSuggestionPopupHeight() {
Rect appRect = new Rect();
((ChromeShellActivity) mContext).getWindow().getDecorView()
.getWindowVisibleDisplayFrame(appRect);
int dropDownItemHeight = mContext.getResources()
.getDimensionPixelSize(R.dimen.dropdown_item_height);
// Applying margin height equal to |dropDownItemHeight| if constrained by app rect.
int popupHeight = appRect.height() - dropDownItemHeight;
if (mSuggestionsPopup != null) {
int height = mSuggestionsPopupItemsCount * dropDownItemHeight;
if (height < popupHeight)
popupHeight = height;
}
return popupHeight;
}
// OnSuggestionsReceivedListener implementation
@Override
public void onSuggestionsReceived(List<OmniboxSuggestion> suggestions,
String inlineAutocompleteText) {
if (!mUrlField.isFocused() || suggestions.isEmpty())
return;
mSuggestionsPopupItemsCount = suggestions.size();
if (mSuggestionsPopup == null) {
mSuggestionsPopup = new ListPopupWindow(
mContext, null, android.R.attr.autoCompleteTextViewStyle);
mSuggestionsPopup.setOnDismissListener(new OnDismissListener() {
@Override
public void onDismiss() {
mHasStartedNewOmniboxEditSession = false;
mSuggestionArrayAdapter = null;
}
});
}
mSuggestionsPopup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NEEDED);
mSuggestionsPopup.setWidth(mToolbar.getWidth());
mSuggestionArrayAdapter =
new SuggestionArrayAdapter(mContext, R.layout.dropdown_item, suggestions,
mUrlField);
mSuggestionsPopup.setHeight(getSuggestionPopupHeight());
mSuggestionsPopup.setAdapter(mSuggestionArrayAdapter);
mSuggestionsPopup.setAnchorView(mToolbar);
mSuggestionsPopup.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
navigateToSuggestion(position);
}
});
mSuggestionsPopup.show();
}
// TextWatcher implementation
@Override
public void afterTextChanged(final Editable editableText) {
if (!mUrlField.hasFocus()) return;
if (!mHasStartedNewOmniboxEditSession) {
mAutocomplete.resetSession();
mHasStartedNewOmniboxEditSession = true;
}
stopAutocomplete(false);
if (TextUtils.isEmpty(editableText)) {
dismissPopup();
} else {
assert mRequestSuggestions == null : "Multiple omnibox requests in flight.";
mRequestSuggestions = new Runnable() {
@Override
public void run() {
// TODO(aurimas): Create new tab if none exists.
if (mToolbar.getCurrentTab() == null) return;
mRequestSuggestions = null;
mAutocomplete.start(
mToolbar.getCurrentTab().getProfile(),
mToolbar.getCurrentTab().getUrl(),
editableText.toString(), false);
}
};
new Handler().postDelayed(mRequestSuggestions, SUGGESTION_START_DELAY_MS);
}
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
mRequestSuggestions = null;
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
}