summaryrefslogtreecommitdiff
path: root/core/java/android/webkit/AutoCompletePopup.java
diff options
context:
space:
mode:
authorGeorge Mount <mount@google.com>2012-03-01 08:39:03 -0800
committerGeorge Mount <mount@google.com>2012-03-09 15:45:32 -0800
commitbcd5dd7c9a6d5fb5add4041dbd62d1670a69d526 (patch)
tree6fae16f675551d1471c9319fab380c6a3c4c8f3f /core/java/android/webkit/AutoCompletePopup.java
parent21b4d6b7a5959b567355aae032bd76500c148e54 (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.java264
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();
+ }
+ }
+}
+