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.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