Home | History | Annotate | Download | only in accessibility
      1 /*
      2  * Copyright (C) 2013 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 
     17 package com.android.settings.accessibility;
     18 
     19 import android.app.AlertDialog.Builder;
     20 import android.app.Dialog;
     21 import android.content.Context;
     22 import android.content.res.TypedArray;
     23 import android.os.Parcel;
     24 import android.os.Parcelable;
     25 import android.preference.DialogPreference;
     26 import android.util.AttributeSet;
     27 import android.view.LayoutInflater;
     28 import android.view.View;
     29 import android.view.ViewGroup;
     30 import android.widget.AbsListView;
     31 import android.widget.AdapterView;
     32 import android.widget.AdapterView.OnItemClickListener;
     33 import android.widget.BaseAdapter;
     34 
     35 /**
     36  * Abstract dialog preference that displays a set of values and optional titles.
     37  */
     38 public abstract class ListDialogPreference extends DialogPreference {
     39     private CharSequence[] mEntryTitles;
     40     private int[] mEntryValues;
     41 
     42     private OnValueChangedListener mOnValueChangedListener;
     43 
     44     /** The layout resource to use for grid items. */
     45     private int mListItemLayout;
     46 
     47     /** The current value of this preference. */
     48     private int mValue;
     49 
     50     /** The index within the value set of the current value. */
     51     private int mValueIndex;
     52 
     53     /** Whether the value had been set using {@link #setValue}. */
     54     private boolean mValueSet;
     55 
     56     public ListDialogPreference(Context context, AttributeSet attrs) {
     57         super(context, attrs);
     58     }
     59 
     60     /**
     61      * Sets a listened to invoke when the value of this preference changes.
     62      *
     63      * @param listener the listener to invoke
     64      */
     65     public void setOnValueChangedListener(OnValueChangedListener listener) {
     66         mOnValueChangedListener = listener;
     67     }
     68 
     69     /**
     70      * Sets the layout to use for grid items.
     71      *
     72      * @param layoutResId the layout to use for displaying grid items
     73      */
     74     public void setListItemLayoutResource(int layoutResId) {
     75         mListItemLayout = layoutResId;
     76     }
     77 
     78     /**
     79      * Sets the list of item values. Values must be distinct.
     80      *
     81      * @param values the list of item values
     82      */
     83     public void setValues(int[] values) {
     84         mEntryValues = values;
     85 
     86         if (mValueSet && mValueIndex == AbsListView.INVALID_POSITION) {
     87             mValueIndex = getIndexForValue(mValue);
     88         }
     89     }
     90 
     91     /**
     92      * Sets the list of item titles. May be null if no titles are specified, or
     93      * may be shorter than the list of values to leave some titles unspecified.
     94      *
     95      * @param titles the list of item titles
     96      */
     97     public void setTitles(CharSequence[] titles) {
     98         mEntryTitles = titles;
     99     }
    100 
    101     /**
    102      * Populates a list item view with data for the specified index.
    103      *
    104      * @param view the view to populate
    105      * @param index the index for which to populate the view
    106      * @see #setListItemLayoutResource(int)
    107      * @see #getValueAt(int)
    108      * @see #getTitleAt(int)
    109      */
    110     protected abstract void onBindListItem(View view, int index);
    111 
    112     /**
    113      * @return the title at the specified index, or null if none specified
    114      */
    115     protected CharSequence getTitleAt(int index) {
    116         if (mEntryTitles == null || mEntryTitles.length <= index) {
    117             return null;
    118         }
    119 
    120         return mEntryTitles[index];
    121     }
    122 
    123     /**
    124      * @return the value at the specified index
    125      */
    126     protected int getValueAt(int index) {
    127         return mEntryValues[index];
    128     }
    129 
    130     @Override
    131     public CharSequence getSummary() {
    132         if (mValueIndex >= 0) {
    133             return getTitleAt(mValueIndex);
    134         }
    135 
    136         return null;
    137     }
    138 
    139     @Override
    140     protected void onPrepareDialogBuilder(Builder builder) {
    141         super.onPrepareDialogBuilder(builder);
    142 
    143         final Context context = getContext();
    144         final int dialogLayout = getDialogLayoutResource();
    145         final View picker = LayoutInflater.from(context).inflate(dialogLayout, null);
    146         final ListPreferenceAdapter adapter = new ListPreferenceAdapter();
    147         final AbsListView list = (AbsListView) picker.findViewById(android.R.id.list);
    148         list.setAdapter(adapter);
    149         list.setOnItemClickListener(new OnItemClickListener() {
    150             @Override
    151             public void onItemClick(AdapterView<?> adapter, View v, int position, long id) {
    152                 if (callChangeListener((int) id)) {
    153                     setValue((int) id);
    154                 }
    155 
    156                 final Dialog dialog = getDialog();
    157                 if (dialog != null) {
    158                     dialog.dismiss();
    159                 }
    160             }
    161         });
    162 
    163         // Set initial selection.
    164         final int selectedPosition = getIndexForValue(mValue);
    165         if (selectedPosition != AbsListView.INVALID_POSITION) {
    166             list.setSelection(selectedPosition);
    167         }
    168 
    169         builder.setView(picker);
    170         builder.setPositiveButton(null, null);
    171     }
    172 
    173     /**
    174      * @return the index of the specified value within the list of entry values,
    175      *         or {@link AbsListView#INVALID_POSITION} if not found
    176      */
    177     protected int getIndexForValue(int value) {
    178         final int[] values = mEntryValues;
    179         if (values != null) {
    180             final int count = values.length;
    181             for (int i = 0; i < count; i++) {
    182                 if (values[i] == value) {
    183                     return i;
    184                 }
    185             }
    186         }
    187 
    188         return AbsListView.INVALID_POSITION;
    189     }
    190 
    191     /**
    192      * Sets the current value. If the value exists within the set of entry
    193      * values, updates the selection index.
    194      *
    195      * @param value the value to set
    196      */
    197     public void setValue(int value) {
    198         final boolean changed = mValue != value;
    199         if (changed || !mValueSet) {
    200             mValue = value;
    201             mValueIndex = getIndexForValue(value);
    202             mValueSet = true;
    203             persistInt(value);
    204             if (changed) {
    205                 notifyDependencyChange(shouldDisableDependents());
    206                 notifyChanged();
    207             }
    208             if (mOnValueChangedListener != null) {
    209                 mOnValueChangedListener.onValueChanged(this, value);
    210             }
    211         }
    212     }
    213 
    214     /**
    215      * @return the current value
    216      */
    217     public int getValue() {
    218         return mValue;
    219     }
    220 
    221     @Override
    222     protected Object onGetDefaultValue(TypedArray a, int index) {
    223         return a.getInt(index, 0);
    224     }
    225 
    226     @Override
    227     protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
    228         setValue(restoreValue ? getPersistedInt(mValue) : (Integer) defaultValue);
    229     }
    230 
    231     @Override
    232     protected Parcelable onSaveInstanceState() {
    233         final Parcelable superState = super.onSaveInstanceState();
    234         if (isPersistent()) {
    235             // No need to save instance state since it's persistent
    236             return superState;
    237         }
    238 
    239         final SavedState myState = new SavedState(superState);
    240         myState.value = getValue();
    241         return myState;
    242     }
    243 
    244     @Override
    245     protected void onRestoreInstanceState(Parcelable state) {
    246         if (state == null || !state.getClass().equals(SavedState.class)) {
    247             // Didn't save state for us in onSaveInstanceState
    248             super.onRestoreInstanceState(state);
    249             return;
    250         }
    251 
    252         SavedState myState = (SavedState) state;
    253         super.onRestoreInstanceState(myState.getSuperState());
    254         setValue(myState.value);
    255     }
    256 
    257     private class ListPreferenceAdapter extends BaseAdapter {
    258         private LayoutInflater mInflater;
    259 
    260         @Override
    261         public int getCount() {
    262             return mEntryValues.length;
    263         }
    264 
    265         @Override
    266         public Integer getItem(int position) {
    267             return mEntryValues[position];
    268         }
    269 
    270         @Override
    271         public long getItemId(int position) {
    272             return mEntryValues[position];
    273         }
    274 
    275         @Override
    276         public boolean hasStableIds() {
    277             return true;
    278         }
    279 
    280         @Override
    281         public View getView(int position, View convertView, ViewGroup parent) {
    282             if (convertView == null) {
    283                 if (mInflater == null) {
    284                     mInflater = LayoutInflater.from(parent.getContext());
    285                 }
    286                 convertView = mInflater.inflate(mListItemLayout, parent, false);
    287             }
    288             onBindListItem(convertView, position);
    289             return convertView;
    290         }
    291     }
    292 
    293     private static class SavedState extends BaseSavedState {
    294         public int value;
    295 
    296         public SavedState(Parcel source) {
    297             super(source);
    298             value = source.readInt();
    299         }
    300 
    301         @Override
    302         public void writeToParcel(Parcel dest, int flags) {
    303             super.writeToParcel(dest, flags);
    304             dest.writeInt(value);
    305         }
    306 
    307         public SavedState(Parcelable superState) {
    308             super(superState);
    309         }
    310 
    311         @SuppressWarnings({ "hiding", "unused" })
    312         public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
    313             @Override
    314             public SavedState createFromParcel(Parcel in) {
    315                 return new SavedState(in);
    316             }
    317 
    318             @Override
    319             public SavedState[] newArray(int size) {
    320                 return new SavedState[size];
    321             }
    322         };
    323     }
    324 
    325     public interface OnValueChangedListener {
    326         public void onValueChanged(ListDialogPreference preference, int value);
    327     }
    328 }
    329