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