blob: 8a70b6d7a8682a2bf47b40c1b62b7ffa144145cc [file] [log] [blame]
Michael Kolbfe251992010-07-08 15:41:55 -07001/*
2 * Copyright (C) 2010 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
Bijan Amirzada41242f22014-03-21 12:12:18 -070017package com.android.browser;
Michael Kolbfe251992010-07-08 15:41:55 -070018
Michael Kolbfe251992010-07-08 15:41:55 -070019import android.content.Context;
Michael Kolb21ce4d22010-09-15 14:55:05 -070020import android.content.res.Configuration;
Narayan Kamath5119edd2011-02-23 15:49:17 +000021import android.graphics.Rect;
John Reck8d021aa2011-09-06 15:30:11 -070022import android.graphics.drawable.Drawable;
Narayan Kamathf3174a52011-11-17 14:43:32 +000023import android.text.Editable;
Axesh R. Ajmera9bb763e2014-04-04 15:37:34 -070024import android.text.InputFilter;
25import android.text.InputFilter.LengthFilter;
26import android.text.Spanned;
John Reckdcda1d52010-11-29 10:05:54 -080027import android.text.TextUtils;
Narayan Kamathf3174a52011-11-17 14:43:32 +000028import android.text.TextWatcher;
Michael Kolbfe251992010-07-08 15:41:55 -070029import android.util.AttributeSet;
John Reckb77bdc42011-01-24 13:24:48 -080030import android.util.Patterns;
Axesh R. Ajmera9bb763e2014-04-04 15:37:34 -070031import android.view.Gravity;
Michael Kolbfe251992010-07-08 15:41:55 -070032import android.view.KeyEvent;
Michael Kolb305b1c52011-06-21 16:16:22 -070033import android.view.MotionEvent;
Michael Kolbfe251992010-07-08 15:41:55 -070034import android.view.View;
Michael Kolbfe251992010-07-08 15:41:55 -070035import android.view.inputmethod.InputMethodManager;
John Reck87369ea2011-01-23 15:20:38 -080036import android.widget.AdapterView;
37import android.widget.AdapterView.OnItemClickListener;
Narayan Kamathf3174a52011-11-17 14:43:32 +000038import android.widget.AutoCompleteTextView;
Michael Kolbfe251992010-07-08 15:41:55 -070039import android.widget.TextView;
Michael Kolbed217742010-08-10 17:52:34 -070040import android.widget.TextView.OnEditorActionListener;
Axesh R. Ajmera9bb763e2014-04-04 15:37:34 -070041import android.widget.Toast;
Michael Kolbfe251992010-07-08 15:41:55 -070042
Bijan Amirzada41242f22014-03-21 12:12:18 -070043import com.android.browser.SuggestionsAdapter.CompletionListener;
44import com.android.browser.SuggestionsAdapter.SuggestItem;
45import com.android.browser.reflect.ReflectHelper;
46import com.android.browser.search.SearchEngine;
47import com.android.browser.search.SearchEngineInfo;
48import com.android.browser.search.SearchEngines;
Michael Kolb305b1c52011-06-21 16:16:22 -070049
Michael Kolbfe251992010-07-08 15:41:55 -070050/**
51 * url/search input view
52 * handling suggestions
53 */
Narayan Kamathf3174a52011-11-17 14:43:32 +000054public class UrlInputView extends AutoCompleteTextView
Michael Kolbdc2ee1b2011-02-14 14:34:40 -080055 implements OnEditorActionListener,
Narayan Kamathf3174a52011-11-17 14:43:32 +000056 CompletionListener, OnItemClickListener, TextWatcher {
Michael Kolb257cc2c2010-12-09 09:45:52 -080057
58 static final String TYPED = "browser-type";
59 static final String SUGGESTED = "browser-suggest";
Tarun Nainanid5306732014-04-28 19:38:28 -070060 static final String LATIN_INPUTMETHOD_PACKAGE_NAME = "com.android.inputmethod.latin";
Michael Kolb257cc2c2010-12-09 09:45:52 -080061
Michael Kolb94ec5272011-08-31 16:23:57 -070062 static final int POST_DELAY = 100;
Axesh R. Ajmera9bb763e2014-04-04 15:37:34 -070063 static final int URL_MAX_LENGTH = 2048;
Michael Kolb94ec5272011-08-31 16:23:57 -070064
Michael Kolb305b1c52011-06-21 16:16:22 -070065 static interface StateListener {
66 static final int STATE_NORMAL = 0;
67 static final int STATE_HIGHLIGHTED = 1;
68 static final int STATE_EDITED = 2;
69
70 public void onStateChanged(int state);
71 }
72
Michael Kolbfe251992010-07-08 15:41:55 -070073 private UrlInputListener mListener;
74 private InputMethodManager mInputManager;
75 private SuggestionsAdapter mAdapter;
Michael Kolb21ce4d22010-09-15 14:55:05 -070076 private View mContainer;
77 private boolean mLandscape;
John Reckb77bdc42011-01-24 13:24:48 -080078 private boolean mIncognitoMode;
Michael Kolbdc2ee1b2011-02-14 14:34:40 -080079 private boolean mNeedsUpdate;
Axesh R. Ajmera9bb763e2014-04-04 15:37:34 -070080 private Context mContext;
Michael Kolbfe251992010-07-08 15:41:55 -070081
Michael Kolb305b1c52011-06-21 16:16:22 -070082 private int mState;
83 private StateListener mStateListener;
84
Michael Kolbfe251992010-07-08 15:41:55 -070085 public UrlInputView(Context context, AttributeSet attrs, int defStyle) {
86 super(context, attrs, defStyle);
87 init(context);
88 }
89
90 public UrlInputView(Context context, AttributeSet attrs) {
Bijan Amirzada9b1e9882014-02-26 17:15:46 -080091 // SWE_TODO : Needs Fix
92 //this(context, attrs, R.attr.autoCompleteTextViewStyle);
93 this(context, attrs, 0);
Michael Kolbfe251992010-07-08 15:41:55 -070094 }
95
96 public UrlInputView(Context context) {
John Reck8d021aa2011-09-06 15:30:11 -070097 this(context, null);
Michael Kolbfe251992010-07-08 15:41:55 -070098 }
99
100 private void init(Context ctx) {
Michael Kolbfe251992010-07-08 15:41:55 -0700101 mInputManager = (InputMethodManager) ctx.getSystemService(Context.INPUT_METHOD_SERVICE);
Michael Kolbed217742010-08-10 17:52:34 -0700102 setOnEditorActionListener(this);
Michael Kolb21ce4d22010-09-15 14:55:05 -0700103 mAdapter = new SuggestionsAdapter(ctx, this);
Michael Kolbfe251992010-07-08 15:41:55 -0700104 setAdapter(mAdapter);
Michael Kolbba99c5d2010-11-29 14:57:41 -0800105 setSelectAllOnFocus(true);
Michael Kolb21ce4d22010-09-15 14:55:05 -0700106 onConfigurationChanged(ctx.getResources().getConfiguration());
John Reck35defff2010-11-11 14:06:45 -0800107 setThreshold(1);
John Reck87369ea2011-01-23 15:20:38 -0800108 setOnItemClickListener(this);
Michael Kolbdc2ee1b2011-02-14 14:34:40 -0800109 mNeedsUpdate = false;
Narayan Kamathf3174a52011-11-17 14:43:32 +0000110 addTextChangedListener(this);
Sungmann Cho7a9471b2014-02-27 20:45:20 +0900111 setDropDownAnchor(R.id.taburlbar);
Michael Kolb305b1c52011-06-21 16:16:22 -0700112 mState = StateListener.STATE_NORMAL;
Axesh R. Ajmera9bb763e2014-04-04 15:37:34 -0700113 mContext = ctx;
114
115 this.setFilters(new InputFilter[] {
116 new UrlLengthFilter(URL_MAX_LENGTH)
117 });
Michael Kolb305b1c52011-06-21 16:16:22 -0700118 }
119
120 protected void onFocusChanged(boolean focused, int direction, Rect prevRect) {
121 super.onFocusChanged(focused, direction, prevRect);
Michael Kolb94ec5272011-08-31 16:23:57 -0700122 int state = -1;
Michael Kolb305b1c52011-06-21 16:16:22 -0700123 if (focused) {
124 if (hasSelection()) {
Michael Kolb94ec5272011-08-31 16:23:57 -0700125 state = StateListener.STATE_HIGHLIGHTED;
Michael Kolb305b1c52011-06-21 16:16:22 -0700126 } else {
Michael Kolb94ec5272011-08-31 16:23:57 -0700127 state = StateListener.STATE_EDITED;
Michael Kolb305b1c52011-06-21 16:16:22 -0700128 }
129 } else {
130 // reset the selection state
Michael Kolb94ec5272011-08-31 16:23:57 -0700131 state = StateListener.STATE_NORMAL;
Michael Kolb305b1c52011-06-21 16:16:22 -0700132 }
Michael Kolb94ec5272011-08-31 16:23:57 -0700133 final int s = state;
Vivek Sekhar8ee3abb2014-07-14 12:32:05 -0700134 post(new Runnable() {
Michael Kolb94ec5272011-08-31 16:23:57 -0700135 public void run() {
136 changeState(s);
137 }
Vivek Sekhar8ee3abb2014-07-14 12:32:05 -0700138 });
Michael Kolb305b1c52011-06-21 16:16:22 -0700139 }
140
141 @Override
142 public boolean onTouchEvent(MotionEvent evt) {
143 boolean hasSelection = hasSelection();
144 boolean res = super.onTouchEvent(evt);
145 if ((MotionEvent.ACTION_DOWN == evt.getActionMasked())
146 && hasSelection) {
Michael Kolb94ec5272011-08-31 16:23:57 -0700147 postDelayed(new Runnable() {
148 public void run() {
149 changeState(StateListener.STATE_EDITED);
150 }}, POST_DELAY);
Michael Kolb305b1c52011-06-21 16:16:22 -0700151 }
152 return res;
Michael Kolbdc2ee1b2011-02-14 14:34:40 -0800153 }
154
155 /**
156 * check if focus change requires a title bar update
157 */
158 boolean needsUpdate() {
159 return mNeedsUpdate;
160 }
161
162 /**
163 * clear the focus change needs title bar update flag
164 */
165 void clearNeedsUpdate() {
166 mNeedsUpdate = false;
Michael Kolb21ce4d22010-09-15 14:55:05 -0700167 }
168
Michael Kolbba99c5d2010-11-29 14:57:41 -0800169 void setController(UiController controller) {
170 UrlSelectionActionMode urlSelectionMode
171 = new UrlSelectionActionMode(controller);
172 setCustomSelectionActionModeCallback(urlSelectionMode);
173 }
174
Michael Kolb21ce4d22010-09-15 14:55:05 -0700175 void setContainer(View container) {
176 mContainer = container;
177 }
178
Michael Kolbdc2ee1b2011-02-14 14:34:40 -0800179 public void setUrlInputListener(UrlInputListener listener) {
180 mListener = listener;
181 }
182
Michael Kolb305b1c52011-06-21 16:16:22 -0700183 public void setStateListener(StateListener listener) {
184 mStateListener = listener;
185 // update listener
186 changeState(mState);
187 }
188
189 private void changeState(int newState) {
190 mState = newState;
191 if (mStateListener != null) {
192 mStateListener.onStateChanged(mState);
193 }
194 }
195
Michael Kolb5e8f2b92011-07-19 13:22:32 -0700196 int getState() {
197 return mState;
198 }
199
Michael Kolb21ce4d22010-09-15 14:55:05 -0700200 @Override
201 protected void onConfigurationChanged(Configuration config) {
John Reck35defff2010-11-11 14:06:45 -0800202 super.onConfigurationChanged(config);
Michael Kolb21ce4d22010-09-15 14:55:05 -0700203 mLandscape = (config.orientation &
Michael Kolb31d469b2010-12-09 20:49:54 -0800204 Configuration.ORIENTATION_LANDSCAPE) != 0;
John Reck35defff2010-11-11 14:06:45 -0800205 mAdapter.setLandscapeMode(mLandscape);
Michael Kolb21ce4d22010-09-15 14:55:05 -0700206 if (isPopupShowing() && (getVisibility() == View.VISIBLE)) {
Sungmann Cho7a9471b2014-02-27 20:45:20 +0900207 dismissDropDown();
208 showDropDown();
Narayan Kamathf3174a52011-11-17 14:43:32 +0000209 performFiltering(getText(), 0);
Michael Kolb21ce4d22010-09-15 14:55:05 -0700210 }
211 }
212
213 @Override
John Reck35defff2010-11-11 14:06:45 -0800214 public void dismissDropDown() {
215 super.dismissDropDown();
216 mAdapter.clearCache();
217 }
218
Michael Kolb513286f2010-09-09 12:55:12 -0700219 @Override
Michael Kolbed217742010-08-10 17:52:34 -0700220 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
Narayan Kamathf3174a52011-11-17 14:43:32 +0000221 finishInput(getText().toString(), null, TYPED);
Michael Kolbed217742010-08-10 17:52:34 -0700222 return true;
223 }
Michael Kolb21ce4d22010-09-15 14:55:05 -0700224
Michael Kolbdc2ee1b2011-02-14 14:34:40 -0800225 void forceFilter() {
Michael Kolbdc2ee1b2011-02-14 14:34:40 -0800226 showDropDown();
Michael Kolba2b2ba82010-08-04 17:54:03 -0700227 }
228
Michael Kolbdc2ee1b2011-02-14 14:34:40 -0800229 void hideIME() {
230 mInputManager.hideSoftInputFromWindow(getWindowToken(), 0);
231 }
232
Michael Kolb4bb6fcb2012-04-13 14:25:27 -0700233 void showIME() {
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800234 //mInputManager.focusIn(this);
235 Object[] params = {this};
236 Class[] type = new Class[] {View.class};
237 ReflectHelper.invokeMethod(mInputManager, "focusIn", type, params);
Michael Kolb4bb6fcb2012-04-13 14:25:27 -0700238 mInputManager.showSoftInput(this, 0);
239 }
240
Michael Kolb257cc2c2010-12-09 09:45:52 -0800241 private void finishInput(String url, String extra, String source) {
Michael Kolbdc2ee1b2011-02-14 14:34:40 -0800242 mNeedsUpdate = true;
243 dismissDropDown();
Michael Kolbfe251992010-07-08 15:41:55 -0700244 mInputManager.hideSoftInputFromWindow(getWindowToken(), 0);
John Reckdcda1d52010-11-29 10:05:54 -0800245 if (TextUtils.isEmpty(url)) {
Michael Kolbfe251992010-07-08 15:41:55 -0700246 mListener.onDismiss();
247 } else {
John Reckb77bdc42011-01-24 13:24:48 -0800248 if (mIncognitoMode && isSearch(url)) {
249 // To prevent logging, intercept this request
250 // TODO: This is a quick hack, refactor this
251 SearchEngine searchEngine = BrowserSettings.getInstance()
252 .getSearchEngine();
253 if (searchEngine == null) return;
254 SearchEngineInfo engineInfo = SearchEngines
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800255 .getSearchEngineInfo(getContext(), searchEngine.getName());
John Reckb77bdc42011-01-24 13:24:48 -0800256 if (engineInfo == null) return;
257 url = engineInfo.getSearchUriForQuery(url);
258 // mLister.onAction can take it from here without logging
259 }
Michael Kolb257cc2c2010-12-09 09:45:52 -0800260 mListener.onAction(url, extra, source);
Michael Kolbfe251992010-07-08 15:41:55 -0700261 }
Michael Kolbfe251992010-07-08 15:41:55 -0700262 }
263
John Reckb77bdc42011-01-24 13:24:48 -0800264 boolean isSearch(String inUrl) {
265 String url = UrlUtils.fixUrl(inUrl).trim();
266 if (TextUtils.isEmpty(url)) return false;
267
268 if (Patterns.WEB_URL.matcher(url).matches()
269 || UrlUtils.ACCEPTED_URI_SCHEMA.matcher(url).matches()) {
270 return false;
271 }
272 return true;
273 }
274
Michael Kolb21ce4d22010-09-15 14:55:05 -0700275 // Completion Listener
276
277 @Override
278 public void onSearch(String search) {
Michael Kolb7cdc4902011-02-03 17:54:40 -0800279 mListener.onCopySuggestion(search);
Michael Kolb21ce4d22010-09-15 14:55:05 -0700280 }
281
282 @Override
Michael Kolbbd2dd642011-01-13 13:01:30 -0800283 public void onSelect(String url, int type, String extra) {
Michael Kolb5ff5c8b2012-05-03 11:37:58 -0700284 finishInput(url, extra, SUGGESTED);
Michael Kolb21ce4d22010-09-15 14:55:05 -0700285 }
286
Michael Kolbfe251992010-07-08 15:41:55 -0700287 @Override
John Reck87369ea2011-01-23 15:20:38 -0800288 public void onItemClick(
289 AdapterView<?> parent, View view, int position, long id) {
290 SuggestItem item = mAdapter.getItem(position);
Narayan Kamath5119edd2011-02-23 15:49:17 +0000291 onSelect(SuggestionsAdapter.getSuggestionUrl(item), item.type, item.extra);
John Reck87369ea2011-01-23 15:20:38 -0800292 }
293
Michael Kolbfe251992010-07-08 15:41:55 -0700294 interface UrlInputListener {
Michael Kolb21ce4d22010-09-15 14:55:05 -0700295
Michael Kolbfe251992010-07-08 15:41:55 -0700296 public void onDismiss();
Michael Kolb21ce4d22010-09-15 14:55:05 -0700297
Michael Kolb257cc2c2010-12-09 09:45:52 -0800298 public void onAction(String text, String extra, String source);
Michael Kolb21ce4d22010-09-15 14:55:05 -0700299
Michael Kolb7cdc4902011-02-03 17:54:40 -0800300 public void onCopySuggestion(String text);
Michael Kolbfe251992010-07-08 15:41:55 -0700301
302 }
303
John Reck117f07d2011-01-24 09:39:03 -0800304 public void setIncognitoMode(boolean incognito) {
John Reckb77bdc42011-01-24 13:24:48 -0800305 mIncognitoMode = incognito;
306 mAdapter.setIncognitoMode(mIncognitoMode);
John Reck117f07d2011-01-24 09:39:03 -0800307 }
308
Michael Kolbdc2ee1b2011-02-14 14:34:40 -0800309 @Override
310 public boolean onKeyDown(int keyCode, KeyEvent evt) {
311 if (keyCode == KeyEvent.KEYCODE_ESCAPE && !isInTouchMode()) {
312 finishInput(null, null, null);
313 return true;
314 }
315 return super.onKeyDown(keyCode, evt);
316 }
317
Narayan Kamath80aad8d2011-02-23 12:01:13 +0000318 public SuggestionsAdapter getAdapter() {
319 return mAdapter;
320 }
Narayan Kamath5119edd2011-02-23 15:49:17 +0000321
Michael Kolb74e60c22011-04-30 16:43:47 -0700322 /*
323 * no-op to prevent scrolling of webview when embedded titlebar
324 * gets edited
325 */
326 @Override
327 public boolean requestRectangleOnScreen(Rect rect, boolean immediate) {
328 return false;
329 }
Michael Kolb305b1c52011-06-21 16:16:22 -0700330
331 @Override
Narayan Kamathf3174a52011-11-17 14:43:32 +0000332 public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
333
334 @Override
335 public void onTextChanged(CharSequence s, int start, int before, int count) {
Sagar Dhawan84ef4442015-07-29 12:08:10 -0700336 if (StateListener.STATE_HIGHLIGHTED == mState ||
337 StateListener.STATE_EDITED == mState) {
Michael Kolb305b1c52011-06-21 16:16:22 -0700338 changeState(StateListener.STATE_EDITED);
339 }
340 }
341
Narayan Kamathf3174a52011-11-17 14:43:32 +0000342 @Override
343 public void afterTextChanged(Editable s) { }
344
Axesh R. Ajmera9bb763e2014-04-04 15:37:34 -0700345 /**
346 * It will prompt the toast if the text length greater than the given length.
347 */
348 private class UrlLengthFilter extends LengthFilter {
349 public UrlLengthFilter(int max) {
350 super(max);
351 }
352
353 @Override
354 public CharSequence filter(CharSequence source, int start, int end, Spanned dest,
355 int dstart, int dend) {
356 CharSequence result = super.filter(source, start, end, dest, dstart, dend);
357 if (result != null) {
358 // If the result is not null, it means the text length is greater than
359 // the given length. We will prompt the toast to alert the user.
360 CharSequence alert = getResources().getString(R.string.max_url_character_limit_msg);
361 Toast t = Toast.makeText(mContext , alert, Toast.LENGTH_SHORT);
362 t.setGravity(Gravity.CENTER, 0, 0);
363 t.show();
364 }
365 return result;
366 }
367 }
368
Michael Kolbfe251992010-07-08 15:41:55 -0700369}