blob: 5f8c78d269e624d7b73628bf1c60b046c779e9c7 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
17package android.preference;
18
19import java.util.ArrayList;
20import java.util.Collections;
21import java.util.List;
22import java.util.Map;
23
24import android.content.Context;
25import android.content.res.TypedArray;
26import android.os.Bundle;
27import android.os.Parcelable;
28import android.text.TextUtils;
29import 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 *
Scott Maincdd0c592012-07-26 17:03:51 -070036 * <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 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043 * @attr ref android.R.styleable#PreferenceGroup_orderingFromXml
44 */
45public 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
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
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 */
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 */
96 public boolean isOrderingAsAdded() {
97 return mOrderingAsAdded;
98 }
99
100 /**
101 * Called by the inflater to add an item to this group.
102 */
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 */
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 */
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 */
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 */
180 public boolean removePreference(Preference preference) {
181 final boolean returnValue = removePreferenceInt(preference);
182 notifyHierarchyChanged();
183 return returnValue;
184 }
185
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 */
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 */
212 protected boolean onPrepareAddPreference(Preference preference) {
Alan Viverette651b4242013-09-05 13:32:40 -0700213 preference.onParentChanged(this, shouldDisableDependents());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214 return true;
215 }
216
217 /**
218 * Finds a {@link Preference} based on its key. If two {@link Preference}
219 * share the same key (not recommended), the first to appear will be
220 * returned (to retrieve the other preference with the same key, call this
221 * method on the first preference). If this preference has the key, it will
222 * not be returned.
223 * <p>
224 * This will recursively search for the preference into children that are
225 * also {@link PreferenceGroup PreferenceGroups}.
226 *
227 * @param key The key of the preference to retrieve.
228 * @return The {@link Preference} with the key, or null.
229 */
230 public Preference findPreference(CharSequence key) {
231 if (TextUtils.equals(getKey(), key)) {
232 return this;
233 }
234 final int preferenceCount = getPreferenceCount();
235 for (int i = 0; i < preferenceCount; i++) {
236 final Preference preference = getPreference(i);
237 final String curKey = preference.getKey();
238
239 if (curKey != null && curKey.equals(key)) {
240 return preference;
241 }
242
243 if (preference instanceof PreferenceGroup) {
244 final Preference returnedPreference = ((PreferenceGroup)preference)
245 .findPreference(key);
246 if (returnedPreference != null) {
247 return returnedPreference;
248 }
249 }
250 }
251
252 return null;
253 }
254
255 /**
256 * Whether this preference group should be shown on the same screen as its
257 * contained preferences.
258 *
259 * @return True if the contained preferences should be shown on the same
260 * screen as this preference.
261 */
262 protected boolean isOnSameScreenAsChildren() {
263 return true;
264 }
265
266 @Override
267 protected void onAttachedToActivity() {
268 super.onAttachedToActivity();
269
270 // Mark as attached so if a preference is later added to this group, we
271 // can tell it we are already attached
272 mAttachedToActivity = true;
273
274 // Dispatch to all contained preferences
275 final int preferenceCount = getPreferenceCount();
276 for (int i = 0; i < preferenceCount; i++) {
277 getPreference(i).onAttachedToActivity();
278 }
279 }
280
281 @Override
282 protected void onPrepareForRemoval() {
283 super.onPrepareForRemoval();
284
285 // We won't be attached to the activity anymore
286 mAttachedToActivity = false;
287 }
288
289 @Override
Alan Viverette02f56802013-08-19 16:32:56 -0700290 public void notifyDependencyChange(boolean disableDependents) {
291 super.notifyDependencyChange(disableDependents);
292
293 // Child preferences have an implicit dependency on their containing
294 // group. Dispatch dependency change to all contained preferences.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 final int preferenceCount = getPreferenceCount();
296 for (int i = 0; i < preferenceCount; i++) {
Alan Viverette02f56802013-08-19 16:32:56 -0700297 getPreference(i).onParentChanged(this, disableDependents);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800298 }
299 }
300
301 void sortPreferences() {
302 synchronized (this) {
303 Collections.sort(mPreferenceList);
304 }
305 }
306
307 @Override
308 protected void dispatchSaveInstanceState(Bundle container) {
309 super.dispatchSaveInstanceState(container);
310
311 // Dispatch to all contained preferences
312 final int preferenceCount = getPreferenceCount();
313 for (int i = 0; i < preferenceCount; i++) {
314 getPreference(i).dispatchSaveInstanceState(container);
315 }
316 }
317
318 @Override
319 protected void dispatchRestoreInstanceState(Bundle container) {
320 super.dispatchRestoreInstanceState(container);
321
322 // Dispatch to all contained preferences
323 final int preferenceCount = getPreferenceCount();
324 for (int i = 0; i < preferenceCount; i++) {
325 getPreference(i).dispatchRestoreInstanceState(container);
326 }
327 }
328
329}