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  * @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