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 defStyle)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 PreferenceGroup(Context context, AttributeSet attrs)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 */ setOrderingAsAdded(boolean orderingAsAdded)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 */ isOrderingAsAdded()96 public boolean isOrderingAsAdded() { 97 return mOrderingAsAdded; 98 } 99 100 /** 101 * Called by the inflater to add an item to this group. 102 */ addItemFromInflater(Preference preference)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 */ getPreferenceCount()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 */ getPreference(int index)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 */ addPreference(Preference preference)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 */ removePreference(Preference preference)180 public boolean removePreference(Preference preference) { 181 final boolean returnValue = removePreferenceInt(preference); 182 notifyHierarchyChanged(); 183 return returnValue; 184 } 185 removePreferenceInt(Preference preference)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 */ removeAll()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 */ onPrepareAddPreference(Preference preference)212 protected boolean onPrepareAddPreference(Preference preference) { 213 if (!super.isEnabled()) { 214 preference.setEnabled(false); 215 } 216 217 return true; 218 } 219 220 /** 221 * Finds a {@link Preference} based on its key. If two {@link Preference} 222 * share the same key (not recommended), the first to appear will be 223 * returned (to retrieve the other preference with the same key, call this 224 * method on the first preference). If this preference has the key, it will 225 * not be returned. 226 * <p> 227 * This will recursively search for the preference into children that are 228 * also {@link PreferenceGroup PreferenceGroups}. 229 * 230 * @param key The key of the preference to retrieve. 231 * @return The {@link Preference} with the key, or null. 232 */ findPreference(CharSequence key)233 public Preference findPreference(CharSequence key) { 234 if (TextUtils.equals(getKey(), key)) { 235 return this; 236 } 237 final int preferenceCount = getPreferenceCount(); 238 for (int i = 0; i < preferenceCount; i++) { 239 final Preference preference = getPreference(i); 240 final String curKey = preference.getKey(); 241 242 if (curKey != null && curKey.equals(key)) { 243 return preference; 244 } 245 246 if (preference instanceof PreferenceGroup) { 247 final Preference returnedPreference = ((PreferenceGroup)preference) 248 .findPreference(key); 249 if (returnedPreference != null) { 250 return returnedPreference; 251 } 252 } 253 } 254 255 return null; 256 } 257 258 /** 259 * Whether this preference group should be shown on the same screen as its 260 * contained preferences. 261 * 262 * @return True if the contained preferences should be shown on the same 263 * screen as this preference. 264 */ isOnSameScreenAsChildren()265 protected boolean isOnSameScreenAsChildren() { 266 return true; 267 } 268 269 @Override onAttachedToActivity()270 protected void onAttachedToActivity() { 271 super.onAttachedToActivity(); 272 273 // Mark as attached so if a preference is later added to this group, we 274 // can tell it we are already attached 275 mAttachedToActivity = true; 276 277 // Dispatch to all contained preferences 278 final int preferenceCount = getPreferenceCount(); 279 for (int i = 0; i < preferenceCount; i++) { 280 getPreference(i).onAttachedToActivity(); 281 } 282 } 283 284 @Override onPrepareForRemoval()285 protected void onPrepareForRemoval() { 286 super.onPrepareForRemoval(); 287 288 // We won't be attached to the activity anymore 289 mAttachedToActivity = false; 290 } 291 292 @Override setEnabled(boolean enabled)293 public void setEnabled(boolean enabled) { 294 super.setEnabled(enabled); 295 296 // Dispatch to all contained preferences 297 final int preferenceCount = getPreferenceCount(); 298 for (int i = 0; i < preferenceCount; i++) { 299 getPreference(i).setEnabled(enabled); 300 } 301 } 302 sortPreferences()303 void sortPreferences() { 304 synchronized (this) { 305 Collections.sort(mPreferenceList); 306 } 307 } 308 309 @Override dispatchSaveInstanceState(Bundle container)310 protected void dispatchSaveInstanceState(Bundle container) { 311 super.dispatchSaveInstanceState(container); 312 313 // Dispatch to all contained preferences 314 final int preferenceCount = getPreferenceCount(); 315 for (int i = 0; i < preferenceCount; i++) { 316 getPreference(i).dispatchSaveInstanceState(container); 317 } 318 } 319 320 @Override dispatchRestoreInstanceState(Bundle container)321 protected void dispatchRestoreInstanceState(Bundle container) { 322 super.dispatchRestoreInstanceState(container); 323 324 // Dispatch to all contained preferences 325 final int preferenceCount = getPreferenceCount(); 326 for (int i = 0; i < preferenceCount; i++) { 327 getPreference(i).dispatchRestoreInstanceState(container); 328 } 329 } 330 331 } 332