diff options
| author | George Mount <mount@google.com> | 2012-03-01 08:39:03 -0800 |
|---|---|---|
| committer | George Mount <mount@google.com> | 2012-03-09 15:45:32 -0800 |
| commit | bcd5dd7c9a6d5fb5add4041dbd62d1670a69d526 (patch) | |
| tree | 6fae16f675551d1471c9319fab380c6a3c4c8f3f /core/java/android/webkit/AutoCompletePopup.java | |
| parent | 21b4d6b7a5959b567355aae032bd76500c148e54 (diff) | |
Add back auto-completion and auto-fill.
Bug 5993316
Webkit Change: I9d09a924a579d2d4551bff0489670de71d04757e
Change-Id: If00d42c0cb4f2c207f0d502e297741e1d6a68bf4
Diffstat (limited to 'core/java/android/webkit/AutoCompletePopup.java')
| -rw-r--r-- | core/java/android/webkit/AutoCompletePopup.java | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/core/java/android/webkit/AutoCompletePopup.java b/core/java/android/webkit/AutoCompletePopup.java new file mode 100644 index 000000000000..e0e5ecaf87b1 --- /dev/null +++ b/core/java/android/webkit/AutoCompletePopup.java @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.webkit; + +import android.content.Context; +import android.graphics.Rect; +import android.os.Handler; +import android.os.Message; +import android.text.Editable; +import android.util.Log; +import android.view.KeyEvent; +import android.view.View; +import android.widget.AbsoluteLayout; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.Filter; +import android.widget.Filterable; +import android.widget.ListAdapter; +import android.widget.ListPopupWindow; + +class AutoCompletePopup implements OnItemClickListener, Filter.FilterListener { + private static class AnchorView extends View { + AnchorView(Context context) { + super(context); + setFocusable(false); + } + } + private static final int AUTOFILL_FORM = 100; + private boolean mIsAutoFillProfileSet; + private Handler mHandler; + private int mQueryId; + private Rect mNodeBounds = new Rect(); + private int mNodeLayerId; + private ListPopupWindow mPopup; + private Filter mFilter; + private CharSequence mText; + private ListAdapter mAdapter; + private boolean mIsFocused; + private View mAnchor; + private WebViewClassic.WebViewInputConnection mInputConnection; + private WebViewClassic mWebView; + + public AutoCompletePopup(Context context, + WebViewClassic webView, + WebViewClassic.WebViewInputConnection inputConnection) { + mInputConnection = inputConnection; + mWebView = webView; + mPopup = new ListPopupWindow(context); + mAnchor = new AnchorView(context); + mWebView.getWebView().addView(mAnchor); + mPopup.setOnItemClickListener(this); + mPopup.setAnchorView(mAnchor); + mPopup.setPromptPosition(ListPopupWindow.POSITION_PROMPT_BELOW); + mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case AUTOFILL_FORM: + mWebView.autoFillForm(mQueryId); + break; + } + } + }; + } + + public boolean onKeyPreIme(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK && mPopup.isShowing()) { + // special case for the back key, we do not even try to send it + // to the drop down list but instead, consume it immediately + if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { + KeyEvent.DispatcherState state = mAnchor.getKeyDispatcherState(); + if (state != null) { + state.startTracking(event, this); + } + return true; + } else if (event.getAction() == KeyEvent.ACTION_UP) { + KeyEvent.DispatcherState state = mAnchor.getKeyDispatcherState(); + if (state != null) { + state.handleUpEvent(event); + } + if (event.isTracking() && !event.isCanceled()) { + Log.v("AutoCompletePopup", "dismiss popup 2"); + mPopup.dismiss(); + return true; + } + } + } + if (mPopup.isShowing()) { + return mPopup.onKeyPreIme(keyCode, event); + } + return false; + } + + public void setFocused(boolean isFocused) { + mIsFocused = isFocused; + if (!mIsFocused) { + Log.v("AutoCompletePopup", "dismiss popup 3"); + mPopup.dismiss(); + } + } + + public void setText(CharSequence text) { + mText = text; + if (mFilter != null) { + mFilter.filter(text, this); + } + } + + public void setAutoFillQueryId(int queryId) { + mQueryId = queryId; + } + + public void clearAdapter() { + mAdapter = null; + mFilter = null; + Log.v("AutoCompletePopup", "dismiss popup 4"); + mPopup.dismiss(); + mPopup.setAdapter(null); + } + + public <T extends ListAdapter & Filterable> void setAdapter(T adapter) { + mPopup.setAdapter(adapter); + mAdapter = adapter; + if (adapter != null) { + mFilter = adapter.getFilter(); + mFilter.filter(mText, this); + } else { + mFilter = null; + } + resetRect(); + } + + public void setNodeBounds(Rect nodeBounds, int layerId) { + mNodeBounds.set(nodeBounds); + mNodeLayerId = layerId; + resetRect(); + } + + public void resetRect() { + Log.v("AutoCompletePopup", "resetRect: " + mNodeBounds); + int left = mWebView.contentToViewX(mNodeBounds.left); + int right = mWebView.contentToViewX(mNodeBounds.right); + int width = right - left; + mPopup.setWidth(width); + + int bottom = mWebView.contentToViewY(mNodeBounds.bottom); + int top = mWebView.contentToViewY(mNodeBounds.top); + int height = bottom - top; + + AbsoluteLayout.LayoutParams lp = + (AbsoluteLayout.LayoutParams) mAnchor.getLayoutParams(); + boolean needsUpdate = false; + if (null == lp) { + lp = new AbsoluteLayout.LayoutParams(width, height, left, top); + } else { + if ((lp.x != left) || (lp.y != top) || (lp.width != width) + || (lp.height != height)) { + needsUpdate = true; + lp.x = left; + lp.y = top; + lp.width = width; + lp.height = height; + } + } + Log.v("AutoCompletePopup", "resetRect layout " + lp.x + ", " + lp.y + + ", " + lp.width + ", " + lp.height); + if (needsUpdate) { + mAnchor.setLayoutParams(lp); + } + if (mPopup.isShowing()) { + Log.v("AutoCompletePopup", "showing popup again"); + mPopup.show(); // update its position + } + } + + public void scrollDelta(int layerId, int dx, int dy) { + if (layerId == mNodeLayerId) { + mNodeBounds.offset(dx, dy); + resetRect(); + } + } + + // AdapterView.OnItemClickListener implementation + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + if (id == 0 && position == 0 && mInputConnection.getIsAutoFillable()) { + mText = ""; + pushTextToInputConnection(); + // Blank out the text box while we wait for WebCore to fill the form. + if (mIsAutoFillProfileSet) { + // Call a webview method to tell WebCore to autofill the form. + mWebView.autoFillForm(mQueryId); + } else { + // There is no autofill profile setup yet and the user has + // elected to try and set one up. Call through to the + // embedder to action that. + mWebView.getWebChromeClient().setupAutoFill( + mHandler.obtainMessage(AUTOFILL_FORM)); + } + } else { + Object selectedItem; + if (position < 0) { + selectedItem = mPopup.getSelectedItem(); + } else { + selectedItem = mAdapter.getItem(position); + } + if (selectedItem != null) { + setText(mFilter.convertResultToString(selectedItem)); + pushTextToInputConnection(); + } + } + Log.v("AutoCompletePopup", "dismiss popup 5"); + mPopup.dismiss(); + } + + public void setIsAutoFillProfileSet(boolean isAutoFillProfileSet) { + mIsAutoFillProfileSet = isAutoFillProfileSet; + } + + private void pushTextToInputConnection() { + Editable oldText = mInputConnection.getEditable(); + mInputConnection.setSelection(0, oldText.length()); + mInputConnection.replaceSelection(mText); + mInputConnection.setSelection(mText.length(), mText.length()); + } + + @Override + public void onFilterComplete(int count) { + if (!mIsFocused) { + Log.v("AutoCompletePopup", "dismiss popup 1"); + mPopup.dismiss(); + return; + } + + boolean showDropDown = (count > 0) && + (mInputConnection.getIsAutoFillable() || mText.length() > 0); + if (showDropDown) { + if (!mPopup.isShowing()) { + // Make sure the list does not obscure the IME when shown for the first time. + mPopup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NEEDED); + } + Log.v("AutoCompletePopup", "showing popup"); + mPopup.show(); + mPopup.getListView().setOverScrollMode(View.OVER_SCROLL_ALWAYS); + } else { + Log.v("AutoCompletePopup", "dismiss popup"); + mPopup.dismiss(); + } + } +} + |
