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.preference.DialogPreference; 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 42 /** 43 * Almost a copy of EditTextPreference that shows a {@link AutoCompleteTextView} instead of the 44 * basic EditText. It always show the suggested options. 45 */ 46 public class AutoCompleteEditTextPreference extends DialogPreference { 47 /** 48 * The edit text shown in the dialog. 49 */ 50 private AutoCompleteTextView mAutoCompleteTextView; 51 52 private String mText; 53 private boolean mTextSet; 54 55 public AutoCompleteEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr, 56 int defStyleRes) { 57 super(context, attrs, defStyleAttr, defStyleRes); 58 59 mAutoCompleteTextView = new InstantAutoCompleteTextView(context, attrs); 60 61 // Give it an ID so it can be saved/restored 62 mAutoCompleteTextView.setId(R.id.edit); 63 64 /* 65 * The preference framework and view framework both have an 'enabled' 66 * attribute. Most likely, the 'enabled' specified in this XML is for 67 * the preference framework, but it was also given to the view framework. 68 * We reset the enabled state. 69 */ 70 mAutoCompleteTextView.setEnabled(true); 71 72 mAutoCompleteTextView.setOnEditorActionListener(new TextView.OnEditorActionListener() { 73 @Override 74 public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { 75 if (actionId == EditorInfo.IME_ACTION_DONE) { 76 onClick(getDialog(), DialogInterface.BUTTON_POSITIVE); 77 getDialog().dismiss(); 78 return true; 79 } 80 return false; 81 } 82 }); 83 setDialogLayoutResource(R.layout.preference_dialog_autocomplete_edittext); 84 } 85 86 public AutoCompleteEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr) { 87 this(context, attrs, defStyleAttr, 0); 88 } 89 90 public AutoCompleteEditTextPreference(Context context, AttributeSet attrs) { 91 this(context, attrs, android.R.attr.dialogPreferenceStyle); 92 } 93 94 public AutoCompleteEditTextPreference(Context context) { 95 this(context, null); 96 } 97 98 public AutoCompleteTextView getAutoCompleteTextView() { 99 return mAutoCompleteTextView; 100 } 101 102 /** 103 * Saves the text to the {@link SharedPreferences}. 104 * 105 * @param text The text to save 106 */ 107 public void setText(String text) { 108 // Always persist/notify the first time. 109 final boolean changed = !TextUtils.equals(mText, text); 110 if (changed || !mTextSet) { 111 mText = text; 112 mTextSet = true; 113 persistString(text); 114 if (changed) { 115 notifyDependencyChange(shouldDisableDependents()); 116 notifyChanged(); 117 } 118 } 119 } 120 121 @Override 122 public void showDialog(Bundle state) { 123 super.showDialog(state); 124 Window window = getDialog().getWindow(); 125 window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); 126 } 127 128 @VisibleForTesting 129 @Override 130 public void onClick() { 131 super.onClick(); 132 } 133 134 /** 135 * Gets the text from the {@link SharedPreferences}. 136 * 137 * @return The current preference value. 138 */ 139 public String getText() { 140 return mText; 141 } 142 143 @Override 144 protected void onBindDialogView(View view) { 145 super.onBindDialogView(view); 146 147 AutoCompleteTextView editText = mAutoCompleteTextView; 148 editText.setText(getText()); 149 150 ViewParent oldParent = editText.getParent(); 151 if (oldParent != view) { 152 if (oldParent != null) { 153 ((ViewGroup) oldParent).removeView(editText); 154 } 155 onAddEditTextToDialogView(view, editText); 156 } 157 } 158 159 /** 160 * Adds the AutoCompleteTextView widget of this preference to the dialog's view. 161 * 162 * @param dialogView The dialog view. 163 */ 164 protected void onAddEditTextToDialogView(View dialogView, AutoCompleteTextView editText) { 165 ViewGroup container = (ViewGroup) dialogView 166 .findViewById(R.id.autocomplete_edittext_container); 167 if (container != null) { 168 container.addView(editText, ViewGroup.LayoutParams.MATCH_PARENT, 169 ViewGroup.LayoutParams.WRAP_CONTENT); 170 } 171 } 172 173 @Override 174 protected void onDialogClosed(boolean positiveResult) { 175 super.onDialogClosed(positiveResult); 176 177 if (positiveResult) { 178 String value = mAutoCompleteTextView.getText().toString(); 179 if (callChangeListener(value)) { 180 setText(value); 181 } 182 } 183 } 184 185 @Override 186 protected Object onGetDefaultValue(TypedArray a, int index) { 187 return a.getString(index); 188 } 189 190 @Override 191 protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { 192 setText(restoreValue ? getPersistedString(mText) : (String) defaultValue); 193 } 194 195 @Override 196 public boolean shouldDisableDependents() { 197 return TextUtils.isEmpty(mText) || super.shouldDisableDependents(); 198 } 199 200 @Override 201 protected Parcelable onSaveInstanceState() { 202 final Parcelable superState = super.onSaveInstanceState(); 203 if (isPersistent()) { 204 // No need to save instance state since it's persistent 205 return superState; 206 } 207 208 final SavedState myState = new SavedState(superState); 209 myState.text = getText(); 210 return myState; 211 } 212 213 @Override 214 protected void onRestoreInstanceState(Parcelable state) { 215 if (state == null || !state.getClass().equals(SavedState.class)) { 216 // Didn't save state for us in onSaveInstanceState 217 super.onRestoreInstanceState(state); 218 return; 219 } 220 221 SavedState myState = (SavedState) state; 222 super.onRestoreInstanceState(myState.getSuperState()); 223 setText(myState.text); 224 } 225 226 private static class SavedState extends BaseSavedState { 227 String text; 228 229 public SavedState(Parcel source) { 230 super(source); 231 text = source.readString(); 232 } 233 234 @Override 235 public void writeToParcel(Parcel dest, int flags) { 236 super.writeToParcel(dest, flags); 237 dest.writeString(text); 238 } 239 240 public SavedState(Parcelable superState) { 241 super(superState); 242 } 243 244 public static final Parcelable.Creator<SavedState> CREATOR = 245 new Parcelable.Creator<SavedState>() { 246 public SavedState createFromParcel(Parcel in) { 247 return new SavedState(in); 248 } 249 250 public SavedState[] newArray(int size) { 251 return new SavedState[size]; 252 } 253 }; 254 } 255 256 /** {@link AutoCompleteTextView} that always shows the suggestions. */ 257 private class InstantAutoCompleteTextView extends AutoCompleteTextView { 258 public InstantAutoCompleteTextView(Context context, AttributeSet attrs) { 259 super(context, attrs); 260 } 261 262 @Override 263 public boolean enoughToFilter() { 264 return true; 265 } 266 267 @Override 268 protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { 269 super.onFocusChanged(focused, direction, previouslyFocusedRect); 270 271 showDropDownIfFocused(); 272 } 273 274 @Override 275 protected void onAttachedToWindow() { 276 super.onAttachedToWindow(); 277 showDropDownIfFocused(); 278 } 279 280 private void showDropDownIfFocused() { 281 if (isFocused() && getWindowVisibility() == View.VISIBLE) { 282 showDropDown(); 283 } 284 } 285 } 286 287 } 288