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 onCreate(Bundle savedInstanceState)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 onStop()114 protected void onStop() { 115 super.onStop(); 116 117 mPreferenceManager.dispatchActivityStop(); 118 } 119 120 @Override onDestroy()121 protected void onDestroy() { 122 super.onDestroy(); 123 124 mPreferenceManager.dispatchActivityDestroy(); 125 } 126 127 @Override onSaveInstanceState(Bundle outState)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 onRestoreInstanceState(Bundle state)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 onActivityResult(int requestCode, int resultCode, Intent data)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 onContentChanged()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 */ postBindPreferences()175 private void postBindPreferences() { 176 if (mHandler.hasMessages(MSG_BIND_PREFERENCES)) return; 177 mHandler.obtainMessage(MSG_BIND_PREFERENCES).sendToTarget(); 178 } 179 bindPreferences()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 */ onCreatePreferenceManager()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 */ getPreferenceManager()206 public PreferenceManager getPreferenceManager() { 207 return mPreferenceManager; 208 } 209 requirePreferenceManager()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 */ setPreferenceScreen(PreferenceScreen preferenceScreen)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 */ getPreferenceScreen()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 */ addPreferencesFromIntent(Intent intent)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 */ addPreferencesFromResource(int preferencesResId)259 public void addPreferencesFromResource(int preferencesResId) { 260 requirePreferenceManager(); 261 262 setPreferenceScreen(mPreferenceManager.inflateFromResource(this, preferencesResId, 263 getPreferenceScreen())); 264 } 265 266 /** 267 * {@inheritDoc} 268 */ onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference)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 */ findPreference(CharSequence key)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 onNewIntent(Intent intent)290 protected void onNewIntent(Intent intent) { 291 if (mPreferenceManager != null) { 292 mPreferenceManager.dispatchNewIntent(intent); 293 } 294 } 295 296 } 297