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 android.content.Context;
     23 import android.content.res.TypedArray;
     24 import android.os.Bundle;
     25 import android.text.TextUtils;
     26 import 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}.
     32  *
     33  * <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>
     39  *
     40  * @attr ref android.R.styleable#PreferenceGroup_orderingFromXml
     41  */
     42 public 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;
     55 
     56     public PreferenceGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
     57         super(context, attrs, defStyleAttr, defStyleRes);
     58 
     59         mPreferenceList = new ArrayList<Preference>();
     60 
     61         final TypedArray a = context.obtainStyledAttributes(
     62                 attrs, com.android.internal.R.styleable.PreferenceGroup, defStyleAttr, defStyleRes);
     63         mOrderingAsAdded = a.getBoolean(com.android.internal.R.styleable.PreferenceGroup_orderingFromXml,
     64                 mOrderingAsAdded);
     65         a.recycle();
     66     }
     67 
     68     public PreferenceGroup(Context context, AttributeSet attrs, int defStyleAttr) {
     69         this(context, attrs, defStyleAttr, 0);
     70     }
     71 
     72     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.
     83      *
     84      * @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.
     93      *
     94      * @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.
    118      *
    119      * @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.
    129      *
    130      * @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         }
    138 
    139         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 
    151         if (!onPrepareAddPreference(preference)) {
    152             return false;
    153         }
    154 
    155         synchronized(this) {
    156             int insertionIndex = Collections.binarySearch(mPreferenceList, preference);
    157             if (insertionIndex < 0) {
    158                 insertionIndex = insertionIndex * -1 - 1;
    159             }
    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      */
    180     public boolean removePreference(Preference preference) {
    181         final boolean returnValue = removePreferenceInt(preference);
    182         notifyHierarchyChanged();
    183         return returnValue;
    184     }
    185 
    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      */
    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      */
    212     protected boolean onPrepareAddPreference(Preference preference) {
    213         preference.onParentChanged(this, shouldDisableDependents());
    214         return true;
    215     }
    216 
    217     /**
    218      * Finds a {@link Preference} based on its key. If two {@link Preference}
    219      * share the same key (not recommended), the first to appear will be
    220      * returned (to retrieve the other preference with the same key, call this
    221      * method on the first preference). If this preference has the key, it will
    222      * not be returned.
    223      * <p>
    224      * This will recursively search for the preference into children that are
    225      * also {@link PreferenceGroup PreferenceGroups}.
    226      *
    227      * @param key The key of the preference to retrieve.
    228      * @return The {@link Preference} with the key, or null.
    229      */
    230     public Preference findPreference(CharSequence key) {
    231         if (TextUtils.equals(getKey(), key)) {
    232             return this;
    233         }
    234         final int preferenceCount = getPreferenceCount();
    235         for (int i = 0; i < preferenceCount; i++) {
    236             final Preference preference = getPreference(i);
    237             final String curKey = preference.getKey();
    238 
    239             if (curKey != null && curKey.equals(key)) {
    240                 return preference;
    241             }
    242 
    243             if (preference instanceof PreferenceGroup) {
    244                 final Preference returnedPreference = ((PreferenceGroup)preference)
    245                         .findPreference(key);
    246                 if (returnedPreference != null) {
    247                     return returnedPreference;
    248                 }
    249             }
    250         }
    251 
    252         return null;
    253     }
    254 
    255     /**
    256      * Whether this preference group should be shown on the same screen as its
    257      * contained preferences.
    258      *
    259      * @return True if the contained preferences should be shown on the same
    260      *         screen as this preference.
    261      */
    262     protected boolean isOnSameScreenAsChildren() {
    263         return true;
    264     }
    265 
    266     @Override
    267     protected void onAttachedToActivity() {
    268         super.onAttachedToActivity();
    269 
    270         // Mark as attached so if a preference is later added to this group, we
    271         // can tell it we are already attached
    272         mAttachedToActivity = true;
    273 
    274         // Dispatch to all contained preferences
    275         final int preferenceCount = getPreferenceCount();
    276         for (int i = 0; i < preferenceCount; i++) {
    277             getPreference(i).onAttachedToActivity();
    278         }
    279     }
    280 
    281     @Override
    282     protected void onPrepareForRemoval() {
    283         super.onPrepareForRemoval();
    284 
    285         // We won't be attached to the activity anymore
    286         mAttachedToActivity = false;
    287     }
    288 
    289     @Override
    290     public void notifyDependencyChange(boolean disableDependents) {
    291         super.notifyDependencyChange(disableDependents);
    292 
    293         // Child preferences have an implicit dependency on their containing
    294         // group. Dispatch dependency change to all contained preferences.
    295         final int preferenceCount = getPreferenceCount();
    296         for (int i = 0; i < preferenceCount; i++) {
    297             getPreference(i).onParentChanged(this, disableDependents);
    298         }
    299     }
    300 
    301     void sortPreferences() {
    302         synchronized (this) {
    303             Collections.sort(mPreferenceList);
    304         }
    305     }
    306 
    307     @Override
    308     protected void dispatchSaveInstanceState(Bundle container) {
    309         super.dispatchSaveInstanceState(container);
    310 
    311         // Dispatch to all contained preferences
    312         final int preferenceCount = getPreferenceCount();
    313         for (int i = 0; i < preferenceCount; i++) {
    314             getPreference(i).dispatchSaveInstanceState(container);
    315         }
    316     }
    317 
    318     @Override
    319     protected void dispatchRestoreInstanceState(Bundle container) {
    320         super.dispatchRestoreInstanceState(container);
    321 
    322         // Dispatch to all contained preferences
    323         final int preferenceCount = getPreferenceCount();
    324         for (int i = 0; i < preferenceCount; i++) {
    325             getPreference(i).dispatchRestoreInstanceState(container);
    326         }
    327     }
    328 
    329 }
    330