• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.os.Handler;
20 import android.os.Message;
21 import android.text.Editable;
22 import android.view.KeyEvent;
23 import android.view.View;
24 import android.widget.AbsoluteLayout;
25 import android.widget.AdapterView;
26 import android.widget.AdapterView.OnItemClickListener;
27 import android.widget.Filter;
28 import android.widget.Filterable;
29 import android.widget.ListAdapter;
30 import android.widget.ListPopupWindow;
31 import android.widget.PopupWindow.OnDismissListener;
32 
33 class AutoCompletePopup implements OnItemClickListener, Filter.FilterListener,
34         OnDismissListener{
35     private static class AnchorView extends View {
AnchorView(Context context)36         AnchorView(Context context) {
37             super(context);
38             setFocusable(false);
39             setVisibility(INVISIBLE);
40         }
41     }
42     private static final int AUTOFILL_FORM = 100;
43     private boolean mIsAutoFillProfileSet;
44     private Handler mHandler;
45     private int mQueryId;
46     private ListPopupWindow mPopup;
47     private Filter mFilter;
48     private CharSequence mText;
49     private ListAdapter mAdapter;
50     private View mAnchor;
51     private WebViewClassic.WebViewInputConnection mInputConnection;
52     private WebViewClassic mWebView;
53 
AutoCompletePopup(WebViewClassic webView, WebViewClassic.WebViewInputConnection inputConnection)54     public AutoCompletePopup(WebViewClassic webView,
55             WebViewClassic.WebViewInputConnection inputConnection) {
56         mInputConnection = inputConnection;
57         mWebView = webView;
58         mHandler = new Handler() {
59             @Override
60             public void handleMessage(Message msg) {
61                 switch (msg.what) {
62                 case AUTOFILL_FORM:
63                     mWebView.autoFillForm(mQueryId);
64                     break;
65                 }
66             }
67         };
68     }
69 
onKeyPreIme(int keyCode, KeyEvent event)70     public boolean onKeyPreIme(int keyCode, KeyEvent event) {
71         if (mPopup == null) {
72             return false;
73         }
74         if (keyCode == KeyEvent.KEYCODE_BACK && mPopup.isShowing()) {
75             // special case for the back key, we do not even try to send it
76             // to the drop down list but instead, consume it immediately
77             if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
78                 KeyEvent.DispatcherState state = mAnchor.getKeyDispatcherState();
79                 if (state != null) {
80                     state.startTracking(event, this);
81                 }
82                 return true;
83             } else if (event.getAction() == KeyEvent.ACTION_UP) {
84                 KeyEvent.DispatcherState state = mAnchor.getKeyDispatcherState();
85                 if (state != null) {
86                     state.handleUpEvent(event);
87                 }
88                 if (event.isTracking() && !event.isCanceled()) {
89                     mPopup.dismiss();
90                     return true;
91                 }
92             }
93         }
94         if (mPopup.isShowing()) {
95             return mPopup.onKeyPreIme(keyCode, event);
96         }
97         return false;
98     }
99 
setText(CharSequence text)100     public void setText(CharSequence text) {
101         mText = text;
102         if (mFilter != null) {
103             mFilter.filter(text, this);
104         }
105     }
106 
setAutoFillQueryId(int queryId)107     public void setAutoFillQueryId(int queryId) {
108         mQueryId = queryId;
109     }
110 
clearAdapter()111     public void clearAdapter() {
112         mAdapter = null;
113         mFilter = null;
114         if (mPopup != null) {
115             mPopup.dismiss();
116             mPopup.setAdapter(null);
117         }
118     }
119 
setAdapter(T adapter)120     public <T extends ListAdapter & Filterable> void setAdapter(T adapter) {
121         ensurePopup();
122         mPopup.setAdapter(adapter);
123         mAdapter = adapter;
124         if (adapter != null) {
125             mFilter = adapter.getFilter();
126             mFilter.filter(mText, this);
127         } else {
128             mFilter = null;
129         }
130         resetRect();
131     }
132 
resetRect()133     public void resetRect() {
134         ensurePopup();
135         int left = mWebView.contentToViewX(mWebView.mEditTextContentBounds.left);
136         int right = mWebView.contentToViewX(mWebView.mEditTextContentBounds.right);
137         int width = right - left;
138         mPopup.setWidth(width);
139 
140         int bottom = mWebView.contentToViewY(mWebView.mEditTextContentBounds.bottom);
141         int top = mWebView.contentToViewY(mWebView.mEditTextContentBounds.top);
142         int height = bottom - top;
143 
144         AbsoluteLayout.LayoutParams lp =
145                 (AbsoluteLayout.LayoutParams) mAnchor.getLayoutParams();
146         boolean needsUpdate = false;
147         if (null == lp) {
148             lp = new AbsoluteLayout.LayoutParams(width, height, left, top);
149         } else {
150             if ((lp.x != left) || (lp.y != top) || (lp.width != width)
151                     || (lp.height != height)) {
152                 needsUpdate = true;
153                 lp.x = left;
154                 lp.y = top;
155                 lp.width = width;
156                 lp.height = height;
157             }
158         }
159         if (needsUpdate) {
160             mAnchor.setLayoutParams(lp);
161         }
162         if (mPopup.isShowing()) {
163             mPopup.show(); // update its position
164         }
165     }
166 
167     // AdapterView.OnItemClickListener implementation
168     @Override
onItemClick(AdapterView<?> parent, View view, int position, long id)169     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
170         if (mPopup == null) {
171             return;
172         }
173         if (id == 0 && position == 0 && mInputConnection.getIsAutoFillable()) {
174             mText = "";
175             pushTextToInputConnection();
176             // Blank out the text box while we wait for WebCore to fill the form.
177             if (mIsAutoFillProfileSet) {
178                 // Call a webview method to tell WebCore to autofill the form.
179                 mWebView.autoFillForm(mQueryId);
180             } else {
181                 // There is no autofill profile setup yet and the user has
182                 // elected to try and set one up. Call through to the
183                 // embedder to action that.
184                 WebChromeClient webChromeClient = mWebView.getWebChromeClient();
185                 if (webChromeClient != null) {
186                     webChromeClient.setupAutoFill(
187                         mHandler.obtainMessage(AUTOFILL_FORM));
188                 }
189             }
190         } else {
191             Object selectedItem;
192             if (position < 0) {
193                 selectedItem = mPopup.getSelectedItem();
194             } else {
195                 selectedItem = mAdapter.getItem(position);
196             }
197             if (selectedItem != null) {
198                 setText(mFilter.convertResultToString(selectedItem));
199                 pushTextToInputConnection();
200             }
201         }
202         mPopup.dismiss();
203     }
204 
setIsAutoFillProfileSet(boolean isAutoFillProfileSet)205     public void setIsAutoFillProfileSet(boolean isAutoFillProfileSet) {
206         mIsAutoFillProfileSet = isAutoFillProfileSet;
207     }
208 
pushTextToInputConnection()209     private void pushTextToInputConnection() {
210         Editable oldText = mInputConnection.getEditable();
211         mInputConnection.setSelection(0, oldText.length());
212         mInputConnection.replaceSelection(mText);
213         mInputConnection.setSelection(mText.length(), mText.length());
214     }
215 
216     @Override
onFilterComplete(int count)217     public void onFilterComplete(int count) {
218         ensurePopup();
219         boolean showDropDown = (count > 0) &&
220                 (mInputConnection.getIsAutoFillable() || mText.length() > 0);
221         if (showDropDown) {
222             if (!mPopup.isShowing()) {
223                 // Make sure the list does not obscure the IME when shown for the first time.
224                 mPopup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NEEDED);
225             }
226             mPopup.show();
227             mPopup.getListView().setOverScrollMode(View.OVER_SCROLL_ALWAYS);
228         } else {
229             mPopup.dismiss();
230         }
231     }
232 
233     @Override
onDismiss()234     public void onDismiss() {
235         mWebView.getWebView().removeView(mAnchor);
236     }
237 
ensurePopup()238     private void ensurePopup() {
239         if (mPopup == null) {
240             mPopup = new ListPopupWindow(mWebView.getContext());
241             mAnchor = new AnchorView(mWebView.getContext());
242             mWebView.getWebView().addView(mAnchor);
243             mPopup.setOnItemClickListener(this);
244             mPopup.setAnchorView(mAnchor);
245             mPopup.setPromptPosition(ListPopupWindow.POSITION_PROMPT_BELOW);
246         } else if (mWebView.getWebView().indexOfChild(mAnchor) < 0) {
247             mWebView.getWebView().addView(mAnchor);
248         }
249     }
250 }
251 
252