1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.emergency.preferences; 17 18 import android.content.Context; 19 import android.content.DialogInterface; 20 import android.content.SharedPreferences; 21 import android.content.res.TypedArray; 22 import android.graphics.Rect; 23 import android.os.Bundle; 24 import android.os.Parcel; 25 import android.os.Parcelable; 26 import android.support.v14.preference.PreferenceDialogFragment; 27 import android.text.TextUtils; 28 import android.util.AttributeSet; 29 import android.view.KeyEvent; 30 import android.view.View; 31 import android.view.ViewGroup; 32 import android.view.ViewParent; 33 import android.view.Window; 34 import android.view.WindowManager; 35 import android.view.inputmethod.EditorInfo; 36 import android.widget.AutoCompleteTextView; 37 import android.widget.TextView; 38 39 import com.android.emergency.R; 40 import com.android.internal.annotations.VisibleForTesting; 41 import com.android.settingslib.CustomDialogPreference; 42 43 /** 44 * Almost a copy of EditTextPreference that shows a {@link AutoCompleteTextView} instead of the 45 * basic EditText. It always show the suggested options. 46 * TODO: an EditTextPreference always shows the soft input keyboard, whereas this class does not, 47 * and it should, to mimic EditTextPreference better. 48 */ 49 public class AutoCompleteEditTextPreference extends CustomDialogPreference { 50 /** 51 * The edit text shown in the dialog. 52 */ 53 private AutoCompleteTextView mAutoCompleteTextView; 54 55 private String mText; 56 private boolean mTextSet; 57 58 public AutoCompleteEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr, 59 int defStyleRes) { 60 super(context, attrs, defStyleAttr, defStyleRes); 61 62 mAutoCompleteTextView = new InstantAutoCompleteTextView(context, attrs); 63 64 // Give it an ID so it can be saved/restored 65 mAutoCompleteTextView.setId(R.id.edit); 66 67 /* 68 * The preference framework and view framework both have an 'enabled' 69 * attribute. Most likely, the 'enabled' specified in this XML is for 70 * the preference framework, but it was also given to the view framework. 71 * We reset the enabled state. 72 */ 73 mAutoCompleteTextView.setEnabled(true); 74 75 mAutoCompleteTextView.setOnEditorActionListener(new TextView.OnEditorActionListener() { 76 @Override 77 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { 78 if (actionId == EditorInfo.IME_ACTION_DONE) { 79 onClick(getDialog(), DialogInterface.BUTTON_POSITIVE); 80 getDialog().dismiss(); 81 return true; 82 } 83 return false; 84 } 85 }); 86 setDialogLayoutResource(R.layout.preference_dialog_autocomplete_edittext); 87 } 88 89 public AutoCompleteEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr) { 90 this(context, attrs, defStyleAttr, 0); 91 } 92 93 public AutoCompleteEditTextPreference(Context context, AttributeSet attrs) { 94 this(context, attrs, R.attr.dialogPreferenceStyle); 95 } 96 97 public AutoCompleteEditTextPreference(Context context) { 98 this(context, null); 99 } 100 101 public AutoCompleteTextView getAutoCompleteTextView() { 102 return mAutoCompleteTextView; 103 } 104 105 /** 106 * Saves the text to the {@link SharedPreferences}. 107 * 108 * @param text The text to save 109 */ 110 public void setText(String text) { 111 // Always persist/notify the first time. 112 final boolean changed = !TextUtils.equals(mText, text); 113 if (changed || !mTextSet) { 114 mText = text; 115 mTextSet = true; 116 persistString(text); 117 if (changed) { 118 notifyDependencyChange(shouldDisableDependents()); 119 notifyChanged(); 120 } 121 } 122 } 123 124 @VisibleForTesting 125 @Override 126 public void onClick() { 127 super.onClick(); 128 } 129 130 /** 131 * Gets the text from the {@link SharedPreferences}. 132 * 133 * @return The current preference value. 134 */ 135 public String getText() { 136 return mText; 137 } 138 139 @Override 140 protected void onBindDialogView(View view) { 141 super.onBindDialogView(view); 142 143 AutoCompleteTextView editText = mAutoCompleteTextView; 144 editText.setText(getText()); 145 editText.requestFocus(); 146 147 ViewParent oldParent = editText.getParent(); 148 if (oldParent != view) { 149 if (oldParent != null) { 150 ((ViewGroup) oldParent).removeView(editText); 151 } 152 onAddEditTextToDialogView(view, editText); 153 } 154 editText.setSelection(editText.getText().length()); 155 } 156 157 /** 158 * Adds the AutoCompleteTextView widget of this preference to the dialog's view. 159 * 160 * @param dialogView The dialog view. 161 */ 162 protected void onAddEditTextToDialogView(View dialogView, AutoCompleteTextView editText) { 163 ViewGroup container = (ViewGroup) dialogView 164 .findViewById(R.id.autocomplete_edittext_container); 165 if (container != null) { 166 container.addView(editText, ViewGroup.LayoutParams.MATCH_PARENT, 167 ViewGroup.LayoutParams.WRAP_CONTENT); 168 } 169 } 170 171 @Override 172 protected void onDialogClosed(boolean positiveResult) { 173 super.onDialogClosed(positiveResult); 174 175 if (positiveResult) { 176 String value = mAutoCompleteTextView.getText().toString(); 177 if (callChangeListener(value)) { 178 setText(value); 179 } 180 } 181 } 182 183 @Override 184 protected Object onGetDefaultValue(TypedArray a, int index) { 185 return a.getString(index); 186 } 187 188 @Override 189 protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { 190 setText(restoreValue ? getPersistedString(mText) : (String) defaultValue); 191 } 192 193 @Override 194 public boolean shouldDisableDependents() { 195 return TextUtils.isEmpty(mText) || super.shouldDisableDependents(); 196 } 197 198 @Override 199 protected Parcelable onSaveInstanceState() { 200 final Parcelable superState = super.onSaveInstanceState(); 201 if (isPersistent()) { 202 // No need to save instance state since it's persistent 203 return superState; 204 } 205 206 final SavedState myState = new SavedState(superState); 207 myState.text = getText(); 208 return myState; 209 } 210 211 @Override 212 protected void onRestoreInstanceState(Parcelable state) { 213 if (state == null || !state.getClass().equals(SavedState.class)) { 214 // Didn't save state for us in onSaveInstanceState 215 super.onRestoreInstanceState(state); 216 return; 217 } 218 219 SavedState myState = (SavedState) state; 220 super.onRestoreInstanceState(myState.getSuperState()); 221 setText(myState.text); 222 } 223 224 private static class SavedState extends BaseSavedState { 225 String text; 226 227 public SavedState(Parcel source) { 228 super(source); 229 text = source.readString(); 230 } 231 232 @Override 233 public void writeToParcel(Parcel dest, int flags) { 234 super.writeToParcel(dest, flags); 235 dest.writeString(text); 236 } 237 238 public SavedState(Parcelable superState) { 239 super(superState); 240 } 241 242 public static final Parcelable.Creator<SavedState> CREATOR = 243 new Parcelable.Creator<SavedState>() { 244 public SavedState createFromParcel(Parcel in) { 245 return new SavedState(in); 246 } 247 248 public SavedState[] newArray(int size) { 249 return new SavedState[size]; 250 } 251 }; 252 } 253 254 /** {@link AutoCompleteTextView} that always shows the suggestions. */ 255 private class InstantAutoCompleteTextView extends AutoCompleteTextView { 256 public InstantAutoCompleteTextView(Context context, AttributeSet attrs) { 257 super(context, attrs); 258 } 259 260 @Override 261 public boolean enoughToFilter() { 262 return true; 263 } 264 265 @Override 266 protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { 267 super.onFocusChanged(focused, direction, previouslyFocusedRect); 268 269 showDropDownIfFocused(); 270 } 271 272 @Override 273 protected void onAttachedToWindow() { 274 super.onAttachedToWindow(); 275 showDropDownIfFocused(); 276 } 277 278 private void showDropDownIfFocused() { 279 if (isFocused() && getWindowVisibility() == View.VISIBLE) { 280 showDropDown(); 281 } 282 } 283 } 284 285 public static class AutoCompleteEditTextPreferenceDialogFragment 286 extends CustomDialogPreference.CustomPreferenceDialogFragment { 287 public static CustomDialogPreference.CustomPreferenceDialogFragment 288 newInstance(String key) { 289 return CustomDialogPreference.CustomPreferenceDialogFragment.newInstance(key); 290 } 291 } 292 } 293