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 
AutoCompleteEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)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 
AutoCompleteEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr)86     public AutoCompleteEditTextPreference(Context context, AttributeSet attrs, int defStyleAttr) {
87         this(context, attrs, defStyleAttr, 0);
88     }
89 
AutoCompleteEditTextPreference(Context context, AttributeSet attrs)90     public AutoCompleteEditTextPreference(Context context, AttributeSet attrs) {
91         this(context, attrs, android.R.attr.dialogPreferenceStyle);
92     }
93 
AutoCompleteEditTextPreference(Context context)94     public AutoCompleteEditTextPreference(Context context) {
95         this(context, null);
96     }
97 
getAutoCompleteTextView()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      */
setText(String text)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
showDialog(Bundle state)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
onClick()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      */
getText()139     public String getText() {
140         return mText;
141     }
142 
143     @Override
onBindDialogView(View view)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      */
onAddEditTextToDialogView(View dialogView, AutoCompleteTextView editText)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
onDialogClosed(boolean positiveResult)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
onGetDefaultValue(TypedArray a, int index)186     protected Object onGetDefaultValue(TypedArray a, int index) {
187         return a.getString(index);
188     }
189 
190     @Override
onSetInitialValue(boolean restoreValue, Object defaultValue)191     protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
192         setText(restoreValue ? getPersistedString(mText) : (String) defaultValue);
193     }
194 
195     @Override
shouldDisableDependents()196     public boolean shouldDisableDependents() {
197         return TextUtils.isEmpty(mText) || super.shouldDisableDependents();
198     }
199 
200     @Override
onSaveInstanceState()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
onRestoreInstanceState(Parcelable state)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 
SavedState(Parcel source)229         public SavedState(Parcel source) {
230             super(source);
231             text = source.readString();
232         }
233 
234         @Override
writeToParcel(Parcel dest, int flags)235         public void writeToParcel(Parcel dest, int flags) {
236             super.writeToParcel(dest, flags);
237             dest.writeString(text);
238         }
239 
SavedState(Parcelable superState)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 {
InstantAutoCompleteTextView(Context context, AttributeSet attrs)258         public InstantAutoCompleteTextView(Context context, AttributeSet attrs) {
259             super(context, attrs);
260         }
261 
262         @Override
enoughToFilter()263         public boolean enoughToFilter() {
264             return true;
265         }
266 
267         @Override
onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect)268         protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
269             super.onFocusChanged(focused, direction, previouslyFocusedRect);
270 
271             showDropDownIfFocused();
272         }
273 
274         @Override
onAttachedToWindow()275         protected void onAttachedToWindow() {
276             super.onAttachedToWindow();
277             showDropDownIfFocused();
278         }
279 
showDropDownIfFocused()280         private void showDropDownIfFocused() {
281             if (isFocused() && getWindowVisibility() == View.VISIBLE) {
282                 showDropDown();
283             }
284         }
285     }
286 
287 }
288