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