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