blob: f135b26fc5dfd9300c7ba40c9354cdefc2c77121 [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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.content.Context;
23import android.content.res.TypedArray;
24import android.os.Bundle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.text.TextUtils;
26import 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}.
Filip Pavliscba64582017-01-10 19:17:09 +000032 *
Scott Maincdd0c592012-07-26 17:03:51 -070033 * <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>
Filip Pavliscba64582017-01-10 19:17:09 +000039 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040 * @attr ref android.R.styleable#PreferenceGroup_orderingFromXml
41 */
42public 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;
Alan Viverette617feb92013-09-09 18:09:13 -070055
56 public PreferenceGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
57 super(context, attrs, defStyleAttr, defStyleRes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058
59 mPreferenceList = new ArrayList<Preference>();
60
Alan Viverette617feb92013-09-09 18:09:13 -070061 final TypedArray a = context.obtainStyledAttributes(
62 attrs, com.android.internal.R.styleable.PreferenceGroup, defStyleAttr, defStyleRes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063 mOrderingAsAdded = a.getBoolean(com.android.internal.R.styleable.PreferenceGroup_orderingFromXml,
64 mOrderingAsAdded);
65 a.recycle();
66 }
67
Alan Viverette617feb92013-09-09 18:09:13 -070068 public PreferenceGroup(Context context, AttributeSet attrs, int defStyleAttr) {
69 this(context, attrs, defStyleAttr, 0);
70 }
71
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072 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.
Filip Pavliscba64582017-01-10 19:17:09 +000083 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084 * @param orderingAsAdded Whether to order according to the order added.
85 * @see Preference#setOrder(int)
86 */
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.
Filip Pavliscba64582017-01-10 19:17:09 +000093 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094 * @return Whether this group orders based on the order the children are added.
95 * @see #setOrderingAsAdded(boolean)
96 */
97 public boolean isOrderingAsAdded() {
98 return mOrderingAsAdded;
99 }
100
101 /**
102 * Called by the inflater to add an item to this group.
103 */
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 */
112 public int getPreferenceCount() {
113 return mPreferenceList.size();
114 }
115
116 /**
117 * Returns the {@link Preference} at a particular index.
Filip Pavliscba64582017-01-10 19:17:09 +0000118 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119 * @param index The index of the {@link Preference} to retrieve.
120 * @return The {@link Preference}.
121 */
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.
Filip Pavliscba64582017-01-10 19:17:09 +0000129 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800130 * @param preference The preference to add.
131 * @return Whether the preference is now in this group.
132 */
133 public boolean addPreference(Preference preference) {
134 if (mPreferenceList.contains(preference)) {
135 // Exists
136 return true;
137 }
Filip Pavliscba64582017-01-10 19:17:09 +0000138
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139 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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151 if (!onPrepareAddPreference(preference)) {
152 return false;
153 }
154
155 synchronized(this) {
Daniel 2 Olofsson6689c902014-02-04 14:28:49 +0100156 int insertionIndex = Collections.binarySearch(mPreferenceList, preference);
157 if (insertionIndex < 0) {
158 insertionIndex = insertionIndex * -1 - 1;
159 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800160 mPreferenceList.add(insertionIndex, preference);
161 }
162
163 preference.onAttachedToHierarchy(getPreferenceManager());
Filip Pavliscba64582017-01-10 19:17:09 +0000164 preference.assignParent(this);
165
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166 if (mAttachedToActivity) {
167 preference.onAttachedToActivity();
168 }
Filip Pavliscba64582017-01-10 19:17:09 +0000169
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 notifyHierarchyChanged();
171
172 return true;
173 }
174
175 /**
176 * Removes a {@link Preference} from this group.
Filip Pavliscba64582017-01-10 19:17:09 +0000177 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178 * @param preference The preference to remove.
179 * @return Whether the preference was found and removed.
180 */
181 public boolean removePreference(Preference preference) {
182 final boolean returnValue = removePreferenceInt(preference);
183 notifyHierarchyChanged();
184 return returnValue;
185 }
186
187 private boolean removePreferenceInt(Preference preference) {
188 synchronized(this) {
189 preference.onPrepareForRemoval();
Filip Pavliscba64582017-01-10 19:17:09 +0000190 if (preference.getParent() == this) {
191 preference.assignParent(null);
192 }
Jason Monkeff3ca52016-04-04 13:19:31 +0000193 return mPreferenceList.remove(preference);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 }
195 }
Filip Pavliscba64582017-01-10 19:17:09 +0000196
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800197 /**
198 * Removes all {@link Preference Preferences} from this group.
199 */
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 }
Filip Pavliscba64582017-01-10 19:17:09 +0000209
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800210 /**
211 * Prepares a {@link Preference} to be added to the group.
Filip Pavliscba64582017-01-10 19:17:09 +0000212 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800213 * @param preference The preference to add.
214 * @return Whether to allow adding the preference (true), or not (false).
215 */
216 protected boolean onPrepareAddPreference(Preference preference) {
Alan Viverette651b4242013-09-05 13:32:40 -0700217 preference.onParentChanged(this, shouldDisableDependents());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 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}.
Filip Pavliscba64582017-01-10 19:17:09 +0000230 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 * @param key The key of the preference to retrieve.
232 * @return The {@link Preference} with the key, or null.
233 */
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 }
Filip Pavliscba64582017-01-10 19:17:09 +0000246
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800247 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.
Filip Pavliscba64582017-01-10 19:17:09 +0000262 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800263 * @return True if the contained preferences should be shown on the same
264 * screen as this preference.
265 */
266 protected boolean isOnSameScreenAsChildren() {
267 return true;
268 }
Filip Pavliscba64582017-01-10 19:17:09 +0000269
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800270 @Override
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;
Filip Pavliscba64582017-01-10 19:17:09 +0000277
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800278 // 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
Jason Monkeff3ca52016-04-04 13:19:31 +0000286 protected void onPrepareForRemoval() {
287 super.onPrepareForRemoval();
Filip Pavliscba64582017-01-10 19:17:09 +0000288
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800289 // We won't be attached to the activity anymore
290 mAttachedToActivity = false;
291 }
292
293 @Override
Alan Viverette02f56802013-08-19 16:32:56 -0700294 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.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800299 final int preferenceCount = getPreferenceCount();
300 for (int i = 0; i < preferenceCount; i++) {
Alan Viverette02f56802013-08-19 16:32:56 -0700301 getPreference(i).onParentChanged(this, disableDependents);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800302 }
303 }
Filip Pavliscba64582017-01-10 19:17:09 +0000304
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800305 void sortPreferences() {
306 synchronized (this) {
307 Collections.sort(mPreferenceList);
308 }
309 }
310
311 @Override
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 }
Filip Pavliscba64582017-01-10 19:17:09 +0000321
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800322 @Override
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}