Skip to content

Commit c4ffe77

Browse files
George MountAndroid (Google) Code Review
authored andcommitted
Merge "Add back auto-completion and auto-fill."
2 parents d85d590 + bcd5dd7 commit c4ffe77

File tree

3 files changed

+472
-16
lines changed

3 files changed

+472
-16
lines changed
Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
/*
2+
* Copyright (C) 2012 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package android.webkit;
17+
18+
import android.content.Context;
19+
import android.graphics.Rect;
20+
import android.os.Handler;
21+
import android.os.Message;
22+
import android.text.Editable;
23+
import android.util.Log;
24+
import android.view.KeyEvent;
25+
import android.view.View;
26+
import android.widget.AbsoluteLayout;
27+
import android.widget.AdapterView;
28+
import android.widget.AdapterView.OnItemClickListener;
29+
import android.widget.Filter;
30+
import android.widget.Filterable;
31+
import android.widget.ListAdapter;
32+
import android.widget.ListPopupWindow;
33+
34+
class AutoCompletePopup implements OnItemClickListener, Filter.FilterListener {
35+
private static class AnchorView extends View {
36+
AnchorView(Context context) {
37+
super(context);
38+
setFocusable(false);
39+
}
40+
}
41+
private static final int AUTOFILL_FORM = 100;
42+
private boolean mIsAutoFillProfileSet;
43+
private Handler mHandler;
44+
private int mQueryId;
45+
private Rect mNodeBounds = new Rect();
46+
private int mNodeLayerId;
47+
private ListPopupWindow mPopup;
48+
private Filter mFilter;
49+
private CharSequence mText;
50+
private ListAdapter mAdapter;
51+
private boolean mIsFocused;
52+
private View mAnchor;
53+
private WebViewClassic.WebViewInputConnection mInputConnection;
54+
private WebViewClassic mWebView;
55+
56+
public AutoCompletePopup(Context context,
57+
WebViewClassic webView,
58+
WebViewClassic.WebViewInputConnection inputConnection) {
59+
mInputConnection = inputConnection;
60+
mWebView = webView;
61+
mPopup = new ListPopupWindow(context);
62+
mAnchor = new AnchorView(context);
63+
mWebView.getWebView().addView(mAnchor);
64+
mPopup.setOnItemClickListener(this);
65+
mPopup.setAnchorView(mAnchor);
66+
mPopup.setPromptPosition(ListPopupWindow.POSITION_PROMPT_BELOW);
67+
mHandler = new Handler() {
68+
@Override
69+
public void handleMessage(Message msg) {
70+
switch (msg.what) {
71+
case AUTOFILL_FORM:
72+
mWebView.autoFillForm(mQueryId);
73+
break;
74+
}
75+
}
76+
};
77+
}
78+
79+
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
80+
if (keyCode == KeyEvent.KEYCODE_BACK && mPopup.isShowing()) {
81+
// special case for the back key, we do not even try to send it
82+
// to the drop down list but instead, consume it immediately
83+
if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
84+
KeyEvent.DispatcherState state = mAnchor.getKeyDispatcherState();
85+
if (state != null) {
86+
state.startTracking(event, this);
87+
}
88+
return true;
89+
} else if (event.getAction() == KeyEvent.ACTION_UP) {
90+
KeyEvent.DispatcherState state = mAnchor.getKeyDispatcherState();
91+
if (state != null) {
92+
state.handleUpEvent(event);
93+
}
94+
if (event.isTracking() && !event.isCanceled()) {
95+
Log.v("AutoCompletePopup", "dismiss popup 2");
96+
mPopup.dismiss();
97+
return true;
98+
}
99+
}
100+
}
101+
if (mPopup.isShowing()) {
102+
return mPopup.onKeyPreIme(keyCode, event);
103+
}
104+
return false;
105+
}
106+
107+
public void setFocused(boolean isFocused) {
108+
mIsFocused = isFocused;
109+
if (!mIsFocused) {
110+
Log.v("AutoCompletePopup", "dismiss popup 3");
111+
mPopup.dismiss();
112+
}
113+
}
114+
115+
public void setText(CharSequence text) {
116+
mText = text;
117+
if (mFilter != null) {
118+
mFilter.filter(text, this);
119+
}
120+
}
121+
122+
public void setAutoFillQueryId(int queryId) {
123+
mQueryId = queryId;
124+
}
125+
126+
public void clearAdapter() {
127+
mAdapter = null;
128+
mFilter = null;
129+
Log.v("AutoCompletePopup", "dismiss popup 4");
130+
mPopup.dismiss();
131+
mPopup.setAdapter(null);
132+
}
133+
134+
public <T extends ListAdapter & Filterable> void setAdapter(T adapter) {
135+
mPopup.setAdapter(adapter);
136+
mAdapter = adapter;
137+
if (adapter != null) {
138+
mFilter = adapter.getFilter();
139+
mFilter.filter(mText, this);
140+
} else {
141+
mFilter = null;
142+
}
143+
resetRect();
144+
}
145+
146+
public void setNodeBounds(Rect nodeBounds, int layerId) {
147+
mNodeBounds.set(nodeBounds);
148+
mNodeLayerId = layerId;
149+
resetRect();
150+
}
151+
152+
public void resetRect() {
153+
Log.v("AutoCompletePopup", "resetRect: " + mNodeBounds);
154+
int left = mWebView.contentToViewX(mNodeBounds.left);
155+
int right = mWebView.contentToViewX(mNodeBounds.right);
156+
int width = right - left;
157+
mPopup.setWidth(width);
158+
159+
int bottom = mWebView.contentToViewY(mNodeBounds.bottom);
160+
int top = mWebView.contentToViewY(mNodeBounds.top);
161+
int height = bottom - top;
162+
163+
AbsoluteLayout.LayoutParams lp =
164+
(AbsoluteLayout.LayoutParams) mAnchor.getLayoutParams();
165+
boolean needsUpdate = false;
166+
if (null == lp) {
167+
lp = new AbsoluteLayout.LayoutParams(width, height, left, top);
168+
} else {
169+
if ((lp.x != left) || (lp.y != top) || (lp.width != width)
170+
|| (lp.height != height)) {
171+
needsUpdate = true;
172+
lp.x = left;
173+
lp.y = top;
174+
lp.width = width;
175+
lp.height = height;
176+
}
177+
}
178+
Log.v("AutoCompletePopup", "resetRect layout " + lp.x + ", " + lp.y +
179+
", " + lp.width + ", " + lp.height);
180+
if (needsUpdate) {
181+
mAnchor.setLayoutParams(lp);
182+
}
183+
if (mPopup.isShowing()) {
184+
Log.v("AutoCompletePopup", "showing popup again");
185+
mPopup.show(); // update its position
186+
}
187+
}
188+
189+
public void scrollDelta(int layerId, int dx, int dy) {
190+
if (layerId == mNodeLayerId) {
191+
mNodeBounds.offset(dx, dy);
192+
resetRect();
193+
}
194+
}
195+
196+
// AdapterView.OnItemClickListener implementation
197+
@Override
198+
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
199+
if (id == 0 && position == 0 && mInputConnection.getIsAutoFillable()) {
200+
mText = "";
201+
pushTextToInputConnection();
202+
// Blank out the text box while we wait for WebCore to fill the form.
203+
if (mIsAutoFillProfileSet) {
204+
// Call a webview method to tell WebCore to autofill the form.
205+
mWebView.autoFillForm(mQueryId);
206+
} else {
207+
// There is no autofill profile setup yet and the user has
208+
// elected to try and set one up. Call through to the
209+
// embedder to action that.
210+
mWebView.getWebChromeClient().setupAutoFill(
211+
mHandler.obtainMessage(AUTOFILL_FORM));
212+
}
213+
} else {
214+
Object selectedItem;
215+
if (position < 0) {
216+
selectedItem = mPopup.getSelectedItem();
217+
} else {
218+
selectedItem = mAdapter.getItem(position);
219+
}
220+
if (selectedItem != null) {
221+
setText(mFilter.convertResultToString(selectedItem));
222+
pushTextToInputConnection();
223+
}
224+
}
225+
Log.v("AutoCompletePopup", "dismiss popup 5");
226+
mPopup.dismiss();
227+
}
228+
229+
public void setIsAutoFillProfileSet(boolean isAutoFillProfileSet) {
230+
mIsAutoFillProfileSet = isAutoFillProfileSet;
231+
}
232+
233+
private void pushTextToInputConnection() {
234+
Editable oldText = mInputConnection.getEditable();
235+
mInputConnection.setSelection(0, oldText.length());
236+
mInputConnection.replaceSelection(mText);
237+
mInputConnection.setSelection(mText.length(), mText.length());
238+
}
239+
240+
@Override
241+
public void onFilterComplete(int count) {
242+
if (!mIsFocused) {
243+
Log.v("AutoCompletePopup", "dismiss popup 1");
244+
mPopup.dismiss();
245+
return;
246+
}
247+
248+
boolean showDropDown = (count > 0) &&
249+
(mInputConnection.getIsAutoFillable() || mText.length() > 0);
250+
if (showDropDown) {
251+
if (!mPopup.isShowing()) {
252+
// Make sure the list does not obscure the IME when shown for the first time.
253+
mPopup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NEEDED);
254+
}
255+
Log.v("AutoCompletePopup", "showing popup");
256+
mPopup.show();
257+
mPopup.getListView().setOverScrollMode(View.OVER_SCROLL_ALWAYS);
258+
} else {
259+
Log.v("AutoCompletePopup", "dismiss popup");
260+
mPopup.dismiss();
261+
}
262+
}
263+
}
264+

0 commit comments

Comments
 (0)