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