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 android.app.Activity;
     20 import android.app.ListActivity;
     21 import android.content.Intent;
     22 import android.content.SharedPreferences;
     23 import android.os.Bundle;
     24 import android.os.Handler;
     25 import android.os.Message;
     26 import android.view.View;
     27 
     28 /**
     29  * Shows a hierarchy of {@link Preference} objects as
     30  * lists, possibly spanning multiple screens. These preferences will
     31  * automatically save to {@link SharedPreferences} as the user interacts with
     32  * them. To retrieve an instance of {@link SharedPreferences} that the
     33  * preference hierarchy in this activity will use, call
     34  * {@link PreferenceManager#getDefaultSharedPreferences(android.content.Context)}
     35  * with a context in the same package as this activity.
     36  * <p>
     37  * Furthermore, the preferences shown will follow the visual style of system
     38  * preferences. It is easy to create a hierarchy of preferences (that can be
     39  * shown on multiple screens) via XML. For these reasons, it is recommended to
     40  * use this activity (as a superclass) to deal with preferences in applications.
     41  * <p>
     42  * A {@link PreferenceScreen} object should be at the top of the preference
     43  * hierarchy. Furthermore, subsequent {@link PreferenceScreen} in the hierarchy
     44  * denote a screen break--that is the preferences contained within subsequent
     45  * {@link PreferenceScreen} should be shown on another screen. The preference
     46  * framework handles showing these other screens from the preference hierarchy.
     47  * <p>
     48  * The preference hierarchy can be formed in multiple ways:
     49  * <li> From an XML file specifying the hierarchy
     50  * <li> From different {@link Activity Activities} that each specify its own
     51  * preferences in an XML file via {@link Activity} meta-data
     52  * <li> From an object hierarchy rooted with {@link PreferenceScreen}
     53  * <p>
     54  * To inflate from XML, use the {@link #addPreferencesFromResource(int)}. The
     55  * root element should be a {@link PreferenceScreen}. Subsequent elements can point
     56  * to actual {@link Preference} subclasses. As mentioned above, subsequent
     57  * {@link PreferenceScreen} in the hierarchy will result in the screen break.
     58  * <p>
     59  * To specify an {@link Intent} to query {@link Activity Activities} that each
     60  * have preferences, use {@link #addPreferencesFromIntent}. Each
     61  * {@link Activity} can specify meta-data in the manifest (via the key
     62  * {@link PreferenceManager#METADATA_KEY_PREFERENCES}) that points to an XML
     63  * resource. These XML resources will be inflated into a single preference
     64  * hierarchy and shown by this activity.
     65  * <p>
     66  * To specify an object hierarchy rooted with {@link PreferenceScreen}, use
     67  * {@link #setPreferenceScreen(PreferenceScreen)}.
     68  * <p>
     69  * As a convenience, this activity implements a click listener for any
     70  * preference in the current hierarchy, see
     71  * {@link #onPreferenceTreeClick(PreferenceScreen, Preference)}.
     72  *
     73  * @see Preference
     74  * @see PreferenceScreen
     75  */
     76 public abstract class PreferenceActivity extends ListActivity implements
     77         PreferenceManager.OnPreferenceTreeClickListener {
     78 
     79     private static final String PREFERENCES_TAG = "android:preferences";
     80 
     81     private PreferenceManager mPreferenceManager;
     82 
     83     private Bundle mSavedInstanceState;
     84 
     85     /**
     86      * The starting request code given out to preference framework.
     87      */
     88     private static final int FIRST_REQUEST_CODE = 100;
     89 
     90     private static final int MSG_BIND_PREFERENCES = 0;
     91     private Handler mHandler = new Handler() {
     92         @Override
     93         public void handleMessage(Message msg) {
     94             switch (msg.what) {
     95 
     96                 case MSG_BIND_PREFERENCES:
     97                     bindPreferences();
     98                     break;
     99             }
    100         }
    101     };
    102 
    103     @Override
    104     protected void onCreate(Bundle savedInstanceState) {
    105         super.onCreate(savedInstanceState);
    106 
    107         setContentView(com.android.internal.R.layout.preference_list_content);
    108 
    109         mPreferenceManager = onCreatePreferenceManager();
    110         getListView().setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
    111     }
    112 
    113     @Override
    114     protected void onStop() {
    115         super.onStop();
    116 
    117         mPreferenceManager.dispatchActivityStop();
    118     }
    119 
    120     @Override
    121     protected void onDestroy() {
    122         super.onDestroy();
    123 
    124         mPreferenceManager.dispatchActivityDestroy();
    125     }
    126 
    127     @Override
    128     protected void onSaveInstanceState(Bundle outState) {
    129         super.onSaveInstanceState(outState);
    130 
    131         final PreferenceScreen preferenceScreen = getPreferenceScreen();
    132         if (preferenceScreen != null) {
    133             Bundle container = new Bundle();
    134             preferenceScreen.saveHierarchyState(container);
    135             outState.putBundle(PREFERENCES_TAG, container);
    136         }
    137     }
    138 
    139     @Override
    140     protected void onRestoreInstanceState(Bundle state) {
    141         Bundle container = state.getBundle(PREFERENCES_TAG);
    142         if (container != null) {
    143             final PreferenceScreen preferenceScreen = getPreferenceScreen();
    144             if (preferenceScreen != null) {
    145                 preferenceScreen.restoreHierarchyState(container);
    146                 mSavedInstanceState = state;
    147                 return;
    148             }
    149         }
    150 
    151         // Only call this if we didn't save the instance state for later.
    152         // If we did save it, it will be restored when we bind the adapter.
    153         super.onRestoreInstanceState(state);
    154     }
    155 
    156     @Override
    157     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    158         super.onActivityResult(requestCode, resultCode, data);
    159 
    160         mPreferenceManager.dispatchActivityResult(requestCode, resultCode, data);
    161     }
    162 
    163     @Override
    164     public void onContentChanged() {
    165         super.onContentChanged();
    166         postBindPreferences();
    167     }
    168 
    169     /**
    170      * Posts a message to bind the preferences to the list view.
    171      * <p>
    172      * Binding late is preferred as any custom preference types created in
    173      * {@link #onCreate(Bundle)} are able to have their views recycled.
    174      */
    175     private void postBindPreferences() {
    176         if (mHandler.hasMessages(MSG_BIND_PREFERENCES)) return;
    177         mHandler.obtainMessage(MSG_BIND_PREFERENCES).sendToTarget();
    178     }
    179 
    180     private void bindPreferences() {
    181         final PreferenceScreen preferenceScreen = getPreferenceScreen();
    182         if (preferenceScreen != null) {
    183             preferenceScreen.bind(getListView());
    184             if (mSavedInstanceState != null) {
    185                 super.onRestoreInstanceState(mSavedInstanceState);
    186                 mSavedInstanceState = null;
    187             }
    188         }
    189     }
    190 
    191     /**
    192      * Creates the {@link PreferenceManager}.
    193      *
    194      * @return The {@link PreferenceManager} used by this activity.
    195      */
    196     private PreferenceManager onCreatePreferenceManager() {
    197         PreferenceManager preferenceManager = new PreferenceManager(this, FIRST_REQUEST_CODE);
    198         preferenceManager.setOnPreferenceTreeClickListener(this);
    199         return preferenceManager;
    200     }
    201 
    202     /**
    203      * Returns the {@link PreferenceManager} used by this activity.
    204      * @return The {@link PreferenceManager}.
    205      */
    206     public PreferenceManager getPreferenceManager() {
    207         return mPreferenceManager;
    208     }
    209 
    210     private void requirePreferenceManager() {
    211         if (mPreferenceManager == null) {
    212             throw new RuntimeException("This should be called after super.onCreate.");
    213         }
    214     }
    215 
    216     /**
    217      * Sets the root of the preference hierarchy that this activity is showing.
    218      *
    219      * @param preferenceScreen The root {@link PreferenceScreen} of the preference hierarchy.
    220      */
    221     public void setPreferenceScreen(PreferenceScreen preferenceScreen) {
    222         if (mPreferenceManager.setPreferences(preferenceScreen) && preferenceScreen != null) {
    223             postBindPreferences();
    224             CharSequence title = getPreferenceScreen().getTitle();
    225             // Set the title of the activity
    226             if (title != null) {
    227                 setTitle(title);
    228             }
    229         }
    230     }
    231 
    232     /**
    233      * Gets the root of the preference hierarchy that this activity is showing.
    234      *
    235      * @return The {@link PreferenceScreen} that is the root of the preference
    236      *         hierarchy.
    237      */
    238     public PreferenceScreen getPreferenceScreen() {
    239         return mPreferenceManager.getPreferenceScreen();
    240     }
    241 
    242     /**
    243      * Adds preferences from activities that match the given {@link Intent}.
    244      *
    245      * @param intent The {@link Intent} to query activities.
    246      */
    247     public void addPreferencesFromIntent(Intent intent) {
    248         requirePreferenceManager();
    249 
    250         setPreferenceScreen(mPreferenceManager.inflateFromIntent(intent, getPreferenceScreen()));
    251     }
    252 
    253     /**
    254      * Inflates the given XML resource and adds the preference hierarchy to the current
    255      * preference hierarchy.
    256      *
    257      * @param preferencesResId The XML resource ID to inflate.
    258      */
    259     public void addPreferencesFromResource(int preferencesResId) {
    260         requirePreferenceManager();
    261 
    262         setPreferenceScreen(mPreferenceManager.inflateFromResource(this, preferencesResId,
    263                 getPreferenceScreen()));
    264     }
    265 
    266     /**
    267      * {@inheritDoc}
    268      */
    269     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
    270         return false;
    271     }
    272 
    273     /**
    274      * Finds a {@link Preference} based on its key.
    275      *
    276      * @param key The key of the preference to retrieve.
    277      * @return The {@link Preference} with the key, or null.
    278      * @see PreferenceGroup#findPreference(CharSequence)
    279      */
    280     public Preference findPreference(CharSequence key) {
    281 
    282         if (mPreferenceManager == null) {
    283             return null;
    284         }
    285 
    286         return mPreferenceManager.findPreference(key);
    287     }
    288 
    289     @Override
    290     protected void onNewIntent(Intent intent) {
    291         if (mPreferenceManager != null) {
    292             mPreferenceManager.dispatchNewIntent(intent);
    293         }
    294     }
    295 
    296 }
    297