Home | History | Annotate | Download | only in preference
      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 
     59     public PreferenceGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
     60         super(context, attrs, defStyleAttr, defStyleRes);
     61 
     62         mPreferenceList = new ArrayList<Preference>();
     63 
     64         final TypedArray a = context.obtainStyledAttributes(
     65                 attrs, com.android.internal.R.styleable.PreferenceGroup, defStyleAttr, defStyleRes);
     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, int defStyleAttr) {
     72         this(context, attrs, defStyleAttr, 0);
     73     }
     74 
     75     public PreferenceGroup(Context context, AttributeSet attrs) {
     76         this(context, attrs, 0);
     77     }
     78 
     79     /**
     80      * Whether to order the {@link Preference} children of this group as they
     81      * are added. If this is false, the ordering will follow each Preference
     82      * order and default to alphabetic for those without an order.
     83      * <p>
     84      * If this is called after preferences are added, they will not be
     85      * re-ordered in the order they were added, hence call this method early on.
     86      *
     87      * @param orderingAsAdded Whether to order according to the order added.
     88      * @see Preference#setOrder(int)
     89      */
     90     public void setOrderingAsAdded(boolean orderingAsAdded) {
     91         mOrderingAsAdded = orderingAsAdded;
     92     }
     93 
     94     /**
     95      * Whether this group is ordering preferences in the order they are added.
     96      *
     97      * @return Whether this group orders based on the order the children are added.
     98      * @see #setOrderingAsAdded(boolean)
     99      */
    100     public boolean isOrderingAsAdded() {
    101         return mOrderingAsAdded;
    102     }
    103 
    104     /**
    105      * Called by the inflater to add an item to this group.
    106      */
    107     public void addItemFromInflater(Preference preference) {
    108         addPreference(preference);
    109     }
    110 
    111     /**
    112      * Returns the number of children {@link Preference}s.
    113      * @return The number of preference children in this group.
    114      */
    115     public int getPreferenceCount() {
    116         return mPreferenceList.size();
    117     }
    118 
    119     /**
    120      * Returns the {@link Preference} at a particular index.
    121      *
    122      * @param index The index of the {@link Preference} to retrieve.
    123      * @return The {@link Preference}.
    124      */
    125     public Preference getPreference(int index) {
    126         return mPreferenceList.get(index);
    127     }
    128 
    129     /**
    130      * Adds a {@link Preference} at the correct position based on the
    131      * preference's order.
    132      *
    133      * @param preference The preference to add.
    134      * @return Whether the preference is now in this group.
    135      */
    136     public boolean addPreference(Preference preference) {
    137         if (mPreferenceList.contains(preference)) {
    138             // Exists
    139             return true;
    140         }
    141 
    142         if (preference.getOrder() == Preference.DEFAULT_ORDER) {
    143             if (mOrderingAsAdded) {
    144                 preference.setOrder(mCurrentPreferenceOrder++);
    145             }
    146 
    147             if (preference instanceof PreferenceGroup) {
    148                 // TODO: fix (method is called tail recursively when inflating,
    149                 // so we won't end up properly passing this flag down to children
    150                 ((PreferenceGroup)preference).setOrderingAsAdded(mOrderingAsAdded);
    151             }
    152         }
    153 
    154         int insertionIndex = Collections.binarySearch(mPreferenceList, preference);
    155         if (insertionIndex < 0) {
    156             insertionIndex = insertionIndex * -1 - 1;
    157         }
    158 
    159         if (!onPrepareAddPreference(preference)) {
    160             return false;
    161         }
    162 
    163         synchronized(this) {
    164             mPreferenceList.add(insertionIndex, preference);
    165         }
    166 
    167         preference.onAttachedToHierarchy(getPreferenceManager());
    168 
    169         if (mAttachedToActivity) {
    170             preference.onAttachedToActivity();
    171         }
    172 
    173         notifyHierarchyChanged();
    174 
    175         return true;
    176     }
    177 
    178     /**
    179      * Removes a {@link Preference} from this group.
    180      *
    181      * @param preference The preference to remove.
    182      * @return Whether the preference was found and removed.
    183      */
    184     public boolean removePreference(Preference preference) {
    185         final boolean returnValue = removePreferenceInt(preference);
    186         notifyHierarchyChanged();
    187         return returnValue;
    188     }
    189 
    190     private boolean removePreferenceInt(Preference preference) {
    191         synchronized(this) {
    192             preference.onPrepareForRemoval();
    193             return mPreferenceList.remove(preference);
    194         }
    195     }
    196 
    197     /**
    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     }
    209 
    210     /**
    211      * Prepares a {@link Preference} to be added to the group.
    212      *
    213      * @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) {
    217         preference.onParentChanged(this, shouldDisableDependents());
    218         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}.
    230      *
    231      * @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             }
    246 
    247             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.
    262      *
    263      * @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     }
    269 
    270     @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;
    277 
    278         // 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
    286     protected void onPrepareForRemoval() {
    287         super.onPrepareForRemoval();
    288 
    289         // We won't be attached to the activity anymore
    290         mAttachedToActivity = false;
    291     }
    292 
    293     @Override
    294     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.
    299         final int preferenceCount = getPreferenceCount();
    300         for (int i = 0; i < preferenceCount; i++) {
    301             getPreference(i).onParentChanged(this, disableDependents);
    302         }
    303     }
    304 
    305     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     }
    321 
    322     @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 }
    334