├── .gitignore ├── README.md ├── art └── logo.png ├── library ├── AndroidManifest.xml ├── libs │ ├── CWAC-MergeAdapter.jar │ ├── CWAC-SackOfViewsAdapter.jar │ ├── android-support-v4.jar │ ├── gson-2.2.2.jar │ └── resty-0.3.2.jar ├── pom.xml ├── proguard-project.txt ├── project.properties ├── res │ ├── color │ │ ├── het__primary_text_dark.xml │ │ └── het__primary_text_light.xml │ ├── drawable-hdpi │ │ └── het__ic_device_access_time.png │ ├── drawable-mdpi │ │ └── het__ic_device_access_time.png │ ├── drawable-xhdpi │ │ └── het__ic_device_access_time.png │ ├── layout │ │ ├── het__dropdown_history_item.xml │ │ ├── het__simple_dropdown_hint.xml │ │ └── het__simple_dropdown_item_1line.xml │ └── values │ │ ├── het__attrs.xml │ │ ├── het__colors.xml │ │ └── het__styles.xml └── src │ └── com │ └── zenlibs │ └── historyedittext │ ├── AbsHistoryEditText.java │ ├── FroyoListView.java │ ├── FroyoPopupWindow.java │ ├── HistoryDb.java │ ├── HistoryEditText.java │ └── Zen.java ├── pom.xml └── sample ├── AndroidManifest.xml ├── ic_launcher-web.png ├── libs └── android-support-v4.jar ├── pom.xml ├── proguard-project.txt ├── project.properties ├── res ├── drawable-hdpi │ └── ic_launcher.png ├── drawable-mdpi │ └── ic_launcher.png ├── drawable-xhdpi │ ├── ic_action_search.png │ ├── ic_content_import_export.png │ ├── ic_launcher.png │ └── ic_location_directions.png ├── drawable-xxhdpi │ └── ic_launcher.png ├── layout │ ├── activity_home.xml │ ├── activity_no_user_adapter.xml │ └── activity_simple.xml ├── menu │ └── activity_simple.xml ├── raw │ └── countries └── values │ ├── strings.xml │ └── styles.xml └── src └── com └── zenlibs └── historyedittext └── demo ├── ActivityInfo.java ├── HomeActivity.java ├── nouseradaptersample └── NoUserAdapterActivity.java └── simplehistorysample └── SimpleHistoryActivity.java /.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # files for the dex VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # generated files 12 | bin/ 13 | gen/ 14 | 15 | # Local configuration file (sdk path, etc) 16 | local.properties 17 | 18 | # Eclipse project files 19 | .classpath 20 | .project 21 | 22 | #Maven 23 | target 24 | release.properties 25 | pom.xml.* 26 | 27 | # Folder containing all the Google Play APKs 28 | apks 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Logo][1] 2 | 3 | HistoryEditText 4 | =============== 5 | Android EditText that auto-saves previous values and displays them in a dropdown when used again. 6 | [1]: http://png-5.findicons.com/files/icons/1757/isabi/128/time_machine_shaped.png 7 | 8 | 9 | *This is a work in progress* 10 | -------------------------------------------------------------------------------- /art/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenlibs/HistoryEditText/311294b7d84323758d7bf729dd27ad8ce7c4e2fb/art/logo.png -------------------------------------------------------------------------------- /library/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /library/libs/CWAC-MergeAdapter.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenlibs/HistoryEditText/311294b7d84323758d7bf729dd27ad8ce7c4e2fb/library/libs/CWAC-MergeAdapter.jar -------------------------------------------------------------------------------- /library/libs/CWAC-SackOfViewsAdapter.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenlibs/HistoryEditText/311294b7d84323758d7bf729dd27ad8ce7c4e2fb/library/libs/CWAC-SackOfViewsAdapter.jar -------------------------------------------------------------------------------- /library/libs/android-support-v4.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenlibs/HistoryEditText/311294b7d84323758d7bf729dd27ad8ce7c4e2fb/library/libs/android-support-v4.jar -------------------------------------------------------------------------------- /library/libs/gson-2.2.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenlibs/HistoryEditText/311294b7d84323758d7bf729dd27ad8ce7c4e2fb/library/libs/gson-2.2.2.jar -------------------------------------------------------------------------------- /library/libs/resty-0.3.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenlibs/HistoryEditText/311294b7d84323758d7bf729dd27ad8ce7c4e2fb/library/libs/resty-0.3.2.jar -------------------------------------------------------------------------------- /library/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 4.0.0 5 | 6 | library 7 | HistoryEditText 8 | apklib 9 | 10 | 11 | com.zenlibs.historyedittext 12 | parent 13 | 1.0.0-SNAPSHOT 14 | ../pom.xml 15 | 16 | 17 | 18 | 19 | com.google.android 20 | android 21 | provided 22 | 23 | 24 | 25 | com.google.android 26 | support-v4 27 | 28 | 29 | 30 | com.actionbarsherlock 31 | actionbarsherlock 32 | 4.2.0 33 | apklib 34 | 35 | 36 | 37 | 38 | src 39 | 40 | 41 | 42 | com.jayway.maven.plugins.android.generation2 43 | android-maven-plugin 44 | true 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /library/proguard-project.txt: -------------------------------------------------------------------------------- 1 | # To enable ProGuard in your project, edit project.properties 2 | # to define the proguard.config property as described in that file. 3 | # 4 | # Add project specific ProGuard rules here. 5 | # By default, the flags in this file are appended to flags specified 6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt 7 | # You can edit the include path and order by changing the ProGuard 8 | # include property in project.properties. 9 | # 10 | # For more details, see 11 | # http://developer.android.com/guide/developing/tools/proguard.html 12 | 13 | # Add any project specific keep options here: 14 | 15 | # If your project uses WebView with JS, uncomment the following 16 | # and specify the fully qualified class name to the JavaScript interface 17 | # class: 18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 19 | # public *; 20 | #} 21 | -------------------------------------------------------------------------------- /library/project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system edit 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | # 10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): 11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt 12 | 13 | # Project target. 14 | target=android-17 15 | android.library=true 16 | -------------------------------------------------------------------------------- /library/res/color/het__primary_text_dark.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /library/res/color/het__primary_text_light.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /library/res/drawable-hdpi/het__ic_device_access_time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenlibs/HistoryEditText/311294b7d84323758d7bf729dd27ad8ce7c4e2fb/library/res/drawable-hdpi/het__ic_device_access_time.png -------------------------------------------------------------------------------- /library/res/drawable-mdpi/het__ic_device_access_time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenlibs/HistoryEditText/311294b7d84323758d7bf729dd27ad8ce7c4e2fb/library/res/drawable-mdpi/het__ic_device_access_time.png -------------------------------------------------------------------------------- /library/res/drawable-xhdpi/het__ic_device_access_time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenlibs/HistoryEditText/311294b7d84323758d7bf729dd27ad8ce7c4e2fb/library/res/drawable-xhdpi/het__ic_device_access_time.png -------------------------------------------------------------------------------- /library/res/layout/het__dropdown_history_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | -------------------------------------------------------------------------------- /library/res/layout/het__simple_dropdown_hint.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | -------------------------------------------------------------------------------- /library/res/layout/het__simple_dropdown_item_1line.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /library/res/values/het__attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /library/res/values/het__colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | @android:color/background_light 5 | @android:color/background_dark 6 | #80ffffff 7 | #80000000 8 | @color/bright_foreground_light 9 | @color/bright_foreground_dark 10 | #bebebe 11 | 12 | 13 | #9983CC39 14 | #808080 15 | #9983CC39 16 | #5c5cff 17 | #808080 18 | #323232 19 | #0000ee 20 | 21 | -------------------------------------------------------------------------------- /library/res/values/het__styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 23 | 24 | 34 | 35 | 43 | 44 | 47 | 48 | -------------------------------------------------------------------------------- /library/src/com/zenlibs/historyedittext/AbsHistoryEditText.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 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 | 17 | package com.zenlibs.historyedittext; 18 | 19 | import android.content.Context; 20 | import android.content.res.TypedArray; 21 | import android.database.DataSetObserver; 22 | import android.graphics.Rect; 23 | import android.graphics.drawable.Drawable; 24 | import android.text.Editable; 25 | import android.text.Selection; 26 | import android.text.TextUtils; 27 | import android.text.TextWatcher; 28 | import android.util.AttributeSet; 29 | import android.util.Log; 30 | import android.view.KeyEvent; 31 | import android.view.LayoutInflater; 32 | import android.view.MotionEvent; 33 | import android.view.View; 34 | import android.view.ViewGroup; 35 | import android.view.WindowManager; 36 | import android.view.inputmethod.CompletionInfo; 37 | import android.view.inputmethod.EditorInfo; 38 | import android.view.inputmethod.InputMethodManager; 39 | import android.widget.AbsListView; 40 | import android.widget.AdapterView; 41 | import android.widget.EditText; 42 | import android.widget.Filter; 43 | import android.widget.Filterable; 44 | import android.widget.LinearLayout; 45 | import android.widget.ListAdapter; 46 | import android.widget.ListView; 47 | import android.widget.PopupWindow; 48 | import android.widget.TextView; 49 | 50 | /** 51 | *

52 | * An editable text view that shows completion suggestions automatically while 53 | * the user is typing. The list of suggestions is displayed in a drop down menu 54 | * from which the user can choose an item to replace the content of the edit box 55 | * with. 56 | *

57 | * 58 | *

59 | * The drop down can be dismissed at any time by pressing the back key or, if no 60 | * item is selected in the drop down, by pressing the enter/dpad center key. 61 | *

62 | * 63 | *

64 | * The list of suggestions is obtained from a data adapter and appears only 65 | * after a given number of characters defined by {@link #getThreshold() the 66 | * threshold}. 67 | *

68 | * 69 | *

70 | * The following code snippet shows how to create a text view which suggests 71 | * various countries names while the user is typing: 72 | *

73 | * 74 | *
  75 |  * public class CountriesActivity extends Activity {
  76 |  *     protected void onCreate(Bundle icicle) {
  77 |  *         super.onCreate(icicle);
  78 |  *         setContentView(R.layout.countries);
  79 |  * 
  80 |  *         ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_dropdown_item_1line,
  81 |  *                 COUNTRIES);
  82 |  *         AutoCompleteTextView textView = (AutoCompleteTextView) findViewById(R.id.countries_list);
  83 |  *         textView.setAdapter(adapter);
  84 |  *     }
  85 |  * 
  86 |  *     private static final String[] COUNTRIES = new String[] { "Belgium", "France", "Italy", "Germany", "Spain" };
  87 |  * }
  88 |  * 
89 | * 90 | * @attr ref android.android.R.styleable#AutoCompleteTextView_completionHint 91 | * @attr ref 92 | * android.android.R.styleable#AutoCompleteTextView_completionThreshold 93 | * @attr ref android.android.R.styleable#AutoCompleteTextView_completionHintView 94 | * @attr ref android.android.R.styleable#AutoCompleteTextView_dropDownSelector 95 | * @attr ref android.android.R.styleable#AutoCompleteTextView_dropDownAnchor 96 | * @attr ref android.android.R.styleable#AutoCompleteTextView_dropDownWidth 97 | * @attr ref android.android.R.styleable#AutoCompleteTextView_dropDownHeight 98 | * @attr ref 99 | * android.android.R.styleable#AutoCompleteTextView_dropDownVerticalOffset 100 | * @attr ref 101 | * android.android.R.styleable#AutoCompleteTextView_dropDownHorizontalOffset 102 | */ 103 | abstract class AbsHistoryEditText extends EditText { 104 | static final boolean DEBUG = false; 105 | static final String TAG = "AutoCompleteTextView"; 106 | 107 | private static final int HINT_VIEW_ID = 0x17; 108 | 109 | /** 110 | * This value controls the length of time that the user must leave a pointer 111 | * down without scrolling to expand the autocomplete dropdown list to cover 112 | * the IME. 113 | */ 114 | private static final int EXPAND_LIST_TIMEOUT = 250; 115 | 116 | private CharSequence mHintText; 117 | private int mHintResource; 118 | 119 | // Zenlibs: this was mAdapter, renamed to mUserAdapter 120 | private ListAdapter mUserAdapter; 121 | private Filter mFilter; 122 | 123 | private FroyoPopupWindow mPopup; 124 | private DropDownListView mDropDownList; 125 | private int mDropDownVerticalOffset; 126 | private int mDropDownHorizontalOffset; 127 | private int mDropDownAnchorId; 128 | private View mDropDownAnchorView; // view is retrieved lazily from id once needed 129 | private int mDropDownWidth; 130 | private int mDropDownHeight; 131 | private final Rect mTempRect = new Rect(); 132 | 133 | private Drawable mDropDownListHighlight; 134 | 135 | private AdapterView.OnItemClickListener mItemClickListener; 136 | private AdapterView.OnItemSelectedListener mItemSelectedListener; 137 | 138 | private final DropDownItemClickListener mDropDownItemClickListener = new DropDownItemClickListener(); 139 | 140 | private boolean mDropDownDismissedOnCompletion = true; 141 | 142 | private boolean mForceIgnoreOutsideTouch = false; 143 | 144 | private int mLastKeyCode = KeyEvent.KEYCODE_UNKNOWN; 145 | private boolean mOpenBefore; 146 | 147 | private Validator mValidator = null; 148 | 149 | private boolean mBlockCompletion; 150 | 151 | private ListSelectorHider mHideSelector; 152 | private Runnable mShowDropDownRunnable; 153 | private Runnable mResizePopupRunnable = new ResizePopupRunnable(); 154 | 155 | private PassThroughClickListener mPassThroughClickListener; 156 | private PopupDataSetObserver mObserver; 157 | private InputMethodManager mImm; 158 | // Zenlibs 159 | private ListAdapter mCombinedAdapter; 160 | private int mThreshold = 1; 161 | 162 | public AbsHistoryEditText(Context context) { 163 | this(context, null); 164 | } 165 | 166 | public AbsHistoryEditText(Context context, AttributeSet attrs) { 167 | this(context, attrs, android.R.attr.autoCompleteTextViewStyle); 168 | } 169 | 170 | public AbsHistoryEditText(Context context, AttributeSet attrs, int defStyle) { 171 | super(context, attrs, defStyle); 172 | 173 | mImm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); 174 | 175 | mPopup = new FroyoPopupWindow(context, attrs, android.R.attr.autoCompleteTextViewStyle); 176 | mPopup.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); 177 | 178 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.HistoryEditText, defStyle, 179 | R.style.Widget_HistoryEditText); 180 | 181 | mThreshold = a.getInt(R.styleable.HistoryEditText_android_completionThreshold, 2); 182 | 183 | mHintText = a.getText(R.styleable.HistoryEditText_android_completionHint); 184 | 185 | mDropDownListHighlight = a.getDrawable(R.styleable.HistoryEditText_android_dropDownSelector); 186 | mDropDownVerticalOffset = (int) a 187 | .getDimension(R.styleable.HistoryEditText_android_dropDownVerticalOffset, 0.0f); 188 | mDropDownHorizontalOffset = (int) a.getDimension(R.styleable.HistoryEditText_android_dropDownHorizontalOffset, 189 | 0.0f); 190 | 191 | // Get the anchor's id now, but the view won't be ready, so wait to actually get the 192 | // view and store it in mDropDownAnchorView lazily in getDropDownAnchorView later. 193 | // Defaults to NO_ID, in which case the getDropDownAnchorView method will simply return 194 | // this TextView, as a default anchoring point. 195 | mDropDownAnchorId = a.getResourceId(R.styleable.HistoryEditText_android_dropDownAnchor, View.NO_ID); 196 | 197 | // For dropdown width, the developer can specify a specific width, or MATCH_PARENT 198 | // (for full screen width) or WRAP_CONTENT (to match the width of the anchored view). 199 | mDropDownWidth = a.getLayoutDimension(R.styleable.HistoryEditText_android_dropDownWidth, 200 | ViewGroup.LayoutParams.WRAP_CONTENT); 201 | mDropDownHeight = a.getLayoutDimension(R.styleable.HistoryEditText_android_dropDownHeight, 202 | ViewGroup.LayoutParams.WRAP_CONTENT); 203 | 204 | mHintResource = a.getResourceId(R.styleable.HistoryEditText_android_completionHintView, 205 | R.layout.het__simple_dropdown_hint); 206 | 207 | // Always turn on the auto complete input type flag, since it 208 | // makes no sense to use this widget without it. 209 | int inputType = getInputType(); 210 | if ((inputType & EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_TEXT) { 211 | inputType |= EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE; 212 | setRawInputType(inputType); 213 | } 214 | 215 | a.recycle(); 216 | 217 | setFocusable(true); 218 | 219 | addTextChangedListener(new MyWatcher()); 220 | 221 | mPassThroughClickListener = new PassThroughClickListener(); 222 | super.setOnClickListener(mPassThroughClickListener); 223 | } 224 | 225 | @Override 226 | public void setOnClickListener(OnClickListener listener) { 227 | mPassThroughClickListener.mWrapped = listener; 228 | } 229 | 230 | /** 231 | * Private hook into the on click event, dispatched from 232 | * {@link PassThroughClickListener} 233 | */ 234 | private void onClickImpl() { 235 | // If the dropdown is showing, bring the keyboard to the front 236 | // when the user touches the text field. 237 | if (mPopup.isShowing()) { 238 | ensureImeVisible(true); 239 | } 240 | } 241 | 242 | /** 243 | *

244 | * Sets the optional hint text that is displayed at the bottom of the the 245 | * matching list. This can be used as a cue to the user on how to best use 246 | * the list, or to provide extra information. 247 | *

248 | * 249 | * @param hint 250 | * the text to be displayed to the user 251 | * 252 | * @attr ref android.android.R.styleable#AutoCompleteTextView_completionHint 253 | */ 254 | public void setCompletionHint(CharSequence hint) { 255 | mHintText = hint; 256 | } 257 | 258 | /** 259 | *

260 | * Returns the current width for the auto-complete drop down list. This can 261 | * be a fixed width, or {@link ViewGroup.LayoutParams#MATCH_PARENT} to fill 262 | * the screen, or {@link ViewGroup.LayoutParams#WRAP_CONTENT} to fit the 263 | * width of its anchor view. 264 | *

265 | * 266 | * @return the width for the drop down list 267 | * 268 | * @attr ref android.android.R.styleable#AutoCompleteTextView_dropDownWidth 269 | */ 270 | public int getDropDownWidth() { 271 | return mDropDownWidth; 272 | } 273 | 274 | /** 275 | *

276 | * Sets the current width for the auto-complete drop down list. This can be 277 | * a fixed width, or {@link ViewGroup.LayoutParams#MATCH_PARENT} to fill the 278 | * screen, or {@link ViewGroup.LayoutParams#WRAP_CONTENT} to fit the width 279 | * of its anchor view. 280 | *

281 | * 282 | * @param width 283 | * the width to use 284 | * 285 | * @attr ref android.android.R.styleable#AutoCompleteTextView_dropDownWidth 286 | */ 287 | public void setDropDownWidth(int width) { 288 | mDropDownWidth = width; 289 | } 290 | 291 | /** 292 | *

293 | * Returns the current height for the auto-complete drop down list. This can 294 | * be a fixed height, or {@link ViewGroup.LayoutParams#MATCH_PARENT} to fill 295 | * the screen, or {@link ViewGroup.LayoutParams#WRAP_CONTENT} to fit the 296 | * height of the drop down's content. 297 | *

298 | * 299 | * @return the height for the drop down list 300 | * 301 | * @attr ref android.android.R.styleable#AutoCompleteTextView_dropDownHeight 302 | */ 303 | public int getDropDownHeight() { 304 | return mDropDownHeight; 305 | } 306 | 307 | /** 308 | *

309 | * Sets the current height for the auto-complete drop down list. This can be 310 | * a fixed height, or {@link ViewGroup.LayoutParams#MATCH_PARENT} to fill 311 | * the screen, or {@link ViewGroup.LayoutParams#WRAP_CONTENT} to fit the 312 | * height of the drop down's content. 313 | *

314 | * 315 | * @param height 316 | * the height to use 317 | * 318 | * @attr ref android.android.R.styleable#AutoCompleteTextView_dropDownHeight 319 | */ 320 | public void setDropDownHeight(int height) { 321 | mDropDownHeight = height; 322 | } 323 | 324 | /** 325 | *

326 | * Returns the id for the view that the auto-complete drop down list is 327 | * anchored to. 328 | *

329 | * 330 | * @return the view's id, or {@link View#NO_ID} if none specified 331 | * 332 | * @attr ref android.android.R.styleable#AutoCompleteTextView_dropDownAnchor 333 | */ 334 | public int getDropDownAnchor() { 335 | return mDropDownAnchorId; 336 | } 337 | 338 | /** 339 | *

340 | * Sets the view to which the auto-complete drop down list should anchor. 341 | * The view corresponding to this id will not be loaded until the next time 342 | * it is needed to avoid loading a view which is not yet instantiated. 343 | *

344 | * 345 | * @param id 346 | * the id to anchor the drop down list view to 347 | * 348 | * @attr ref android.android.R.styleable#AutoCompleteTextView_dropDownAnchor 349 | */ 350 | public void setDropDownAnchor(int id) { 351 | mDropDownAnchorId = id; 352 | mDropDownAnchorView = null; 353 | } 354 | 355 | /** 356 | *

357 | * Gets the background of the auto-complete drop-down list. 358 | *

359 | * 360 | * @return the background drawable 361 | * 362 | * @attr ref android.android.R.styleable#PopupWindow_popupBackground 363 | */ 364 | public Drawable getDropDownBackground() { 365 | return mPopup.getBackground(); 366 | } 367 | 368 | /** 369 | *

370 | * Sets the background of the auto-complete drop-down list. 371 | *

372 | * 373 | * @param d 374 | * the drawable to set as the background 375 | * 376 | * @attr ref android.android.R.styleable#PopupWindow_popupBackground 377 | */ 378 | public void setDropDownBackgroundDrawable(Drawable d) { 379 | mPopup.setBackgroundDrawable(d); 380 | } 381 | 382 | /** 383 | *

384 | * Sets the background of the auto-complete drop-down list. 385 | *

386 | * 387 | * @param id 388 | * the id of the drawable to set as the background 389 | * 390 | * @attr ref android.android.R.styleable#PopupWindow_popupBackground 391 | */ 392 | public void setDropDownBackgroundResource(int id) { 393 | mPopup.setBackgroundDrawable(getResources().getDrawable(id)); 394 | } 395 | 396 | /** 397 | *

398 | * Sets the vertical offset used for the auto-complete drop-down list. 399 | *

400 | * 401 | * @param offset 402 | * the vertical offset 403 | */ 404 | public void setDropDownVerticalOffset(int offset) { 405 | mDropDownVerticalOffset = offset; 406 | } 407 | 408 | /** 409 | *

410 | * Gets the vertical offset used for the auto-complete drop-down list. 411 | *

412 | * 413 | * @return the vertical offset 414 | */ 415 | public int getDropDownVerticalOffset() { 416 | return mDropDownVerticalOffset; 417 | } 418 | 419 | /** 420 | *

421 | * Sets the horizontal offset used for the auto-complete drop-down list. 422 | *

423 | * 424 | * @param offset 425 | * the horizontal offset 426 | */ 427 | public void setDropDownHorizontalOffset(int offset) { 428 | mDropDownHorizontalOffset = offset; 429 | } 430 | 431 | /** 432 | *

433 | * Gets the horizontal offset used for the auto-complete drop-down list. 434 | *

435 | * 436 | * @return the horizontal offset 437 | */ 438 | public int getDropDownHorizontalOffset() { 439 | return mDropDownHorizontalOffset; 440 | } 441 | 442 | /** 443 | *

444 | * Sets the animation style of the auto-complete drop-down list. 445 | *

446 | * 447 | *

448 | * If the drop-down is showing, calling this method will take effect only 449 | * the next time the drop-down is shown. 450 | *

451 | * 452 | * @param animationStyle 453 | * animation style to use when the drop-down appears and 454 | * disappears. Set to -1 for the default animation, 0 for no 455 | * animation, or a resource identifier for an explicit animation. 456 | * 457 | * @hide Pending API council approval 458 | */ 459 | public void setDropDownAnimationStyle(int animationStyle) { 460 | mPopup.setAnimationStyle(animationStyle); 461 | } 462 | 463 | /** 464 | *

465 | * Returns the animation style that is used when the drop-down list appears 466 | * and disappears 467 | *

468 | * 469 | * @return the animation style that is used when the drop-down list appears 470 | * and disappears 471 | * 472 | * @hide Pending API council approval 473 | */ 474 | public int getDropDownAnimationStyle() { 475 | return mPopup.getAnimationStyle(); 476 | } 477 | 478 | /** 479 | * Checks whether the drop-down is dismissed when a suggestion is clicked. 480 | * 481 | * @hide Pending API council approval 482 | */ 483 | public boolean isDropDownDismissedOnCompletion() { 484 | return mDropDownDismissedOnCompletion; 485 | } 486 | 487 | /** 488 | * Sets whether the drop-down is dismissed when a suggestion is clicked. 489 | * This is true by default. 490 | * 491 | * @param dropDownDismissedOnCompletion 492 | * Whether to dismiss the drop-down. 493 | * 494 | * @hide Pending API council approval 495 | */ 496 | public void setDropDownDismissedOnCompletion(boolean dropDownDismissedOnCompletion) { 497 | mDropDownDismissedOnCompletion = dropDownDismissedOnCompletion; 498 | } 499 | 500 | /** 501 | *

502 | * Returns the number of characters the user must type before the drop down 503 | * list is shown. 504 | *

505 | * 506 | * @return the minimum number of characters to type to show the drop down 507 | * 508 | * @see #setThreshold(int) 509 | */ 510 | public int getThreshold() { 511 | return mThreshold; 512 | } 513 | 514 | /** 515 | *

516 | * Specifies the minimum number of characters the user has to type in the 517 | * edit box before the drop down list is shown. 518 | *

519 | * 520 | *

521 | * When threshold is less than or equals 0, a threshold of 1 is 522 | * applied. 523 | *

524 | * 525 | * @param threshold 526 | * the number of characters to type before the drop down is shown 527 | * 528 | * @see #getThreshold() 529 | * 530 | * @attr ref android.R.styleable#AutoCompleteTextView_completionThreshold 531 | */ 532 | public void setThreshold(int threshold) { 533 | if (threshold <= 0) { 534 | threshold = 1; 535 | } 536 | mThreshold = threshold; 537 | } 538 | 539 | /** 540 | *

541 | * Sets the listener that will be notified when the user clicks an item in 542 | * the drop down list. 543 | *

544 | * 545 | * @param l 546 | * the item click listener 547 | */ 548 | public void setOnItemClickListener(AdapterView.OnItemClickListener l) { 549 | mItemClickListener = l; 550 | } 551 | 552 | /** 553 | *

554 | * Sets the listener that will be notified when the user selects an item in 555 | * the drop down list. 556 | *

557 | * 558 | * @param l 559 | * the item selected listener 560 | */ 561 | public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener l) { 562 | mItemSelectedListener = l; 563 | } 564 | 565 | /** 566 | *

567 | * Returns the listener that is notified whenever the user clicks an item in 568 | * the drop down list. 569 | *

570 | * 571 | * @return the item click listener 572 | * 573 | * @deprecated Use {@link #getOnItemClickListener()} intead 574 | */ 575 | @Deprecated 576 | public AdapterView.OnItemClickListener getItemClickListener() { 577 | return mItemClickListener; 578 | } 579 | 580 | /** 581 | *

582 | * Returns the listener that is notified whenever the user selects an item 583 | * in the drop down list. 584 | *

585 | * 586 | * @return the item selected listener 587 | * 588 | * @deprecated Use {@link #getOnItemSelectedListener()} intead 589 | */ 590 | @Deprecated 591 | public AdapterView.OnItemSelectedListener getItemSelectedListener() { 592 | return mItemSelectedListener; 593 | } 594 | 595 | /** 596 | *

597 | * Returns the listener that is notified whenever the user clicks an item in 598 | * the drop down list. 599 | *

600 | * 601 | * @return the item click listener 602 | */ 603 | public AdapterView.OnItemClickListener getOnItemClickListener() { 604 | return mItemClickListener; 605 | } 606 | 607 | /** 608 | *

609 | * Returns the listener that is notified whenever the user selects an item 610 | * in the drop down list. 611 | *

612 | * 613 | * @return the item selected listener 614 | */ 615 | public AdapterView.OnItemSelectedListener getOnItemSelectedListener() { 616 | return mItemSelectedListener; 617 | } 618 | 619 | /** 620 | *

621 | * Returns a filterable list adapter used for auto completion. 622 | *

623 | * 624 | * @return a data adapter used for auto completion 625 | */ 626 | public ListAdapter getAdapter() { 627 | return mUserAdapter; 628 | } 629 | 630 | /** 631 | *

632 | * Changes the list of data used for auto completion. The provided list must 633 | * be a filterable list adapter. 634 | *

635 | * 636 | *

637 | * The caller is still responsible for managing any resources used by the 638 | * adapter. Notably, when the AutoCompleteTextView is closed or released, 639 | * the adapter is not notified. A common case is the use of 640 | * {@link android.widget.CursorAdapter}, which contains a 641 | * {@link android.database.Cursor} that must be closed. This can be done 642 | * automatically (see 643 | * {@link android.app.Activity#startManagingCursor(android.database.Cursor) 644 | * startManagingCursor()}), or by manually closing the cursor when the 645 | * AutoCompleteTextView is dismissed. 646 | *

647 | * 648 | * @param adapter 649 | * the adapter holding the auto completion data 650 | * 651 | * @see #getAdapter() 652 | * @see android.widget.Filterable 653 | * @see android.widget.ListAdapter 654 | */ 655 | public void setAdapter(T adapter) { 656 | mUserAdapter = adapter; 657 | if (mUserAdapter != null) { 658 | mFilter = ((Filterable) mUserAdapter).getFilter(); 659 | } else { 660 | mFilter = null; 661 | } 662 | rebuildCombinedAdapter(); 663 | } 664 | 665 | // Zenlibs 666 | void setCombinedAdapter(ListAdapter adapter) { 667 | if (adapter == null) { 668 | if (mObserver != null && mCombinedAdapter != null) { 669 | mCombinedAdapter.unregisterDataSetObserver(mObserver); 670 | } 671 | mCombinedAdapter = null; 672 | mFilter = null; 673 | return; 674 | } 675 | 676 | if (mObserver == null) { 677 | mObserver = new PopupDataSetObserver(); 678 | } else if (mCombinedAdapter != null) { 679 | mCombinedAdapter.unregisterDataSetObserver(mObserver); 680 | } 681 | mCombinedAdapter = adapter; 682 | if (mCombinedAdapter != null) { 683 | adapter.registerDataSetObserver(mObserver); 684 | } 685 | 686 | if (mDropDownList != null) { 687 | mDropDownList.setAdapter(mCombinedAdapter); 688 | } 689 | } 690 | 691 | @Override 692 | public boolean onKeyPreIme(int keyCode, KeyEvent event) { 693 | if (keyCode == KeyEvent.KEYCODE_BACK && isPopupShowing() && true) { 694 | // special case for the back key, we do not even try to send it 695 | // to the drop down list but instead, consume it immediately 696 | if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { 697 | getKeyDispatcherState().startTracking(event, this); 698 | return true; 699 | } else if (event.getAction() == KeyEvent.ACTION_UP) { 700 | getKeyDispatcherState().handleUpEvent(event); 701 | if (event.isTracking() && !event.isCanceled()) { 702 | dismissDropDown(); 703 | return true; 704 | } 705 | } 706 | } 707 | return super.onKeyPreIme(keyCode, event); 708 | } 709 | 710 | @Override 711 | public boolean onKeyUp(int keyCode, KeyEvent event) { 712 | if (isPopupShowing() && mDropDownList.getSelectedItemPosition() >= 0) { 713 | boolean consumed = mDropDownList.onKeyUp(keyCode, event); 714 | if (consumed) { 715 | switch (keyCode) { 716 | // if the list accepts the key events and the key event 717 | // was a click, the text view gets the selected item 718 | // from the drop down as its content 719 | case KeyEvent.KEYCODE_ENTER: 720 | case KeyEvent.KEYCODE_DPAD_CENTER: 721 | performCompletion(); 722 | return true; 723 | } 724 | } 725 | } 726 | return super.onKeyUp(keyCode, event); 727 | } 728 | 729 | @Override 730 | public boolean onKeyDown(int keyCode, KeyEvent event) { 731 | // when the drop down is shown, we drive it directly 732 | if (isPopupShowing()) { 733 | // the key events are forwarded to the list in the drop down view 734 | // note that ListView handles space but we don't want that to happen 735 | // also if selection is not currently in the drop down, then don't 736 | // let center or enter presses go there since that would cause it 737 | // to select one of its items 738 | if (keyCode != KeyEvent.KEYCODE_SPACE 739 | && (mDropDownList.getSelectedItemPosition() >= 0 || (keyCode != KeyEvent.KEYCODE_ENTER && keyCode != KeyEvent.KEYCODE_DPAD_CENTER))) { 740 | int curIndex = mDropDownList.getSelectedItemPosition(); 741 | boolean consumed; 742 | 743 | final boolean below = !mPopup.isAboveAnchor(); 744 | 745 | final ListAdapter adapter = mCombinedAdapter; 746 | 747 | boolean allEnabled; 748 | int firstItem = Integer.MAX_VALUE; 749 | int lastItem = Integer.MIN_VALUE; 750 | 751 | if (adapter != null) { 752 | allEnabled = adapter.areAllItemsEnabled(); 753 | firstItem = allEnabled ? 0 : mDropDownList.lookForSelectablePositionCompat(0, true); 754 | lastItem = allEnabled ? adapter.getCount() - 1 : mDropDownList.lookForSelectablePositionCompat( 755 | adapter.getCount() - 1, false); 756 | } 757 | 758 | if ((below && keyCode == KeyEvent.KEYCODE_DPAD_UP && curIndex <= firstItem) 759 | || (!below && keyCode == KeyEvent.KEYCODE_DPAD_DOWN && curIndex >= lastItem)) { 760 | // When the selection is at the top, we block the key 761 | // event to prevent focus from moving. 762 | clearListSelection(); 763 | mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); 764 | showDropDown(); 765 | return true; 766 | } else { 767 | // WARNING: Please read the comment where mListSelectionHidden 768 | // is declared 769 | mDropDownList.mListSelectionHidden = false; 770 | } 771 | 772 | consumed = mDropDownList.onKeyDown(keyCode, event); 773 | if (DEBUG) 774 | Log.v(TAG, "Key down: code=" + keyCode + " list consumed=" + consumed); 775 | 776 | if (consumed) { 777 | // If it handled the key event, then the user is 778 | // navigating in the list, so we should put it in front. 779 | mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); 780 | // Here's a little trick we need to do to make sure that 781 | // the list view is actually showing its focus indicator, 782 | // by ensuring it has focus and getting its window out 783 | // of touch mode. 784 | mDropDownList.requestFocusFromTouch(); 785 | showDropDown(); 786 | 787 | switch (keyCode) { 788 | // avoid passing the focus from the text view to the 789 | // next component 790 | case KeyEvent.KEYCODE_ENTER: 791 | case KeyEvent.KEYCODE_DPAD_CENTER: 792 | case KeyEvent.KEYCODE_DPAD_DOWN: 793 | case KeyEvent.KEYCODE_DPAD_UP: 794 | return true; 795 | } 796 | } else { 797 | if (below && keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { 798 | // when the selection is at the bottom, we block the 799 | // event to avoid going to the next focusable widget 800 | if (curIndex == lastItem) { 801 | return true; 802 | } 803 | } else if (!below && keyCode == KeyEvent.KEYCODE_DPAD_UP && curIndex == firstItem) { 804 | return true; 805 | } 806 | } 807 | } 808 | } else { 809 | switch (keyCode) { 810 | case KeyEvent.KEYCODE_DPAD_DOWN: 811 | performValidation(); 812 | } 813 | } 814 | 815 | mLastKeyCode = keyCode; 816 | boolean handled = super.onKeyDown(keyCode, event); 817 | mLastKeyCode = KeyEvent.KEYCODE_UNKNOWN; 818 | 819 | if (handled && isPopupShowing() && mDropDownList != null) { 820 | clearListSelection(); 821 | } 822 | 823 | return handled; 824 | } 825 | 826 | /** 827 | * This is used to watch for edits to the text view. Note that we call to 828 | * methods on the auto complete text view class so that we can access 829 | * private vars without going through thunks. 830 | */ 831 | private class MyWatcher implements TextWatcher { 832 | public void afterTextChanged(Editable s) { 833 | doAfterTextChanged(); 834 | } 835 | 836 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 837 | doBeforeTextChanged(); 838 | } 839 | 840 | public void onTextChanged(CharSequence s, int start, int before, int count) { 841 | } 842 | } 843 | 844 | void doBeforeTextChanged() { 845 | if (mBlockCompletion) 846 | return; 847 | 848 | // when text is changed, inserted or deleted, we attempt to show 849 | // the drop down 850 | mOpenBefore = isPopupShowing(); 851 | if (DEBUG) 852 | Log.v(TAG, "before text changed: open=" + mOpenBefore); 853 | } 854 | 855 | void doAfterTextChanged() { 856 | if (mBlockCompletion) 857 | return; 858 | 859 | // if the list was open before the keystroke, but closed afterwards, 860 | // then something in the keystroke processing (an input filter perhaps) 861 | // called performCompletion() and we shouldn't do any more processing. 862 | if (DEBUG) 863 | Log.v(TAG, "after text changed: openBefore=" + mOpenBefore + " open=" + isPopupShowing()); 864 | if (mOpenBefore && !isPopupShowing()) { 865 | return; 866 | } 867 | 868 | performFiltering(getText(), mLastKeyCode); 869 | } 870 | 871 | /** 872 | *

873 | * Indicates whether the popup menu is showing. 874 | *

875 | * 876 | * @return true if the popup menu is showing, false otherwise 877 | */ 878 | public boolean isPopupShowing() { 879 | return mPopup.isShowing(); 880 | } 881 | 882 | /** 883 | *

884 | * Converts the selected item from the drop down list into a sequence of 885 | * character that can be used in the edit box. 886 | *

887 | * 888 | * @param selectedItem 889 | * the item selected by the user for completion 890 | * 891 | * @return a sequence of characters representing the selected suggestion 892 | */ 893 | protected CharSequence convertSelectionToString(Object selectedItem) { 894 | // 895 | if (mFilter == null) { 896 | // This means there's no user adapter and thus no filter 897 | return selectedItem == null ? "" : selectedItem.toString(); 898 | } 899 | // 900 | return mFilter.convertResultToString(selectedItem); 901 | } 902 | 903 | /** 904 | *

905 | * Clear the list selection. This may only be temporary, as user input will 906 | * often bring it back. 907 | */ 908 | public void clearListSelection() { 909 | final DropDownListView list = mDropDownList; 910 | if (list != null) { 911 | // WARNING: Please read the comment where mListSelectionHidden is declared 912 | list.mListSelectionHidden = true; 913 | list.hideSelectorCompat(); 914 | list.requestLayout(); 915 | } 916 | } 917 | 918 | /** 919 | * Set the position of the dropdown view selection. 920 | * 921 | * @param position 922 | * The position to move the selector to. 923 | */ 924 | public void setListSelection(int position) { 925 | if (mPopup.isShowing() && (mDropDownList != null)) { 926 | mDropDownList.mListSelectionHidden = false; 927 | mDropDownList.setSelection(position); 928 | // ListView.setSelection() will call requestLayout() 929 | } 930 | } 931 | 932 | /** 933 | * Get the position of the dropdown view selection, if there is one. Returns 934 | * {@link ListView#INVALID_POSITION ListView.INVALID_POSITION} if there is 935 | * no dropdown or if there is no selection. 936 | * 937 | * @return the position of the current selection, if there is one, or 938 | * {@link ListView#INVALID_POSITION ListView.INVALID_POSITION} if 939 | * not. 940 | * 941 | * @see ListView#getSelectedItemPosition() 942 | */ 943 | public int getListSelection() { 944 | if (mPopup.isShowing() && (mDropDownList != null)) { 945 | return mDropDownList.getSelectedItemPosition(); 946 | } 947 | return ListView.INVALID_POSITION; 948 | } 949 | 950 | /** 951 | *

952 | * Starts filtering the content of the drop down list. The filtering pattern 953 | * is the content of the edit box. Subclasses should override this method to 954 | * filter with a different pattern, for instance a substring of 955 | * text. 956 | *

957 | * 958 | * @param text 959 | * the filtering pattern 960 | * @param keyCode 961 | * the last character inserted in the edit box; beware that this 962 | * will be null when text is being added through a soft input 963 | * method. 964 | */ 965 | protected void performFiltering(CharSequence text, int keyCode) { 966 | if (text == null) { 967 | updateDropDownForFilter(0, true); 968 | } else { 969 | // 970 | if (mFilter != null) { 971 | // 972 | mFilter.filter(text, new Filter.FilterListener() { 973 | @Override 974 | public void onFilterComplete(int count) { 975 | updateDropDownForFilter(count, false); 976 | } 977 | }); 978 | } 979 | } 980 | } 981 | 982 | /** 983 | *

984 | * Performs the text completion by converting the selected item from the 985 | * drop down list into a string, replacing the text box's content with this 986 | * string and finally dismissing the drop down menu. 987 | *

988 | */ 989 | public void performCompletion() { 990 | performCompletion(null, -1, -1); 991 | } 992 | 993 | @Override 994 | public void onCommitCompletion(CompletionInfo completion) { 995 | if (isPopupShowing()) { 996 | mBlockCompletion = true; 997 | replaceText(completion.getText()); 998 | mBlockCompletion = false; 999 | 1000 | if (mItemClickListener != null) { 1001 | final DropDownListView list = mDropDownList; 1002 | // Note that we don't have a View here, so we will need to 1003 | // supply null. Hopefully no existing apps crash... 1004 | mItemClickListener.onItemClick(list, null, completion.getPosition(), completion.getId()); 1005 | } 1006 | } 1007 | } 1008 | 1009 | private void performCompletion(View selectedView, int position, long id) { 1010 | if (isPopupShowing()) { 1011 | Object selectedItem; 1012 | if (position < 0) { 1013 | selectedItem = mDropDownList.getSelectedItem(); 1014 | } else { 1015 | selectedItem = mCombinedAdapter.getItem(position); 1016 | } 1017 | if (selectedItem == null) { 1018 | Log.w(TAG, "performCompletion: no selected item"); 1019 | return; 1020 | } 1021 | 1022 | mBlockCompletion = true; 1023 | replaceText(convertSelectionToString(selectedItem)); 1024 | mBlockCompletion = false; 1025 | 1026 | if (mItemClickListener != null) { 1027 | final DropDownListView list = mDropDownList; 1028 | 1029 | if (selectedView == null || position < 0) { 1030 | selectedView = list.getSelectedView(); 1031 | position = list.getSelectedItemPosition(); 1032 | id = list.getSelectedItemId(); 1033 | } 1034 | mItemClickListener.onItemClick(list, selectedView, position, id); 1035 | } 1036 | } 1037 | 1038 | if (mDropDownDismissedOnCompletion && true) { 1039 | dismissDropDown(); 1040 | } 1041 | } 1042 | 1043 | /** 1044 | * Identifies whether the view is currently performing a text completion, so 1045 | * subclasses can decide whether to respond to text changed events. 1046 | */ 1047 | public boolean isPerformingCompletion() { 1048 | return mBlockCompletion; 1049 | } 1050 | 1051 | /** 1052 | * Like {@link #setText(CharSequence)}, except that it can disable 1053 | * filtering. 1054 | * 1055 | * @param filter 1056 | * If false, no filtering will be performed as a 1057 | * result of this call. 1058 | * 1059 | * @hide Pending API council approval. 1060 | */ 1061 | public void setText(CharSequence text, boolean filter) { 1062 | if (filter) { 1063 | setText(text); 1064 | } else { 1065 | mBlockCompletion = true; 1066 | setText(text); 1067 | mBlockCompletion = false; 1068 | } 1069 | } 1070 | 1071 | /** 1072 | *

1073 | * Performs the text completion by replacing the current text by the 1074 | * selected item. Subclasses should override this method to avoid replacing 1075 | * the whole content of the edit box. 1076 | *

1077 | * 1078 | * @param text 1079 | * the selected suggestion in the drop down list 1080 | */ 1081 | protected void replaceText(CharSequence text) { 1082 | clearComposingText(); 1083 | 1084 | setText(text); 1085 | // make sure we keep the caret at the end of the text view 1086 | Editable spannable = getText(); 1087 | Selection.setSelection(spannable, spannable.length()); 1088 | } 1089 | 1090 | private void updateDropDownForFilter(int count, boolean force) { 1091 | // Not attached to window, don't update drop-down 1092 | if (getWindowVisibility() == View.GONE) 1093 | return; 1094 | 1095 | /* 1096 | * This checks enoughToFilter() again because filtering requests 1097 | * are asynchronous, so the result may come back after enough text 1098 | * has since been deleted to make it no longer appropriate 1099 | * to filter. 1100 | */ 1101 | 1102 | if (count > 0 || force) { 1103 | if (hasFocus() && hasWindowFocus()) { 1104 | showDropDown(); 1105 | } 1106 | } else { 1107 | dismissDropDown(); 1108 | } 1109 | } 1110 | 1111 | @Override 1112 | public void onWindowFocusChanged(boolean hasWindowFocus) { 1113 | super.onWindowFocusChanged(hasWindowFocus); 1114 | if (!hasWindowFocus) { 1115 | dismissDropDown(); 1116 | } 1117 | } 1118 | 1119 | @Override 1120 | protected void onDisplayHint(int hint) { 1121 | super.onDisplayHint(hint); 1122 | switch (hint) { 1123 | case INVISIBLE: 1124 | if (true) { 1125 | dismissDropDown(); 1126 | } 1127 | break; 1128 | } 1129 | } 1130 | 1131 | @Override 1132 | protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { 1133 | super.onFocusChanged(focused, direction, previouslyFocusedRect); 1134 | // Perform validation if the view is losing focus. 1135 | if (!focused) { 1136 | performValidation(); 1137 | dismissDropDown(); 1138 | } else { 1139 | // Zenlibs 1140 | showDropDown(); 1141 | } 1142 | } 1143 | 1144 | @Override 1145 | protected void onAttachedToWindow() { 1146 | super.onAttachedToWindow(); 1147 | } 1148 | 1149 | @Override 1150 | protected void onDetachedFromWindow() { 1151 | dismissDropDown(); 1152 | super.onDetachedFromWindow(); 1153 | } 1154 | 1155 | /** 1156 | *

1157 | * Closes the drop down if present on screen. 1158 | *

1159 | */ 1160 | public void dismissDropDown() { 1161 | if (mImm != null) { 1162 | mImm.displayCompletions(this, null); 1163 | } 1164 | mPopup.dismiss(); 1165 | mPopup.setContentView(null); 1166 | mDropDownList = null; 1167 | } 1168 | 1169 | @Override 1170 | protected boolean setFrame(final int l, int t, final int r, int b) { 1171 | boolean result = super.setFrame(l, t, r, b); 1172 | 1173 | if (mPopup.isShowing()) { 1174 | showDropDown(); 1175 | } 1176 | 1177 | return result; 1178 | } 1179 | 1180 | /** 1181 | *

1182 | * Used for lazy instantiation of the anchor view from the id we have. If 1183 | * the value of the id is NO_ID or we can't find a view for the given id, we 1184 | * return this TextView as the default anchoring point. 1185 | *

1186 | */ 1187 | private View getDropDownAnchorView() { 1188 | if (mDropDownAnchorView == null && mDropDownAnchorId != View.NO_ID) { 1189 | mDropDownAnchorView = getRootView().findViewById(mDropDownAnchorId); 1190 | } 1191 | return mDropDownAnchorView == null ? this : mDropDownAnchorView; 1192 | } 1193 | 1194 | /** 1195 | * Issues a runnable to show the dropdown as soon as possible. 1196 | * 1197 | * @hide internal used only by SearchDialog 1198 | */ 1199 | public void showDropDownAfterLayout() { 1200 | post(mShowDropDownRunnable); 1201 | } 1202 | 1203 | /** 1204 | * Ensures that the drop down is not obscuring the IME. 1205 | * 1206 | * @param visible 1207 | * whether the ime should be in front. If false, the ime is 1208 | * pushed to the background. 1209 | * @hide internal used only here and SearchDialog 1210 | */ 1211 | public void ensureImeVisible(boolean visible) { 1212 | mPopup.setInputMethodMode(visible ? PopupWindow.INPUT_METHOD_NEEDED : PopupWindow.INPUT_METHOD_NOT_NEEDED); 1213 | showDropDown(); 1214 | } 1215 | 1216 | /** 1217 | * @hide internal used only here and SearchDialog 1218 | */ 1219 | public boolean isInputMethodNotNeeded() { 1220 | return mPopup.getInputMethodMode() == PopupWindow.INPUT_METHOD_NOT_NEEDED; 1221 | } 1222 | 1223 | /** 1224 | *

1225 | * Displays the drop down on screen. 1226 | *

1227 | */ 1228 | public void showDropDown() { 1229 | int height = buildDropDown(); 1230 | 1231 | int widthSpec = 0; 1232 | int heightSpec = 0; 1233 | 1234 | boolean noInputMethod = isInputMethodNotNeeded(); 1235 | 1236 | if (mPopup.isShowing()) { 1237 | if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) { 1238 | // The call to PopupWindow's update method below can accept -1 for any 1239 | // value you do not want to update. 1240 | widthSpec = -1; 1241 | } else if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) { 1242 | widthSpec = getDropDownAnchorView().getWidth(); 1243 | } else { 1244 | widthSpec = mDropDownWidth; 1245 | } 1246 | 1247 | if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) { 1248 | // The call to PopupWindow's update method below can accept -1 for any 1249 | // value you do not want to update. 1250 | heightSpec = noInputMethod ? height : ViewGroup.LayoutParams.MATCH_PARENT; 1251 | if (noInputMethod) { 1252 | mPopup.setWindowLayoutMode( 1253 | mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ? ViewGroup.LayoutParams.MATCH_PARENT 1254 | : 0, 0); 1255 | } else { 1256 | mPopup.setWindowLayoutMode( 1257 | mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ? ViewGroup.LayoutParams.MATCH_PARENT 1258 | : 0, ViewGroup.LayoutParams.MATCH_PARENT); 1259 | } 1260 | } else if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) { 1261 | heightSpec = height; 1262 | } else { 1263 | heightSpec = mDropDownHeight; 1264 | } 1265 | 1266 | mPopup.setOutsideTouchable(!mForceIgnoreOutsideTouch && true); 1267 | 1268 | mPopup.update(getDropDownAnchorView(), mDropDownHorizontalOffset, mDropDownVerticalOffset, widthSpec, 1269 | heightSpec); 1270 | } else { 1271 | if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) { 1272 | widthSpec = ViewGroup.LayoutParams.MATCH_PARENT; 1273 | } else { 1274 | if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) { 1275 | mPopup.setWidth(getDropDownAnchorView().getWidth()); 1276 | } else { 1277 | mPopup.setWidth(mDropDownWidth); 1278 | } 1279 | } 1280 | 1281 | if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) { 1282 | heightSpec = ViewGroup.LayoutParams.MATCH_PARENT; 1283 | } else { 1284 | if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) { 1285 | mPopup.setHeight(height); 1286 | } else { 1287 | mPopup.setHeight(mDropDownHeight); 1288 | } 1289 | } 1290 | 1291 | mPopup.setWindowLayoutMode(widthSpec, heightSpec); 1292 | mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); 1293 | 1294 | // use outside touchable to dismiss drop down when touching outside of it, so 1295 | // only set this if the dropdown is not always visible 1296 | mPopup.setOutsideTouchable(!mForceIgnoreOutsideTouch && true); 1297 | mPopup.setTouchInterceptor(new PopupTouchInterceptor()); 1298 | mPopup.showAsDropDown(getDropDownAnchorView(), mDropDownHorizontalOffset, mDropDownVerticalOffset); 1299 | mDropDownList.setSelection(ListView.INVALID_POSITION); 1300 | clearListSelection(); 1301 | post(mHideSelector); 1302 | } 1303 | } 1304 | 1305 | /** 1306 | * Forces outside touches to be ignored. Normally if 1307 | * {@link #isDropDownAlwaysVisible()} is false, we allow outside touch to 1308 | * dismiss the dropdown. If this is set to true, then we ignore outside 1309 | * touch even when the drop down is not set to always visible. 1310 | * 1311 | * @hide used only by SearchDialog 1312 | */ 1313 | public void setForceIgnoreOutsideTouch(boolean forceIgnoreOutsideTouch) { 1314 | mForceIgnoreOutsideTouch = forceIgnoreOutsideTouch; 1315 | } 1316 | 1317 | /** 1318 | *

1319 | * Builds the popup window's content and returns the height the popup should 1320 | * have. Returns -1 when the content already exists. 1321 | *

1322 | * 1323 | * @return the content's height or -1 if content already exists 1324 | */ 1325 | private int buildDropDown() { 1326 | ViewGroup dropDownView; 1327 | int otherHeights = 0; 1328 | 1329 | rebuildCombinedAdapter(); 1330 | 1331 | if (mCombinedAdapter != null) { 1332 | if (mImm != null) { 1333 | final int count = Math.min(mCombinedAdapter.getCount(), 20); 1334 | CompletionInfo[] completions = new CompletionInfo[count]; 1335 | int realCount = 0; 1336 | 1337 | for (int i = 0; i < count; i++) { 1338 | if (mCombinedAdapter.isEnabled(i)) { 1339 | realCount++; 1340 | Object item = mCombinedAdapter.getItem(i); 1341 | long id = mCombinedAdapter.getItemId(i); 1342 | completions[i] = new CompletionInfo(id, i, convertSelectionToString(item)); 1343 | } 1344 | } 1345 | 1346 | if (realCount != count) { 1347 | CompletionInfo[] tmp = new CompletionInfo[realCount]; 1348 | System.arraycopy(completions, 0, tmp, 0, realCount); 1349 | completions = tmp; 1350 | } 1351 | 1352 | mImm.displayCompletions(this, completions); 1353 | } 1354 | } 1355 | 1356 | if (mDropDownList == null) { 1357 | Context context = getContext(); 1358 | 1359 | mHideSelector = new ListSelectorHider(); 1360 | 1361 | /** 1362 | * This Runnable exists for the sole purpose of checking if the view 1363 | * layout has got completed and if so call showDropDown to display 1364 | * the drop down. This is used to show the drop down as soon as 1365 | * possible after user opens up the search dialog, without waiting 1366 | * for the normal UI pipeline to do it's job which is slower than 1367 | * this method. 1368 | */ 1369 | mShowDropDownRunnable = new Runnable() { 1370 | public void run() { 1371 | // View layout should be all done before displaying the drop down. 1372 | View view = getDropDownAnchorView(); 1373 | if (view != null && view.getWindowToken() != null) { 1374 | showDropDown(); 1375 | } 1376 | } 1377 | }; 1378 | 1379 | mDropDownList = new DropDownListView(context); 1380 | mDropDownList.setSelector(mDropDownListHighlight); 1381 | mDropDownList.setAdapter(mCombinedAdapter); 1382 | mDropDownList.setVerticalFadingEdgeEnabled(true); 1383 | mDropDownList.setOnItemClickListener(mDropDownItemClickListener); 1384 | mDropDownList.setFocusable(true); 1385 | mDropDownList.setFocusableInTouchMode(true); 1386 | mDropDownList.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { 1387 | public void onItemSelected(AdapterView parent, View view, int position, long id) { 1388 | 1389 | if (position != -1) { 1390 | DropDownListView dropDownList = mDropDownList; 1391 | 1392 | if (dropDownList != null) { 1393 | dropDownList.mListSelectionHidden = false; 1394 | } 1395 | } 1396 | } 1397 | 1398 | public void onNothingSelected(AdapterView parent) { 1399 | } 1400 | }); 1401 | mDropDownList.setOnScrollListener(new PopupScrollListener()); 1402 | 1403 | if (mItemSelectedListener != null) { 1404 | mDropDownList.setOnItemSelectedListener(mItemSelectedListener); 1405 | } 1406 | 1407 | dropDownView = mDropDownList; 1408 | 1409 | View hintView = getHintView(context); 1410 | if (hintView != null) { 1411 | // if an hint has been specified, we accomodate more space for it and 1412 | // add a text view in the drop down menu, at the bottom of the list 1413 | LinearLayout hintContainer = new LinearLayout(context); 1414 | hintContainer.setOrientation(LinearLayout.VERTICAL); 1415 | 1416 | LinearLayout.LayoutParams hintParams = new LinearLayout.LayoutParams( 1417 | ViewGroup.LayoutParams.MATCH_PARENT, 0, 1.0f); 1418 | hintContainer.addView(dropDownView, hintParams); 1419 | hintContainer.addView(hintView); 1420 | 1421 | // measure the hint's height to find how much more vertical space 1422 | // we need to add to the drop down's height 1423 | int widthSpec = MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST); 1424 | int heightSpec = MeasureSpec.UNSPECIFIED; 1425 | hintView.measure(widthSpec, heightSpec); 1426 | 1427 | hintParams = (LinearLayout.LayoutParams) hintView.getLayoutParams(); 1428 | otherHeights = hintView.getMeasuredHeight() + hintParams.topMargin + hintParams.bottomMargin; 1429 | 1430 | dropDownView = hintContainer; 1431 | } 1432 | 1433 | mPopup.setContentView(dropDownView); 1434 | } else { 1435 | dropDownView = (ViewGroup) mPopup.getContentView(); 1436 | final View view = dropDownView.findViewById(HINT_VIEW_ID); 1437 | if (view != null) { 1438 | LinearLayout.LayoutParams hintParams = (LinearLayout.LayoutParams) view.getLayoutParams(); 1439 | otherHeights = view.getMeasuredHeight() + hintParams.topMargin + hintParams.bottomMargin; 1440 | } 1441 | } 1442 | 1443 | // Max height available on the screen for a popup. 1444 | boolean ignoreBottomDecorations = mPopup.getInputMethodMode() == PopupWindow.INPUT_METHOD_NOT_NEEDED; 1445 | final int maxHeight = mPopup.getMaxAvailableHeightCompat(getDropDownAnchorView(), mDropDownVerticalOffset, 1446 | ignoreBottomDecorations); 1447 | 1448 | // getMaxAvailableHeight() subtracts the padding, so we put it back, 1449 | // to get the available height for the whole window 1450 | int padding = 0; 1451 | Drawable background = mPopup.getBackground(); 1452 | if (background != null) { 1453 | background.getPadding(mTempRect); 1454 | padding = mTempRect.top + mTempRect.bottom; 1455 | } 1456 | 1457 | if (false || mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) { 1458 | return maxHeight + padding; 1459 | } 1460 | 1461 | final int listContent = mDropDownList.measureHeightOfChildrenCompat(MeasureSpec.UNSPECIFIED, 0, 1462 | FroyoListView.NO_POSITION, maxHeight - otherHeights, 2); 1463 | // add padding only if the list has items in it, that way we don't show 1464 | // the popup if it is not needed 1465 | if (listContent > 0) 1466 | otherHeights += padding; 1467 | 1468 | return listContent + otherHeights; 1469 | } 1470 | 1471 | protected void rebuildCombinedAdapter() { 1472 | ListAdapter adapter = getCombinedAdapter(mUserAdapter); 1473 | setCombinedAdapter(adapter); 1474 | } 1475 | 1476 | /** 1477 | * Returns true if the amount of text in the field meets or 1478 | * exceeds the {@link #getThreshold} requirement. You can override this to 1479 | * impose a different standard for when filtering will be triggered. 1480 | */ 1481 | protected boolean enoughToFilter() { 1482 | if (DEBUG) 1483 | Log.v(TAG, "Enough to filter: len=" + getText().length() + " threshold=" + mThreshold); 1484 | return getText().length() >= mThreshold; 1485 | } 1486 | 1487 | // Zenlibs 1488 | protected abstract ListAdapter getCombinedAdapter(ListAdapter userAdapter); 1489 | 1490 | private View getHintView(Context context) { 1491 | if (mHintText != null && mHintText.length() > 0) { 1492 | final TextView hintView = (TextView) LayoutInflater.from(context).inflate(mHintResource, null) 1493 | .findViewById(android.R.id.text1); 1494 | hintView.setText(mHintText); 1495 | hintView.setId(HINT_VIEW_ID); 1496 | return hintView; 1497 | } else { 1498 | return null; 1499 | } 1500 | } 1501 | 1502 | /** 1503 | * Sets the validator used to perform text validation. 1504 | * 1505 | * @param validator 1506 | * The validator used to validate the text entered in this 1507 | * widget. 1508 | * 1509 | * @see #getValidator() 1510 | * @see #performValidation() 1511 | */ 1512 | public void setValidator(Validator validator) { 1513 | mValidator = validator; 1514 | } 1515 | 1516 | /** 1517 | * Returns the Validator set with {@link #setValidator}, or 1518 | * null if it was not set. 1519 | * 1520 | * @see #setValidator(AbsHistoryEditText.Validator) 1521 | * @see #performValidation() 1522 | */ 1523 | public Validator getValidator() { 1524 | return mValidator; 1525 | } 1526 | 1527 | /** 1528 | * If a validator was set on this view and the current string is not valid, 1529 | * ask the validator to fix it. 1530 | * 1531 | * @see #getValidator() 1532 | * @see #setValidator(AbsHistoryEditText.Validator) 1533 | */ 1534 | public void performValidation() { 1535 | if (mValidator == null) 1536 | return; 1537 | 1538 | CharSequence text = getText(); 1539 | 1540 | if (!TextUtils.isEmpty(text) && !mValidator.isValid(text)) { 1541 | setText(mValidator.fixText(text)); 1542 | } 1543 | } 1544 | 1545 | /** 1546 | * Returns the Filter obtained from {@link Filterable#getFilter}, or 1547 | * null if {@link #setAdapter} was not called with a 1548 | * Filterable. 1549 | */ 1550 | protected Filter getFilter() { 1551 | return mFilter; 1552 | } 1553 | 1554 | private class ListSelectorHider implements Runnable { 1555 | public void run() { 1556 | clearListSelection(); 1557 | } 1558 | } 1559 | 1560 | private class ResizePopupRunnable implements Runnable { 1561 | public void run() { 1562 | mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); 1563 | showDropDown(); 1564 | } 1565 | } 1566 | 1567 | private class PopupTouchInterceptor implements OnTouchListener { 1568 | public boolean onTouch(View v, MotionEvent event) { 1569 | final int action = event.getAction(); 1570 | if (action == MotionEvent.ACTION_DOWN && mPopup != null && mPopup.isShowing()) { 1571 | postDelayed(mResizePopupRunnable, EXPAND_LIST_TIMEOUT); 1572 | } else if (action == MotionEvent.ACTION_UP) { 1573 | removeCallbacks(mResizePopupRunnable); 1574 | } 1575 | return false; 1576 | } 1577 | } 1578 | 1579 | private class PopupScrollListener implements ListView.OnScrollListener { 1580 | public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { 1581 | 1582 | } 1583 | 1584 | public void onScrollStateChanged(AbsListView view, int scrollState) { 1585 | if (scrollState == SCROLL_STATE_TOUCH_SCROLL && !isInputMethodNotNeeded() 1586 | && mPopup.getContentView() != null) { 1587 | removeCallbacks(mResizePopupRunnable); 1588 | mResizePopupRunnable.run(); 1589 | } 1590 | } 1591 | } 1592 | 1593 | private class DropDownItemClickListener implements AdapterView.OnItemClickListener { 1594 | public void onItemClick(AdapterView parent, View v, int position, long id) { 1595 | performCompletion(v, position, id); 1596 | } 1597 | } 1598 | 1599 | /** 1600 | *

1601 | * Wrapper class for a ListView. This wrapper hijacks the focus to make sure 1602 | * the list uses the appropriate drawables and states when displayed on 1603 | * screen within a drop down. The focus is never actually passed to the drop 1604 | * down; the list only looks focused. 1605 | *

1606 | */ 1607 | private static class DropDownListView extends FroyoListView { 1608 | /* 1609 | * WARNING: This is a workaround for a touch mode issue. 1610 | * 1611 | * Touch mode is propagated lazily to windows. This causes problems in 1612 | * the following scenario: 1613 | * - Type something in the AutoCompleteTextView and get some results 1614 | * - Move down with the d-pad to select an item in the list 1615 | * - Move up with the d-pad until the selection disappears 1616 | * - Type more text in the AutoCompleteTextView *using the soft keyboard* 1617 | * and get new results; you are now in touch mode 1618 | * - The selection comes back on the first item in the list, even though 1619 | * the list is supposed to be in touch mode 1620 | * 1621 | * Using the soft keyboard triggers the touch mode change but that change 1622 | * is propagated to our window only after the first list layout, therefore 1623 | * after the list attempts to resurrect the selection. 1624 | * 1625 | * The trick to work around this issue is to pretend the list is in touch 1626 | * mode when we know that the selection should not appear, that is when 1627 | * we know the user moved the selection away from the list. 1628 | * 1629 | * This boolean is set to true whenever we explicitely hide the list's 1630 | * selection and reset to false whenver we know the user moved the 1631 | * selection back to the list. 1632 | * 1633 | * When this boolean is true, isInTouchMode() returns true, otherwise it 1634 | * returns super.isInTouchMode(). 1635 | */ 1636 | private boolean mListSelectionHidden; 1637 | 1638 | /** 1639 | *

1640 | * Creates a new list view wrapper. 1641 | *

1642 | * 1643 | * @param context 1644 | * this view's context 1645 | */ 1646 | public DropDownListView(Context context) { 1647 | super(context, null, android.R.attr.dropDownListViewStyle); 1648 | } 1649 | 1650 | /** 1651 | *

1652 | * Avoids jarring scrolling effect by ensuring that list elements made 1653 | * of a text view fit on a single line. 1654 | *

1655 | * 1656 | * @param position 1657 | * the item index in the list to get a view for 1658 | * @return the view for the specified item 1659 | */ 1660 | @Override 1661 | View obtainView(int position, boolean[] isScrap) { 1662 | View view = super.obtainView(position, isScrap); 1663 | 1664 | if (view instanceof TextView) { 1665 | ((TextView) view).setHorizontallyScrolling(true); 1666 | } 1667 | 1668 | return view; 1669 | } 1670 | 1671 | @Override 1672 | public boolean isInTouchMode() { 1673 | // WARNING: Please read the comment where mListSelectionHidden is declared 1674 | return mListSelectionHidden || super.isInTouchMode(); 1675 | } 1676 | 1677 | /** 1678 | *

1679 | * Returns the focus state in the drop down. 1680 | *

1681 | * 1682 | * @return true always 1683 | */ 1684 | @Override 1685 | public boolean hasWindowFocus() { 1686 | return true; 1687 | } 1688 | 1689 | /** 1690 | *

1691 | * Returns the focus state in the drop down. 1692 | *

1693 | * 1694 | * @return true always 1695 | */ 1696 | @Override 1697 | public boolean isFocused() { 1698 | return true; 1699 | } 1700 | 1701 | /** 1702 | *

1703 | * Returns the focus state in the drop down. 1704 | *

1705 | * 1706 | * @return true always 1707 | */ 1708 | @Override 1709 | public boolean hasFocus() { 1710 | return true; 1711 | } 1712 | 1713 | protected int[] onCreateDrawableState(int extraSpace) { 1714 | int[] res = super.onCreateDrawableState(extraSpace); 1715 | //noinspection ConstantIfStatement 1716 | if (false) { 1717 | StringBuilder sb = new StringBuilder("Created drawable state: ["); 1718 | for (int i = 0; i < res.length; i++) { 1719 | if (i > 0) 1720 | sb.append(", "); 1721 | sb.append("0x"); 1722 | sb.append(Integer.toHexString(res[i])); 1723 | } 1724 | sb.append("]"); 1725 | Log.i(TAG, sb.toString()); 1726 | } 1727 | return res; 1728 | } 1729 | } 1730 | 1731 | /** 1732 | * This interface is used to make sure that the text entered in this 1733 | * TextView complies to a certain format. Since there is no foolproof way to 1734 | * prevent the user from leaving this View with an incorrect value in it, 1735 | * all we can do is try to fix it ourselves when this happens. 1736 | */ 1737 | public interface Validator { 1738 | /** 1739 | * Validates the specified text. 1740 | * 1741 | * @return true If the text currently in the text editor is valid. 1742 | * 1743 | * @see #fixText(CharSequence) 1744 | */ 1745 | boolean isValid(CharSequence text); 1746 | 1747 | /** 1748 | * Corrects the specified text to make it valid. 1749 | * 1750 | * @param invalidText 1751 | * A string that doesn't pass validation: 1752 | * isValid(invalidText) returns false 1753 | * 1754 | * @return A string based on invalidText such as invoking isValid() on 1755 | * it returns true. 1756 | * 1757 | * @see #isValid(CharSequence) 1758 | */ 1759 | CharSequence fixText(CharSequence invalidText); 1760 | } 1761 | 1762 | /** 1763 | * Allows us a private hook into the on click event without preventing users 1764 | * from setting their own click listener. 1765 | */ 1766 | private class PassThroughClickListener implements OnClickListener { 1767 | 1768 | private View.OnClickListener mWrapped; 1769 | 1770 | /** {@inheritDoc} */ 1771 | public void onClick(View v) { 1772 | onClickImpl(); 1773 | 1774 | if (mWrapped != null) 1775 | mWrapped.onClick(v); 1776 | } 1777 | } 1778 | 1779 | private class PopupDataSetObserver extends DataSetObserver { 1780 | @Override 1781 | public void onChanged() { 1782 | if (isPopupShowing()) { 1783 | // This will resize the popup to fit the new adapter's content 1784 | showDropDown(); 1785 | } else if (mCombinedAdapter != null) { 1786 | // If the popup is not showing already, showing it will cause 1787 | // the list of data set observers attached to the adapter to 1788 | // change. We can't do it from here, because we are in the middle 1789 | // of iterating throught he list of observers. 1790 | post(new Runnable() { 1791 | public void run() { 1792 | final ListAdapter adapter = mCombinedAdapter; 1793 | if (adapter != null) { 1794 | updateDropDownForFilter(adapter.getCount(), false); 1795 | } 1796 | } 1797 | }); 1798 | } 1799 | } 1800 | 1801 | @Override 1802 | public void onInvalidated() { 1803 | if (true) { 1804 | // There's no data to display so make sure we're not showing 1805 | // the drop down and its list 1806 | dismissDropDown(); 1807 | } 1808 | } 1809 | } 1810 | } -------------------------------------------------------------------------------- /library/src/com/zenlibs/historyedittext/FroyoListView.java: -------------------------------------------------------------------------------- 1 | package com.zenlibs.historyedittext; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.View; 6 | import android.view.ViewDebug; 7 | import android.widget.AbsListView; 8 | import android.widget.ListAdapter; 9 | import android.widget.ListView; 10 | 11 | import java.lang.reflect.Field; 12 | import java.lang.reflect.InvocationTargetException; 13 | import java.lang.reflect.Method; 14 | 15 | public class FroyoListView extends ListView { 16 | /** 17 | * Used to indicate a no preference for a position type. 18 | */ 19 | public static final int NO_POSITION = -1; 20 | private boolean mAreAllItemsSelectable; 21 | private Method mHideSelectorMethod; 22 | private Object mRecycler; 23 | private Method mGetScrapViewMethod; 24 | private Method mAddScrapViewMethod; 25 | private Method mDispatchFinishTemporaryDispatchMethod; 26 | private Method mMeasureHeightOfChildren; 27 | 28 | public FroyoListView(Context context) { 29 | super(context); 30 | init(); 31 | } 32 | 33 | public FroyoListView(Context context, AttributeSet attrs) { 34 | super(context, attrs); 35 | init(); 36 | } 37 | 38 | public FroyoListView(Context context, AttributeSet attrs, int defStyle) { 39 | super(context, attrs, defStyle); 40 | init(); 41 | } 42 | 43 | private void init() { 44 | try { 45 | // This is dangerous, whenever a new version of Android is released we should 46 | // check that this method still exists and that it hasn't 47 | mHideSelectorMethod = AbsListView.class.getDeclaredMethod("hideSelector"); 48 | mHideSelectorMethod.setAccessible(true); 49 | mDispatchFinishTemporaryDispatchMethod = View.class.getDeclaredMethod("dispatchFinishTemporaryDetach"); 50 | mDispatchFinishTemporaryDispatchMethod.setAccessible(true); 51 | mMeasureHeightOfChildren = ListView.class.getDeclaredMethod("measureHeightOfChildren", int.class, int.class, int.class, int.class, int.class); 52 | mMeasureHeightOfChildren.setAccessible(true); 53 | 54 | Field recyclerField = AbsListView.class.getDeclaredField("mRecycler"); 55 | recyclerField.setAccessible(true); 56 | mRecycler = recyclerField.get(this); 57 | Class[] classes = AbsListView.class.getDeclaredClasses(); 58 | for (Class class_ : classes) { 59 | if (class_.getSimpleName().equals("RecycleBin")) { 60 | Class recycleBinClass = class_; 61 | mGetScrapViewMethod = recycleBinClass.getDeclaredMethod("getScrapView", int.class); 62 | mGetScrapViewMethod.setAccessible(true); 63 | mAddScrapViewMethod = recycleBinClass.getDeclaredMethod("addScrapView", View.class, int.class); 64 | mAddScrapViewMethod.setAccessible(true); 65 | break; 66 | } 67 | } 68 | } catch (Exception e) { 69 | e.printStackTrace(); 70 | } 71 | } 72 | 73 | @Override 74 | public void setAdapter(ListAdapter adapter) { 75 | super.setAdapter(adapter); 76 | if (adapter != null) { 77 | mAreAllItemsSelectable = adapter.areAllItemsEnabled(); 78 | } else { 79 | mAreAllItemsSelectable = true; 80 | } 81 | } 82 | 83 | int lookForSelectablePositionCompat(int position, boolean lookDown) { 84 | final ListAdapter adapter = getAdapter(); 85 | if (adapter == null || isInTouchMode()) { 86 | return INVALID_POSITION; 87 | } 88 | 89 | final int count = adapter.getCount(); 90 | if (!mAreAllItemsSelectable) { 91 | if (lookDown) { 92 | position = Math.max(0, position); 93 | while (position < count && !adapter.isEnabled(position)) { 94 | position++; 95 | } 96 | } else { 97 | position = Math.min(position, count - 1); 98 | while (position >= 0 && !adapter.isEnabled(position)) { 99 | position--; 100 | } 101 | } 102 | 103 | if (position < 0 || position >= count) { 104 | return INVALID_POSITION; 105 | } 106 | return position; 107 | } else { 108 | if (position < 0 || position >= count) { 109 | return INVALID_POSITION; 110 | } 111 | return position; 112 | } 113 | } 114 | 115 | void hideSelectorCompat() { 116 | try { 117 | mHideSelectorMethod.invoke(this); 118 | } catch (Exception e) { 119 | e.printStackTrace(); 120 | } 121 | } 122 | 123 | /** 124 | * Get a view and have it show the data associated with the specified 125 | * position. This is called when we have already discovered that the view is 126 | * not available for reuse in the recycle bin. The only choices left are 127 | * converting an old view or making a new one. 128 | * 129 | * @param position The position to display 130 | * @param isScrap Array of at least 1 boolean, the first entry will become true if 131 | * the returned view was taken from the scrap heap, false if otherwise. 132 | * 133 | * @return A view displaying the data associated with the specified position 134 | */ 135 | View obtainView(int position, boolean[] isScrap) { 136 | try { 137 | isScrap[0] = false; 138 | View scrapView; 139 | scrapView = (View) mGetScrapViewMethod.invoke(mRecycler, position); 140 | 141 | View child; 142 | ListAdapter adapter = getAdapter(); 143 | int cacheColorHint = getCacheColorHint(); 144 | if (scrapView != null) { 145 | if (ViewDebug.TRACE_RECYCLER) { 146 | ViewDebug.trace(scrapView, ViewDebug.RecyclerTraceType.RECYCLE_FROM_SCRAP_HEAP, 147 | position, -1); 148 | } 149 | 150 | child = adapter.getView(position, scrapView, this); 151 | 152 | if (ViewDebug.TRACE_RECYCLER) { 153 | ViewDebug.trace(child, ViewDebug.RecyclerTraceType.BIND_VIEW, 154 | position, getChildCount()); 155 | } 156 | if (child != scrapView) { 157 | mAddScrapViewMethod.invoke(mRecycler, scrapView); 158 | if (cacheColorHint != 0) { 159 | child.setDrawingCacheBackgroundColor(cacheColorHint); 160 | } 161 | if (ViewDebug.TRACE_RECYCLER) { 162 | ViewDebug.trace(scrapView, ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP, 163 | position, -1); 164 | } 165 | } else { 166 | isScrap[0] = true; 167 | mDispatchFinishTemporaryDispatchMethod.invoke(child); 168 | } 169 | } else { 170 | child = adapter.getView(position, null, this); 171 | if (cacheColorHint != 0) { 172 | child.setDrawingCacheBackgroundColor(cacheColorHint); 173 | } 174 | if (ViewDebug.TRACE_RECYCLER) { 175 | ViewDebug.trace(child, ViewDebug.RecyclerTraceType.NEW_VIEW, 176 | position, getChildCount()); 177 | } 178 | } 179 | return child; 180 | } catch (InvocationTargetException e) { 181 | e.printStackTrace(); 182 | } catch (IllegalAccessException e) { 183 | e.printStackTrace(); 184 | } 185 | return null; 186 | } 187 | 188 | public final int measureHeightOfChildrenCompat(int widthMeasureSpec, int startPosition, int endPosition, 189 | final int maxHeight, int disallowPartialChildPosition) { 190 | try { 191 | Object result = mMeasureHeightOfChildren.invoke(this, widthMeasureSpec, startPosition, endPosition, 192 | maxHeight, disallowPartialChildPosition); 193 | return (Integer) result; 194 | } catch (IllegalArgumentException e) { 195 | e.printStackTrace(); 196 | } catch (IllegalAccessException e) { 197 | e.printStackTrace(); 198 | } catch (InvocationTargetException e) { 199 | e.printStackTrace(); 200 | } catch (Exception e) { 201 | e.printStackTrace(); 202 | } 203 | 204 | return 0; 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /library/src/com/zenlibs/historyedittext/FroyoPopupWindow.java: -------------------------------------------------------------------------------- 1 | package com.zenlibs.historyedittext; 2 | 3 | import java.lang.reflect.InvocationTargetException; 4 | import java.lang.reflect.Method; 5 | 6 | import android.content.Context; 7 | import android.util.AttributeSet; 8 | import android.view.View; 9 | import android.widget.PopupWindow; 10 | 11 | public class FroyoPopupWindow extends PopupWindow { 12 | 13 | 14 | 15 | private Method mGetMaxAvailableHeightMethod; 16 | 17 | public FroyoPopupWindow() { 18 | super(); 19 | init(); 20 | } 21 | 22 | public FroyoPopupWindow(Context context, AttributeSet attrs, int defStyle) { 23 | super(context, attrs, defStyle); 24 | init(); 25 | } 26 | 27 | public FroyoPopupWindow(Context context, AttributeSet attrs) { 28 | super(context, attrs); 29 | init(); 30 | } 31 | 32 | public FroyoPopupWindow(Context context) { 33 | super(context); 34 | init(); 35 | } 36 | 37 | public FroyoPopupWindow(int width, int height) { 38 | super(width, height); 39 | init(); 40 | } 41 | 42 | public FroyoPopupWindow(View contentView, int width, int height, boolean focusable) { 43 | super(contentView, width, height, focusable); 44 | init(); 45 | } 46 | 47 | public FroyoPopupWindow(View contentView, int width, int height) { 48 | super(contentView, width, height); 49 | init(); 50 | } 51 | 52 | public FroyoPopupWindow(View contentView) { 53 | super(contentView); 54 | init(); 55 | } 56 | 57 | private void init() { 58 | try { 59 | mGetMaxAvailableHeightMethod = getClass().getMethod("getMaxAvailableHeight", View.class, int.class, boolean.class); 60 | } catch (NoSuchMethodException e) { 61 | e.printStackTrace(); 62 | } 63 | } 64 | 65 | public int getMaxAvailableHeightCompat(View anchor, int yOffset, boolean ignoreBottomDecorations) { 66 | try { 67 | return (Integer) mGetMaxAvailableHeightMethod.invoke(this, anchor, yOffset, ignoreBottomDecorations); 68 | } catch (IllegalArgumentException e) { 69 | e.printStackTrace(); 70 | } catch (IllegalAccessException e) { 71 | e.printStackTrace(); 72 | } catch (InvocationTargetException e) { 73 | e.printStackTrace(); 74 | } catch (Exception e) { 75 | e.printStackTrace(); 76 | } 77 | 78 | return 0; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /library/src/com/zenlibs/historyedittext/HistoryDb.java: -------------------------------------------------------------------------------- 1 | package com.zenlibs.historyedittext; 2 | 3 | import android.content.ContentValues; 4 | import android.content.Context; 5 | import android.database.Cursor; 6 | import android.database.sqlite.SQLiteDatabase; 7 | import android.database.sqlite.SQLiteOpenHelper; 8 | 9 | class HistoryDb { 10 | private static final String HISTORY_TABLE = "history"; 11 | private static final String COLUMN_ID = "_id"; 12 | private static final String COLUMN_TAG = "tag"; 13 | private static final String COLUMN_TEXT = "text"; 14 | private static final String COLUMN_TIME = "time"; 15 | private static final String[] QUERY_BY_TAG_COLUMNS = new String[] { COLUMN_TEXT }; 16 | private static final String QUERY_BY_TAG_SELECTION = COLUMN_TAG + "=?"; 17 | private static final String QUERY_BY_TAG_ORDER = COLUMN_TIME + " DESC"; 18 | private static final String QUERY_BY_TAG_TEXT_SELECTION = COLUMN_TAG + "=? AND " + COLUMN_TEXT + "=?"; 19 | private static HistoryDbSQLiteHelper mHelper; 20 | 21 | public static void clear(SQLiteDatabase db) { 22 | db.delete(HISTORY_TABLE, null, null); 23 | } 24 | 25 | public static void insertEntry(SQLiteDatabase db, String tag, String text) { 26 | if (text != null) { 27 | text = text.trim(); 28 | 29 | ContentValues values = new ContentValues(); 30 | values.put(COLUMN_TAG, tag); 31 | values.put(COLUMN_TEXT, text); 32 | values.put(COLUMN_TIME, System.currentTimeMillis()); 33 | 34 | // Test if it already exists 35 | Cursor c = queryByTagText(db, tag, text); 36 | if (c.getCount() > 0) { 37 | // It's an update 38 | db.update(HISTORY_TABLE, values, QUERY_BY_TAG_TEXT_SELECTION, new String[] { tag, text }); 39 | } else { 40 | // It's an insert 41 | db.insert(HISTORY_TABLE, null, values); 42 | } 43 | } 44 | } 45 | 46 | public static Cursor queryByTag(SQLiteDatabase db, String tag) { 47 | return db.query(HISTORY_TABLE, QUERY_BY_TAG_COLUMNS, QUERY_BY_TAG_SELECTION, new String[] { tag }, null, null, 48 | QUERY_BY_TAG_ORDER); 49 | } 50 | 51 | private static Cursor queryByTagText(SQLiteDatabase db, String tag, String text) { 52 | return db.query(HISTORY_TABLE, QUERY_BY_TAG_COLUMNS, QUERY_BY_TAG_TEXT_SELECTION, new String[] { tag, text }, 53 | null, null, QUERY_BY_TAG_ORDER); 54 | } 55 | 56 | static SQLiteDatabase getReadable(Context context) { 57 | HistoryDbSQLiteHelper helper = getHelper(context); 58 | return helper.getReadableDatabase(); 59 | } 60 | 61 | static SQLiteDatabase getWritable(Context context) { 62 | HistoryDbSQLiteHelper helper = getHelper(context); 63 | return helper.getWritableDatabase(); 64 | } 65 | 66 | private static HistoryDbSQLiteHelper getHelper(Context context) { 67 | if (mHelper == null) { 68 | mHelper = new HistoryDbSQLiteHelper(context); 69 | } 70 | return mHelper; 71 | } 72 | 73 | static class HistoryDbSQLiteHelper extends SQLiteOpenHelper { 74 | 75 | private static final String DATABASE_NAME = "historyedittext.db"; 76 | private static final int DATABASE_VERSION = 1; 77 | 78 | public HistoryDbSQLiteHelper(Context context) { 79 | super(context, DATABASE_NAME, null, DATABASE_VERSION); 80 | } 81 | 82 | @Override 83 | public void onCreate(SQLiteDatabase db) { 84 | db.execSQL("create table " + HISTORY_TABLE + "(" + COLUMN_ID + " integer primary key autoincrement, " 85 | + COLUMN_TAG + " text not null, " + COLUMN_TEXT + " text not null, " + COLUMN_TIME + " long);"); 86 | } 87 | 88 | @Override 89 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 90 | db.execSQL("drop table if exists " + HISTORY_TABLE); 91 | onCreate(db); 92 | } 93 | } 94 | 95 | public static String getText(Cursor c) { 96 | return Zen.getTextColumn(c, COLUMN_TEXT); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /library/src/com/zenlibs/historyedittext/HistoryEditText.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Zenlibs 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 | 17 | package com.zenlibs.historyedittext; 18 | 19 | import android.content.Context; 20 | import android.content.res.TypedArray; 21 | import android.database.Cursor; 22 | import android.database.sqlite.SQLiteDatabase; 23 | import android.graphics.Rect; 24 | import android.text.TextUtils; 25 | import android.util.AttributeSet; 26 | import android.widget.ArrayAdapter; 27 | import android.widget.Filter; 28 | import android.widget.Filter.FilterListener; 29 | import android.widget.Filterable; 30 | import android.widget.ListAdapter; 31 | import com.commonsware.cwac.merge.MergeAdapter; 32 | 33 | public class HistoryEditText extends AbsHistoryEditText { 34 | 35 | private boolean mFirstFiltering = true; 36 | private ListAdapter mHistoryAdapter; 37 | private Filter mHistoryFilter; 38 | private int mMaxHistoryValues; 39 | 40 | public HistoryEditText(Context context, AttributeSet attrs, int defStyle) { 41 | super(context, attrs, defStyle); 42 | parseAttrs(context, attrs, defStyle); 43 | } 44 | 45 | private void parseAttrs(Context context, AttributeSet attrs, int defStyle) { 46 | 47 | // Getting max history values 48 | TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.HistoryEditText, defStyle, 49 | R.style.Widget_HistoryEditText); 50 | 51 | mMaxHistoryValues = a.getInt(R.styleable.HistoryEditText_maxHistoryValues, 5); 52 | 53 | a.recycle(); 54 | } 55 | 56 | public HistoryEditText(Context context, AttributeSet attrs) { 57 | super(context, attrs); 58 | parseAttrs(context, attrs, android.R.attr.autoCompleteTextViewStyle); 59 | } 60 | 61 | public HistoryEditText(Context context) { 62 | super(context); 63 | } 64 | 65 | public void clearHistory() { 66 | boolean wasShowing = isPopupShowing(); 67 | SQLiteDatabase db = HistoryDb.getWritable(getContext()); 68 | HistoryDb.clear(db); 69 | db.close(); 70 | rebuildHistoryAdapter(); 71 | if (wasShowing) { 72 | showDropDown(); 73 | } 74 | } 75 | 76 | @Override 77 | protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { 78 | super.onFocusChanged(focused, direction, previouslyFocusedRect); 79 | if (focused) { 80 | performFiltering(null, -1); 81 | } 82 | } 83 | 84 | @Override 85 | public void onEditorAction(int actionCode) { 86 | super.onEditorAction(actionCode); 87 | if (actionCode == getImeOptions()) { 88 | addCurrentTextToHistory(); 89 | rebuildHistoryAdapter(); 90 | } 91 | } 92 | 93 | private void setHistoryAdapter(T adapter) { 94 | mHistoryAdapter = adapter; 95 | if (adapter != null) { 96 | mHistoryFilter = adapter.getFilter(); 97 | } 98 | rebuildCombinedAdapter(); 99 | } 100 | 101 | @Override 102 | protected void performFiltering(final CharSequence text, final int keyCode) { 103 | if (mFirstFiltering) { 104 | rebuildHistoryAdapter(); 105 | mFirstFiltering = false; 106 | } 107 | if (text == null) { 108 | HistoryEditText.super.performFiltering(text, keyCode); 109 | } else if (mHistoryFilter != null) { 110 | mHistoryFilter.filter(text, new FilterListener() { 111 | @Override 112 | public void onFilterComplete(int count) { 113 | HistoryEditText.super.performFiltering(text, keyCode); 114 | } 115 | }); 116 | } 117 | } 118 | 119 | @Override 120 | protected ListAdapter getCombinedAdapter(ListAdapter userAdapter) { 121 | if (mHistoryAdapter == null) { 122 | if (enoughToFilter()) { 123 | return userAdapter; 124 | } else { 125 | return null; 126 | } 127 | } else { 128 | if (enoughToFilter() && userAdapter != null) { 129 | MergeAdapter mergeAdapter = new MergeAdapter(); 130 | mergeAdapter.addAdapter(mHistoryAdapter); 131 | mergeAdapter.addAdapter(userAdapter); 132 | return mergeAdapter; 133 | } else { 134 | return mHistoryAdapter; 135 | } 136 | } 137 | } 138 | 139 | private void rebuildHistoryAdapter() { 140 | SQLiteDatabase db = HistoryDb.getReadable(getContext()); 141 | Cursor c = HistoryDb.queryByTag(db, (String) getTag()); 142 | int count = Math.min(c.getCount(), mMaxHistoryValues); 143 | if (count == 0) { 144 | ArrayAdapter adapter = null; 145 | setHistoryAdapter(adapter); 146 | } else { 147 | String[] items = new String[count]; 148 | int i = 0; 149 | while (c.moveToNext() && i < count) { 150 | items[i++] = HistoryDb.getText(c); 151 | } 152 | c.close(); 153 | db.close(); 154 | int itemLayout = R.layout.het__dropdown_history_item; 155 | ArrayAdapter adapter = new ArrayAdapter(getContext(), itemLayout, items); 156 | setHistoryAdapter(adapter); 157 | } 158 | } 159 | 160 | private void addCurrentTextToHistory() { 161 | String text = getCurrentText(); 162 | if (!TextUtils.isEmpty(text)) { 163 | addTextToHistory(text); 164 | } 165 | } 166 | 167 | private void addTextToHistory(String text) { 168 | SQLiteDatabase db = HistoryDb.getWritable(getContext()); 169 | HistoryDb.insertEntry(db, (String) getTag(), text); 170 | db.close(); 171 | } 172 | 173 | private String getCurrentText() { 174 | return getText().toString().trim(); 175 | } 176 | } -------------------------------------------------------------------------------- /library/src/com/zenlibs/historyedittext/Zen.java: -------------------------------------------------------------------------------- 1 | package com.zenlibs.historyedittext; 2 | 3 | import android.content.Context; 4 | import android.database.Cursor; 5 | import android.widget.Toast; 6 | 7 | public class Zen { 8 | public static void longToast(Context ctx, String fmt, Object ... args) { 9 | Toast.makeText(ctx, String.format(fmt, args), Toast.LENGTH_LONG).show(); 10 | } 11 | 12 | public static void longToast(Context ctx, int resId, Object ... args) { 13 | longToast(ctx, ctx.getString(resId), args); 14 | } 15 | 16 | public static void shortToast(Context ctx, String fmt, Object ... args) { 17 | Toast.makeText(ctx, String.format(fmt, args), Toast.LENGTH_SHORT).show(); 18 | } 19 | 20 | public static void shortToast(Context ctx, int resId, Object ... args) { 21 | shortToast(ctx, ctx.getString(resId), args); 22 | } 23 | 24 | public static String getTextColumn(Cursor c, String columnName) { 25 | int index = c.getColumnIndex(columnName); 26 | return c.getString(index); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 4.0.0 5 | 6 | 7 | org.sonatype.oss 8 | oss-parent 9 | 7 10 | 11 | 12 | com.zenlibs.historyedittext 13 | parent 14 | pom 15 | 1.0.0-SNAPSHOT 16 | 17 | Android-HistoryEditText (Parent) 18 | Android library for. 19 | https://github.com/zenlibs/HistoryEditText/ 20 | 2013 21 | 22 | 23 | library 24 | sample 25 | 26 | 27 | 28 | https://github.com/zenlibs/HistoryEditText/ 29 | https://github.com/zenlibs/HistoryEditText.git 30 | scm:git:git@github.com:zenlibs/HistoryEditText.git 31 | 32 | 33 | 34 | 35 | Manuel Peinado 36 | manuelpeinado@gmail.com 37 | manuelpeinado 38 | http://github.com/ManuelPeinado 39 | 0 40 | 41 | developer 42 | 43 | 44 | 45 | Antonio Leiva 46 | antonioleivag@gmail.com 47 | antoniolg 48 | http://github.com/antoniolg 49 | 0 50 | 51 | developer 52 | 53 | 54 | 55 | 56 | 57 | 58 | Apache License Version 2.0 59 | http://www.apache.org/licenses/LICENSE-2.0.txt 60 | repo 61 | 62 | 63 | 64 | 65 | Zenlibs 66 | http://www.zenlibs.com 67 | 68 | 69 | 70 | GitHub Issues 71 | https://github.com/ManuelPeinado/HistoryEditText/issues 72 | 73 | 74 | 75 | UTF-8 76 | UTF-8 77 | 78 | 1.6 79 | 4.1.1.4 80 | 16 81 | r7 82 | 83 | 3.3.0 84 | 85 | 86 | 87 | 88 | 89 | com.google.android 90 | android 91 | ${android.version} 92 | 93 | 94 | 95 | com.google.android 96 | support-v4 97 | ${android.support.version} 98 | 99 | 100 | 101 | com.actionbarsherlock 102 | actionbarsherlock 103 | 4.2.0 104 | apklib 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | org.apache.maven.plugins 115 | maven-compiler-plugin 116 | 2.3.2 117 | 118 | ${java.version} 119 | ${java.version} 120 | 121 | 122 | 123 | 124 | com.jayway.maven.plugins.android.generation2 125 | android-maven-plugin 126 | ${android-maven.version} 127 | 128 | 129 | ${android.platform} 130 | 131 | true 132 | 133 | true 134 | 135 | 136 | 137 | org.apache.maven.plugins 138 | maven-checkstyle-plugin 139 | 2.6 140 | 141 | true 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | org.apache.maven.plugins 150 | maven-release-plugin 151 | 2.1 152 | 153 | true 154 | 155 | 156 | 157 | 158 | org.apache.maven.plugins 159 | maven-eclipse-plugin 160 | 2.9 161 | 162 | 163 | android:android 164 | com.google.android.maps:maps 165 | 166 | bin 167 | 168 | com.android.ide.eclipse.adt.ANDROID_FRAMEWORK 169 | 170 | 171 | com.android.ide.eclipse.adt.AndroidNature 172 | org.eclipse.ajdt.ui.ajnature 173 | 174 | 175 | com.android.ide.eclipse.adt.ResourceManagerBuilder 176 | com.android.ide.eclipse.adt.PreCompilerBuilder 177 | org.eclipse.jdt.core.javabuilder 178 | com.android.ide.eclipse.adt.ApkBuilder 179 | 180 | false 181 | 182 | 183 | 184 | 185 | -------------------------------------------------------------------------------- /sample/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 28 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /sample/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenlibs/HistoryEditText/311294b7d84323758d7bf729dd27ad8ce7c4e2fb/sample/ic_launcher-web.png -------------------------------------------------------------------------------- /sample/libs/android-support-v4.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenlibs/HistoryEditText/311294b7d84323758d7bf729dd27ad8ce7c4e2fb/sample/libs/android-support-v4.jar -------------------------------------------------------------------------------- /sample/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 4.0.0 5 | 6 | sample 7 | HistoryEditText Sample 8 | apk 9 | 10 | 11 | com.zenlibs.historyedittext 12 | parent 13 | 1.0.0-SNAPSHOT 14 | ../pom.xml 15 | 16 | 17 | 18 | 19 | com.google.android 20 | android 21 | provided 22 | 23 | 24 | 25 | com.google.android 26 | support-v4 27 | 28 | 29 | 30 | com.zenlibs.historyedittext 31 | library 32 | ${project.version} 33 | apklib 34 | 35 | 36 | 37 | 38 | 39 | src 40 | ${project.artifactId}-unaligned 41 | 42 | 43 | 44 | com.jayway.maven.plugins.android.generation2 45 | android-maven-plugin 46 | true 47 | 48 | 49 | 50 | 51 | 52 | 53 | release 54 | 55 | 56 | performRelease 57 | true 58 | 59 | 60 | 61 | 62 | 63 | com.jayway.maven.plugins.android.generation2 64 | android-maven-plugin 65 | ${android-maven.version} 66 | true 67 | 68 | 69 | ${project.build.directory}/${project.build.finalName}.apk 70 | ${project.build.directory}/${project.artifactId}.apk 71 | 72 | 73 | 74 | 75 | alignApk 76 | package 77 | 78 | zipalign 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /sample/proguard-project.txt: -------------------------------------------------------------------------------- 1 | # To enable ProGuard in your project, edit project.properties 2 | # to define the proguard.config property as described in that file. 3 | # 4 | # Add project specific ProGuard rules here. 5 | # By default, the flags in this file are appended to flags specified 6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt 7 | # You can edit the include path and order by changing the ProGuard 8 | # include property in project.properties. 9 | # 10 | # For more details, see 11 | # http://developer.android.com/guide/developing/tools/proguard.html 12 | 13 | # Add any project specific keep options here: 14 | 15 | # If your project uses WebView with JS, uncomment the following 16 | # and specify the fully qualified class name to the JavaScript interface 17 | # class: 18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 19 | # public *; 20 | #} 21 | -------------------------------------------------------------------------------- /sample/project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system edit 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | # 10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): 11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt 12 | 13 | # Project target. 14 | target=android-17 15 | android.library.reference.1=../library 16 | android.library.reference.2=../../ActionBarSherlock 17 | -------------------------------------------------------------------------------- /sample/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenlibs/HistoryEditText/311294b7d84323758d7bf729dd27ad8ce7c4e2fb/sample/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenlibs/HistoryEditText/311294b7d84323758d7bf729dd27ad8ce7c4e2fb/sample/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/res/drawable-xhdpi/ic_action_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenlibs/HistoryEditText/311294b7d84323758d7bf729dd27ad8ce7c4e2fb/sample/res/drawable-xhdpi/ic_action_search.png -------------------------------------------------------------------------------- /sample/res/drawable-xhdpi/ic_content_import_export.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenlibs/HistoryEditText/311294b7d84323758d7bf729dd27ad8ce7c4e2fb/sample/res/drawable-xhdpi/ic_content_import_export.png -------------------------------------------------------------------------------- /sample/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenlibs/HistoryEditText/311294b7d84323758d7bf729dd27ad8ce7c4e2fb/sample/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/res/drawable-xhdpi/ic_location_directions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenlibs/HistoryEditText/311294b7d84323758d7bf729dd27ad8ce7c4e2fb/sample/res/drawable-xhdpi/ic_location_directions.png -------------------------------------------------------------------------------- /sample/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zenlibs/HistoryEditText/311294b7d84323758d7bf729dd27ad8ce7c4e2fb/sample/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/res/layout/activity_home.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | -------------------------------------------------------------------------------- /sample/res/layout/activity_no_user_adapter.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 22 | 23 | 28 | 29 | 37 | 38 | -------------------------------------------------------------------------------- /sample/res/layout/activity_simple.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 22 | 23 | 28 | 29 | 35 | 36 | 41 | 42 | 50 | 51 | -------------------------------------------------------------------------------- /sample/res/menu/activity_simple.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | -------------------------------------------------------------------------------- /sample/res/raw/countries: -------------------------------------------------------------------------------- 1 | Afghanistan 2 | Albania 3 | Algeria 4 | Andorra 5 | Angola 6 | Antigua & Deps 7 | Argentina 8 | Armenia 9 | Australia 10 | Austria 11 | Azerbaijan 12 | Bahamas 13 | Bahrain 14 | Bangladesh 15 | Barbados 16 | Belarus 17 | Belgium 18 | Belize 19 | Benin 20 | Bhutan 21 | Bolivia 22 | Bosnia Herzegovina 23 | Botswana 24 | Brazil 25 | Brunei 26 | Bulgaria 27 | Burkina 28 | Burundi 29 | Cambodia 30 | Cameroon 31 | Canada 32 | Cape Verde 33 | Central African Rep 34 | Chad 35 | Chile 36 | China 37 | Colombia 38 | Comoros 39 | Congo 40 | Congo {Democratic Rep} 41 | Costa Rica 42 | Croatia 43 | Cuba 44 | Cyprus 45 | Czech Republic 46 | Denmark 47 | Djibouti 48 | Dominica 49 | Dominican Republic 50 | East Timor 51 | Ecuador 52 | Egypt 53 | El Salvador 54 | Equatorial Guinea 55 | Eritrea 56 | Estonia 57 | Ethiopia 58 | Fiji 59 | Finland 60 | France 61 | Gabon 62 | Gambia 63 | Georgia 64 | Germany 65 | Ghana 66 | Greece 67 | Grenada 68 | Guatemala 69 | Guinea 70 | Guinea-Bissau 71 | Guyana 72 | Haiti 73 | Honduras 74 | Hungary 75 | Iceland 76 | India 77 | Indonesia 78 | Iran 79 | Iraq 80 | Ireland {Republic} 81 | Israel 82 | Italy 83 | Ivory Coast 84 | Jamaica 85 | Japan 86 | Jordan 87 | Kazakhstan 88 | Kenya 89 | Kiribati 90 | Korea North 91 | Korea South 92 | Kosovo 93 | Kuwait 94 | Kyrgyzstan 95 | Laos 96 | Latvia 97 | Lebanon 98 | Lesotho 99 | Liberia 100 | Libya 101 | Liechtenstein 102 | Lithuania 103 | Luxembourg 104 | Macedonia 105 | Madagascar 106 | Malawi 107 | Malaysia 108 | Maldives 109 | Mali 110 | Malta 111 | Marshall Islands 112 | Mauritania 113 | Mauritius 114 | Mexico 115 | Micronesia 116 | Moldova 117 | Monaco 118 | Mongolia 119 | Montenegro 120 | Morocco 121 | Mozambique 122 | Myanmar, {Burma} 123 | Namibia 124 | Nauru 125 | Nepal 126 | Netherlands 127 | New Zealand 128 | Nicaragua 129 | Niger 130 | Nigeria 131 | Norway 132 | Oman 133 | Pakistan 134 | Palau 135 | Panama 136 | Papua New Guinea 137 | Paraguay 138 | Peru 139 | Philippines 140 | Poland 141 | Portugal 142 | Qatar 143 | Romania 144 | Russian Federation 145 | Rwanda 146 | St Kitts & Nevis 147 | St Lucia 148 | Saint Vincent & the Grenadines 149 | Samoa 150 | San Marino 151 | Sao Tome & Principe 152 | Saudi Arabia 153 | Senegal 154 | Serbia 155 | Seychelles 156 | Sierra Leone 157 | Singapore 158 | Slovakia 159 | Slovenia 160 | Solomon Islands 161 | Somalia 162 | South Africa 163 | South Sudan 164 | Spain 165 | Sri Lanka 166 | Sudan 167 | Suriname 168 | Swaziland 169 | Sweden 170 | Switzerland 171 | Syria 172 | Taiwan 173 | Tajikistan 174 | Tanzania 175 | Thailand 176 | Togo 177 | Tonga 178 | Trinidad & Tobago 179 | Tunisia 180 | Turkey 181 | Turkmenistan 182 | Tuvalu 183 | Uganda 184 | Ukraine 185 | United Arab Emirates 186 | United Kingdom 187 | United States 188 | Uruguay 189 | Uzbekistan 190 | Vanuatu 191 | Vatican City 192 | Venezuela 193 | Vietnam 194 | Yemen 195 | Zambia 196 | Zimbabwe 197 | -------------------------------------------------------------------------------- /sample/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | HistoryEditText Demo 5 | Basic usage 6 | No user adapter 7 | Clear history 8 | -------------------------------------------------------------------------------- /sample/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /sample/src/com/zenlibs/historyedittext/demo/ActivityInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Zenlibs 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 com.zenlibs.historyedittext.demo; 17 | 18 | import android.app.Activity; 19 | 20 | public class ActivityInfo { 21 | public Class activityClass; 22 | public int titleResourceId; 23 | 24 | public ActivityInfo(Class activityClass, int titleResourceId) { 25 | this.activityClass = activityClass; 26 | this.titleResourceId = titleResourceId; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /sample/src/com/zenlibs/historyedittext/demo/HomeActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 Zenlibs 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 com.zenlibs.historyedittext.demo; 17 | 18 | import java.util.Arrays; 19 | import java.util.List; 20 | 21 | import android.app.Activity; 22 | import android.content.Intent; 23 | import android.os.Bundle; 24 | import android.view.View; 25 | import android.widget.ArrayAdapter; 26 | import android.widget.ListView; 27 | 28 | import com.actionbarsherlock.app.SherlockListActivity; 29 | import com.zenlibs.historyedittext.demo.nouseradaptersample.NoUserAdapterActivity; 30 | import com.zenlibs.historyedittext.demo.simplehistorysample.SimpleHistoryActivity; 31 | 32 | public class HomeActivity extends SherlockListActivity { 33 | private List activitiesInfo = Arrays.asList( 34 | new ActivityInfo(SimpleHistoryActivity.class, R.string.activity_title_basic_usage), 35 | new ActivityInfo(NoUserAdapterActivity.class, R.string.activity_title_no_user_adapter) 36 | ); 37 | 38 | @Override 39 | protected void onCreate(Bundle savedInstanceState) { 40 | super.onCreate(savedInstanceState); 41 | setContentView(R.layout.activity_home); 42 | String[] titles = getActivityTitles(); 43 | setListAdapter(new ArrayAdapter( 44 | this, android.R.layout.simple_list_item_1, android.R.id.text1, titles)); 45 | } 46 | 47 | @Override 48 | protected void onListItemClick(ListView l, View v, int position, long id) { 49 | Class class_ = activitiesInfo.get(position).activityClass; 50 | Intent intent = new Intent(this, class_); 51 | startActivity(intent); 52 | } 53 | 54 | private String[] getActivityTitles() { 55 | String[] result = new String[activitiesInfo.size()]; 56 | int i = 0; 57 | for (ActivityInfo info : activitiesInfo) { 58 | result[i++] = getString(info.titleResourceId); 59 | } 60 | return result; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /sample/src/com/zenlibs/historyedittext/demo/nouseradaptersample/NoUserAdapterActivity.java: -------------------------------------------------------------------------------- 1 | package com.zenlibs.historyedittext.demo.nouseradaptersample; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.InputStreamReader; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | import android.os.Bundle; 11 | 12 | import com.actionbarsherlock.app.SherlockActivity; 13 | import com.actionbarsherlock.view.Menu; 14 | import com.actionbarsherlock.view.MenuInflater; 15 | import com.actionbarsherlock.view.MenuItem; 16 | import com.zenlibs.historyedittext.HistoryEditText; 17 | import com.zenlibs.historyedittext.demo.R; 18 | 19 | public class NoUserAdapterActivity extends SherlockActivity { 20 | 21 | private List mCountries = new ArrayList(); 22 | private HistoryEditText mHistoryEditText; 23 | private HistoryEditText mHistoryEditText2; 24 | 25 | @Override 26 | protected void onCreate(Bundle savedInstanceState) { 27 | super.onCreate(savedInstanceState); 28 | 29 | try { 30 | InputStream inputStream = getResources().openRawResource(R.raw.countries); 31 | BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); 32 | String line; 33 | while ((line = reader.readLine()) != null) { 34 | mCountries.add(line); 35 | } 36 | reader.close(); 37 | } catch (IOException e) { 38 | } 39 | 40 | setContentView(R.layout.activity_no_user_adapter); 41 | } 42 | 43 | @Override 44 | public boolean onCreateOptionsMenu(Menu menu) { 45 | new MenuInflater(this).inflate(R.menu.activity_simple, menu); 46 | return true; 47 | } 48 | 49 | @Override 50 | public boolean onOptionsItemSelected(MenuItem item) { 51 | if (item.getItemId() == R.id.action_clear_history) { 52 | clearHistory(); 53 | return true; 54 | } 55 | return false; 56 | } 57 | 58 | private void clearHistory() { 59 | mHistoryEditText.clearHistory(); 60 | mHistoryEditText2.clearHistory(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /sample/src/com/zenlibs/historyedittext/demo/simplehistorysample/SimpleHistoryActivity.java: -------------------------------------------------------------------------------- 1 | package com.zenlibs.historyedittext.demo.simplehistorysample; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.InputStreamReader; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | import android.os.Bundle; 11 | import android.view.View; 12 | import android.widget.ArrayAdapter; 13 | import android.widget.AutoCompleteTextView; 14 | 15 | import com.actionbarsherlock.app.SherlockActivity; 16 | import com.actionbarsherlock.view.Menu; 17 | import com.actionbarsherlock.view.MenuInflater; 18 | import com.actionbarsherlock.view.MenuItem; 19 | import com.zenlibs.historyedittext.HistoryEditText; 20 | import com.zenlibs.historyedittext.demo.R; 21 | 22 | public class SimpleHistoryActivity extends SherlockActivity { 23 | 24 | private List mCountries = new ArrayList(); 25 | private HistoryEditText mHistoryEditText; 26 | private HistoryEditText mHistoryEditText2; 27 | private AutoCompleteTextView autoCompleteTextView; 28 | 29 | @Override 30 | protected void onCreate(Bundle savedInstanceState) { 31 | super.onCreate(savedInstanceState); 32 | 33 | try { 34 | InputStream inputStream = getResources().openRawResource(R.raw.countries); 35 | BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); 36 | String line; 37 | while ((line = reader.readLine()) != null) { 38 | mCountries.add(line); 39 | } 40 | reader.close(); 41 | } catch (IOException e) { 42 | } 43 | 44 | setContentView(R.layout.activity_simple); 45 | 46 | mHistoryEditText = (HistoryEditText) findViewById(R.id.historyEditText); 47 | mHistoryEditText.setAdapter(createAdapter()); 48 | mHistoryEditText2 = (HistoryEditText) findViewById(R.id.historyEditText2); 49 | mHistoryEditText2.setAdapter(createAdapter()); 50 | autoCompleteTextView = (AutoCompleteTextView) findViewById(R.id.autoCompleteTextView); 51 | autoCompleteTextView.setAdapter(createAdapter()); 52 | autoCompleteTextView.setThreshold(1); 53 | autoCompleteTextView.setOnFocusChangeListener(new View.OnFocusChangeListener() { 54 | @Override 55 | public void onFocusChange(View v, boolean hasFocus) { 56 | if (hasFocus) { 57 | autoCompleteTextView.showDropDown(); 58 | } else { 59 | autoCompleteTextView.dismissDropDown(); 60 | } 61 | } 62 | }); 63 | } 64 | 65 | private ArrayAdapter createAdapter() { 66 | ArrayAdapter adapter = new ArrayAdapter(this, R.layout.het__simple_dropdown_item_1line, 67 | mCountries); 68 | return adapter; 69 | } 70 | 71 | @Override 72 | public boolean onCreateOptionsMenu(Menu menu) { 73 | new MenuInflater(this).inflate(R.menu.activity_simple, menu); 74 | return true; 75 | } 76 | 77 | @Override 78 | public boolean onOptionsItemSelected(MenuItem item) { 79 | if (item.getItemId() == R.id.action_clear_history) { 80 | clearHistory(); 81 | return true; 82 | } 83 | return false; 84 | } 85 | 86 | private void clearHistory() { 87 | mHistoryEditText.clearHistory(); 88 | mHistoryEditText2.clearHistory(); 89 | } 90 | } 91 | --------------------------------------------------------------------------------