• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 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 android.preference;
18 
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.List;
22 import java.util.Map;
23 
24 import android.content.Context;
25 import android.content.res.TypedArray;
26 import android.os.Bundle;
27 import android.os.Parcelable;
28 import android.text.TextUtils;
29 import android.util.AttributeSet;
30 
31 /**
32  * A container for multiple
33  * {@link Preference} objects. It is a base class for  Preference objects that are
34  * parents, such as {@link PreferenceCategory} and {@link PreferenceScreen}.
35  *
36  * <div class="special reference">
37  * <h3>Developer Guides</h3>
38  * <p>For information about building a settings UI with Preferences,
39  * read the <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>
40  * guide.</p>
41  * </div>
42  *
43  * @attr ref android.R.styleable#PreferenceGroup_orderingFromXml
44  */
45 public abstract class PreferenceGroup extends Preference implements GenericInflater.Parent<Preference> {
46     /**
47      * The container for child {@link Preference}s. This is sorted based on the
48      * ordering, please use {@link #addPreference(Preference)} instead of adding
49      * to this directly.
50      */
51     private List<Preference> mPreferenceList;
52 
53     private boolean mOrderingAsAdded = true;
54 
55     private int mCurrentPreferenceOrder = 0;
56 
57     private boolean mAttachedToActivity = false;
58 
PreferenceGroup(Context context, AttributeSet attrs, int defStyle)59     public PreferenceGroup(Context context, AttributeSet attrs, int defStyle) {
60         super(context, attrs, defStyle);
61 
62         mPreferenceList = new ArrayList<Preference>();
63 
64         TypedArray a = context.obtainStyledAttributes(attrs,
65                 com.android.internal.R.styleable.PreferenceGroup, defStyle, 0);
66         mOrderingAsAdded = a.getBoolean(com.android.internal.R.styleable.PreferenceGroup_orderingFromXml,
67                 mOrderingAsAdded);
68         a.recycle();
69     }
70 
PreferenceGroup(Context context, AttributeSet attrs)71     public PreferenceGroup(Context context, AttributeSet attrs) {
72         this(context, attrs, 0);
73     }
74 
75     /**
76      * Whether to order the {@link Preference} children of this group as they
77      * are added. If this is false, the ordering will follow each Preference
78      * order and default to alphabetic for those without an order.
79      * <p>
80      * If this is called after preferences are added, they will not be
81      * re-ordered in the order they were added, hence call this method early on.
82      *
83      * @param orderingAsAdded Whether to order according to the order added.
84      * @see Preference#setOrder(int)
85      */
setOrderingAsAdded(boolean orderingAsAdded)86     public void setOrderingAsAdded(boolean orderingAsAdded) {
87         mOrderingAsAdded = orderingAsAdded;
88     }
89 
90     /**
91      * Whether this group is ordering preferences in the order they are added.
92      *
93      * @return Whether this group orders based on the order the children are added.
94      * @see #setOrderingAsAdded(boolean)
95      */
isOrderingAsAdded()96     public boolean isOrderingAsAdded() {
97         return mOrderingAsAdded;
98     }
99 
100     /**
101      * Called by the inflater to add an item to this group.
102      */
addItemFromInflater(Preference preference)103     public void addItemFromInflater(Preference preference) {
104         addPreference(preference);
105     }
106 
107     /**
108      * Returns the number of children {@link Preference}s.
109      * @return The number of preference children in this group.
110      */
getPreferenceCount()111     public int getPreferenceCount() {
112         return mPreferenceList.size();
113     }
114 
115     /**
116      * Returns the {@link Preference} at a particular index.
117      *
118      * @param index The index of the {@link Preference} to retrieve.
119      * @return The {@link Preference}.
120      */
getPreference(int index)121     public Preference getPreference(int index) {
122         return mPreferenceList.get(index);
123     }
124 
125     /**
126      * Adds a {@link Preference} at the correct position based on the
127      * preference's order.
128      *
129      * @param preference The preference to add.
130      * @return Whether the preference is now in this group.
131      */
addPreference(Preference preference)132     public boolean addPreference(Preference preference) {
133         if (mPreferenceList.contains(preference)) {
134             // Exists
135             return true;
136         }
137 
138         if (preference.getOrder() == Preference.DEFAULT_ORDER) {
139             if (mOrderingAsAdded) {
140                 preference.setOrder(mCurrentPreferenceOrder++);
141             }
142 
143             if (preference instanceof PreferenceGroup) {
144                 // TODO: fix (method is called tail recursively when inflating,
145                 // so we won't end up properly passing this flag down to children
146                 ((PreferenceGroup)preference).setOrderingAsAdded(mOrderingAsAdded);
147             }
148         }
149 
150         int insertionIndex = Collections.binarySearch(mPreferenceList, preference);
151         if (insertionIndex < 0) {
152             insertionIndex = insertionIndex * -1 - 1;
153         }
154 
155         if (!onPrepareAddPreference(preference)) {
156             return false;
157         }
158 
159         synchronized(this) {
160             mPreferenceList.add(insertionIndex, preference);
161         }
162 
163         preference.onAttachedToHierarchy(getPreferenceManager());
164 
165         if (mAttachedToActivity) {
166             preference.onAttachedToActivity();
167         }
168 
169         notifyHierarchyChanged();
170 
171         return true;
172     }
173 
174     /**
175      * Removes a {@link Preference} from this group.
176      *
177      * @param preference The preference to remove.
178      * @return Whether the preference was found and removed.
179      */
removePreference(Preference preference)180     public boolean removePreference(Preference preference) {
181         final boolean returnValue = removePreferenceInt(preference);
182         notifyHierarchyChanged();
183         return returnValue;
184     }
185 
removePreferenceInt(Preference preference)186     private boolean removePreferenceInt(Preference preference) {
187         synchronized(this) {
188             preference.onPrepareForRemoval();
189             return mPreferenceList.remove(preference);
190         }
191     }
192 
193     /**
194      * Removes all {@link Preference Preferences} from this group.
195      */
removeAll()196     public void removeAll() {
197         synchronized(this) {
198             List<Preference> preferenceList = mPreferenceList;
199             for (int i = preferenceList.size() - 1; i >= 0; i--) {
200                 removePreferenceInt(preferenceList.get(0));
201             }
202         }
203         notifyHierarchyChanged();
204     }
205 
206     /**
207      * Prepares a {@link Preference} to be added to the group.
208      *
209      * @param preference The preference to add.
210      * @return Whether to allow adding the preference (true), or not (false).
211      */
onPrepareAddPreference(Preference preference)212     protected boolean onPrepareAddPreference(Preference preference) {
213         if (!super.isEnabled()) {
214             preference.setEnabled(false);
215         }
216 
217         return true;
218     }
219 
220     /**
221      * Finds a {@link Preference} based on its key. If two {@link Preference}
222      * share the same key (not recommended), the first to appear will be
223      * returned (to retrieve the other preference with the same key, call this
224      * method on the first preference). If this preference has the key, it will
225      * not be returned.
226      * <p>
227      * This will recursively search for the preference into children that are
228      * also {@link PreferenceGroup PreferenceGroups}.
229      *
230      * @param key The key of the preference to retrieve.
231      * @return The {@link Preference} with the key, or null.
232      */
findPreference(CharSequence key)233     public Preference findPreference(CharSequence key) {
234         if (TextUtils.equals(getKey(), key)) {
235             return this;
236         }
237         final int preferenceCount = getPreferenceCount();
238         for (int i = 0; i < preferenceCount; i++) {
239             final Preference preference = getPreference(i);
240             final String curKey = preference.getKey();
241 
242             if (curKey != null && curKey.equals(key)) {
243                 return preference;
244             }
245 
246             if (preference instanceof PreferenceGroup) {
247                 final Preference returnedPreference = ((PreferenceGroup)preference)
248                         .findPreference(key);
249                 if (returnedPreference != null) {
250                     return returnedPreference;
251                 }
252             }
253         }
254 
255         return null;
256     }
257 
258     /**
259      * Whether this preference group should be shown on the same screen as its
260      * contained preferences.
261      *
262      * @return True if the contained preferences should be shown on the same
263      *         screen as this preference.
264      */
isOnSameScreenAsChildren()265     protected boolean isOnSameScreenAsChildren() {
266         return true;
267     }
268 
269     @Override
onAttachedToActivity()270     protected void onAttachedToActivity() {
271         super.onAttachedToActivity();
272 
273         // Mark as attached so if a preference is later added to this group, we
274         // can tell it we are already attached
275         mAttachedToActivity = true;
276 
277         // Dispatch to all contained preferences
278         final int preferenceCount = getPreferenceCount();
279         for (int i = 0; i < preferenceCount; i++) {
280             getPreference(i).onAttachedToActivity();
281         }
282     }
283 
284     @Override
onPrepareForRemoval()285     protected void onPrepareForRemoval() {
286         super.onPrepareForRemoval();
287 
288         // We won't be attached to the activity anymore
289         mAttachedToActivity = false;
290     }
291 
292     @Override
setEnabled(boolean enabled)293     public void setEnabled(boolean enabled) {
294         super.setEnabled(enabled);
295 
296         // Dispatch to all contained preferences
297         final int preferenceCount = getPreferenceCount();
298         for (int i = 0; i < preferenceCount; i++) {
299             getPreference(i).setEnabled(enabled);
300         }
301     }
302 
sortPreferences()303     void sortPreferences() {
304         synchronized (this) {
305             Collections.sort(mPreferenceList);
306         }
307     }
308 
309     @Override
dispatchSaveInstanceState(Bundle container)310     protected void dispatchSaveInstanceState(Bundle container) {
311         super.dispatchSaveInstanceState(container);
312 
313         // Dispatch to all contained preferences
314         final int preferenceCount = getPreferenceCount();
315         for (int i = 0; i < preferenceCount; i++) {
316             getPreference(i).dispatchSaveInstanceState(container);
317         }
318     }
319 
320     @Override
dispatchRestoreInstanceState(Bundle container)321     protected void dispatchRestoreInstanceState(Bundle container) {
322         super.dispatchRestoreInstanceState(container);
323 
324         // Dispatch to all contained preferences
325         final int preferenceCount = getPreferenceCount();
326         for (int i = 0; i < preferenceCount; i++) {
327             getPreference(i).dispatchRestoreInstanceState(container);
328         }
329     }
330 
331 }
332