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