/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settings.accessibility; import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.content.res.TypedArray; import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.BaseAdapter; import com.android.settings.CustomDialogPreference; /** * Abstract dialog preference that displays a set of values and optional titles. */ public abstract class ListDialogPreference extends CustomDialogPreference { private CharSequence[] mEntryTitles; private int[] mEntryValues; private OnValueChangedListener mOnValueChangedListener; /** The layout resource to use for grid items. */ private int mListItemLayout; /** The current value of this preference. */ private int mValue; /** The index within the value set of the current value. */ private int mValueIndex; /** Whether the value had been set using {@link #setValue}. */ private boolean mValueSet; public ListDialogPreference(Context context, AttributeSet attrs) { super(context, attrs); } /** * Sets a listened to invoke when the value of this preference changes. * * @param listener the listener to invoke */ public void setOnValueChangedListener(OnValueChangedListener listener) { mOnValueChangedListener = listener; } /** * Sets the layout to use for grid items. * * @param layoutResId the layout to use for displaying grid items */ public void setListItemLayoutResource(int layoutResId) { mListItemLayout = layoutResId; } /** * Sets the list of item values. Values must be distinct. * * @param values the list of item values */ public void setValues(int[] values) { mEntryValues = values; if (mValueSet && mValueIndex == AbsListView.INVALID_POSITION) { mValueIndex = getIndexForValue(mValue); } } /** * Sets the list of item titles. May be null if no titles are specified, or * may be shorter than the list of values to leave some titles unspecified. * * @param titles the list of item titles */ public void setTitles(CharSequence[] titles) { mEntryTitles = titles; } /** * Populates a list item view with data for the specified index. * * @param view the view to populate * @param index the index for which to populate the view * @see #setListItemLayoutResource(int) * @see #getValueAt(int) * @see #getTitleAt(int) */ protected abstract void onBindListItem(View view, int index); /** * @return the title at the specified index, or null if none specified */ protected CharSequence getTitleAt(int index) { if (mEntryTitles == null || mEntryTitles.length <= index) { return null; } return mEntryTitles[index]; } /** * @return the value at the specified index */ protected int getValueAt(int index) { return mEntryValues[index]; } @Override public CharSequence getSummary() { if (mValueIndex >= 0) { return getTitleAt(mValueIndex); } return null; } @Override protected void onPrepareDialogBuilder(AlertDialog.Builder builder, DialogInterface.OnClickListener listener) { super.onPrepareDialogBuilder(builder, listener); final Context context = getContext(); final int dialogLayout = getDialogLayoutResource(); final View picker = LayoutInflater.from(context).inflate(dialogLayout, null); final ListPreferenceAdapter adapter = new ListPreferenceAdapter(); final AbsListView list = (AbsListView) picker.findViewById(android.R.id.list); list.setAdapter(adapter); list.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapter, View v, int position, long id) { if (callChangeListener((int) id)) { setValue((int) id); } final Dialog dialog = getDialog(); if (dialog != null) { dialog.dismiss(); } } }); // Set initial selection. final int selectedPosition = getIndexForValue(mValue); if (selectedPosition != AbsListView.INVALID_POSITION) { list.setSelection(selectedPosition); } builder.setView(picker); builder.setPositiveButton(null, null); } /** * @return the index of the specified value within the list of entry values, * or {@link AbsListView#INVALID_POSITION} if not found */ protected int getIndexForValue(int value) { final int[] values = mEntryValues; if (values != null) { final int count = values.length; for (int i = 0; i < count; i++) { if (values[i] == value) { return i; } } } return AbsListView.INVALID_POSITION; } /** * Sets the current value. If the value exists within the set of entry * values, updates the selection index. * * @param value the value to set */ public void setValue(int value) { final boolean changed = mValue != value; if (changed || !mValueSet) { mValue = value; mValueIndex = getIndexForValue(value); mValueSet = true; persistInt(value); if (changed) { notifyDependencyChange(shouldDisableDependents()); notifyChanged(); } if (mOnValueChangedListener != null) { mOnValueChangedListener.onValueChanged(this, value); } } } /** * @return the current value */ public int getValue() { return mValue; } @Override protected Object onGetDefaultValue(TypedArray a, int index) { return a.getInt(index, 0); } @Override protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { setValue(restoreValue ? getPersistedInt(mValue) : (Integer) defaultValue); } @Override protected Parcelable onSaveInstanceState() { final Parcelable superState = super.onSaveInstanceState(); if (isPersistent()) { // No need to save instance state since it's persistent return superState; } final SavedState myState = new SavedState(superState); myState.value = getValue(); return myState; } @Override protected void onRestoreInstanceState(Parcelable state) { if (state == null || !state.getClass().equals(SavedState.class)) { // Didn't save state for us in onSaveInstanceState super.onRestoreInstanceState(state); return; } SavedState myState = (SavedState) state; super.onRestoreInstanceState(myState.getSuperState()); setValue(myState.value); } private class ListPreferenceAdapter extends BaseAdapter { private LayoutInflater mInflater; @Override public int getCount() { return mEntryValues.length; } @Override public Integer getItem(int position) { return mEntryValues[position]; } @Override public long getItemId(int position) { return mEntryValues[position]; } @Override public boolean hasStableIds() { return true; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { if (mInflater == null) { mInflater = LayoutInflater.from(parent.getContext()); } convertView = mInflater.inflate(mListItemLayout, parent, false); } onBindListItem(convertView, position); return convertView; } } private static class SavedState extends BaseSavedState { public int value; public SavedState(Parcel source) { super(source); value = source.readInt(); } @Override public void writeToParcel(Parcel dest, int flags) { super.writeToParcel(dest, flags); dest.writeInt(value); } public SavedState(Parcelable superState) { super(superState); } @SuppressWarnings({ "hiding", "unused" }) public static final Creator<SavedState> CREATOR = new Creator<SavedState>() { @Override public SavedState createFromParcel(Parcel in) { return new SavedState(in); } @Override public SavedState[] newArray(int size) { return new SavedState[size]; } }; } public interface OnValueChangedListener { public void onValueChanged(ListDialogPreference preference, int value); } }