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 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 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 */ 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 */ 89 public boolean isOrderingAsAdded() { 90 return mOrderingAsAdded; 91 } 92 93 /** 94 * Called by the inflater to add an item to this group. 95 */ 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 */ 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 */ 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 */ 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 */ 173 public boolean removePreference(Preference preference) { 174 final boolean returnValue = removePreferenceInt(preference); 175 notifyHierarchyChanged(); 176 return returnValue; 177 } 178 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 */ 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 */ 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 */ 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 */ 258 protected boolean isOnSameScreenAsChildren() { 259 return true; 260 } 261 262 @Override 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 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 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 296 void sortPreferences() { 297 synchronized (this) { 298 Collections.sort(mPreferenceList); 299 } 300 } 301 302 @Override 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 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