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 */
16package com.android.emergency.preferences;
17
18import android.content.Context;
19import android.content.DialogInterface;
20import android.content.SharedPreferences;
21import android.content.res.TypedArray;
22import android.graphics.Rect;
23import android.os.Bundle;
24import android.os.Parcel;
25import android.os.Parcelable;
26import android.preference.DialogPreference;
27import android.text.TextUtils;
28import android.util.AttributeSet;
29import android.view.KeyEvent;
30import android.view.View;
31import android.view.ViewGroup;
32import android.view.ViewParent;
33import android.view.Window;
34import android.view.WindowManager;
35import android.view.inputmethod.EditorInfo;
36import android.widget.AutoCompleteTextView;
37import android.widget.TextView;
38
39import com.android.emergency.R;
40import 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 */
46public 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