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