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