Home | History | Annotate | Download | only in preferences
      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