| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.preference; |
| |
| import android.app.Activity; |
| import android.app.Fragment; |
| import android.content.Intent; |
| import android.content.SharedPreferences; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.Message; |
| import android.view.KeyEvent; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.view.View.OnKeyListener; |
| import android.widget.ListView; |
| |
| /** |
| * Shows a hierarchy of {@link Preference} objects as |
| * lists. These preferences will |
| * automatically save to {@link SharedPreferences} as the user interacts with |
| * them. To retrieve an instance of {@link SharedPreferences} that the |
| * preference hierarchy in this fragment will use, call |
| * {@link PreferenceManager#getDefaultSharedPreferences(android.content.Context)} |
| * with a context in the same package as this fragment. |
| * <p> |
| * Furthermore, the preferences shown will follow the visual style of system |
| * preferences. It is easy to create a hierarchy of preferences (that can be |
| * shown on multiple screens) via XML. For these reasons, it is recommended to |
| * use this fragment (as a superclass) to deal with preferences in applications. |
| * <p> |
| * A {@link PreferenceScreen} object should be at the top of the preference |
| * hierarchy. Furthermore, subsequent {@link PreferenceScreen} in the hierarchy |
| * denote a screen break--that is the preferences contained within subsequent |
| * {@link PreferenceScreen} should be shown on another screen. The preference |
| * framework handles showing these other screens from the preference hierarchy. |
| * <p> |
| * The preference hierarchy can be formed in multiple ways: |
| * <li> From an XML file specifying the hierarchy |
| * <li> From different {@link Activity Activities} that each specify its own |
| * preferences in an XML file via {@link Activity} meta-data |
| * <li> From an object hierarchy rooted with {@link PreferenceScreen} |
| * <p> |
| * To inflate from XML, use the {@link #addPreferencesFromResource(int)}. The |
| * root element should be a {@link PreferenceScreen}. Subsequent elements can point |
| * to actual {@link Preference} subclasses. As mentioned above, subsequent |
| * {@link PreferenceScreen} in the hierarchy will result in the screen break. |
| * <p> |
| * To specify an {@link Intent} to query {@link Activity Activities} that each |
| * have preferences, use {@link #addPreferencesFromIntent}. Each |
| * {@link Activity} can specify meta-data in the manifest (via the key |
| * {@link PreferenceManager#METADATA_KEY_PREFERENCES}) that points to an XML |
| * resource. These XML resources will be inflated into a single preference |
| * hierarchy and shown by this fragment. |
| * <p> |
| * To specify an object hierarchy rooted with {@link PreferenceScreen}, use |
| * {@link #setPreferenceScreen(PreferenceScreen)}. |
| * <p> |
| * As a convenience, this fragment implements a click listener for any |
| * preference in the current hierarchy, see |
| * {@link #onPreferenceTreeClick(PreferenceScreen, Preference)}. |
| * |
| * <a name="SampleCode"></a> |
| * <h3>Sample Code</h3> |
| * |
| * <p>The following sample code shows a simple preference fragment that is |
| * populated from a resource. The resource it loads is:</p> |
| * |
| * {@sample development/samples/ApiDemos/res/xml/preferences.xml preferences} |
| * |
| * <p>The fragment implementation itself simply populates the preferences |
| * when created. Note that the preferences framework takes care of loading |
| * the current values out of the app preferences and writing them when changed:</p> |
| * |
| * {@sample development/samples/ApiDemos/src/com/example/android/apis/preference/FragmentPreferences.java |
| * fragment} |
| * |
| * @see Preference |
| * @see PreferenceScreen |
| */ |
| public abstract class PreferenceFragment extends Fragment implements |
| PreferenceManager.OnPreferenceTreeClickListener { |
| |
| private static final String PREFERENCES_TAG = "android:preferences"; |
| |
| private PreferenceManager mPreferenceManager; |
| private ListView mList; |
| private boolean mHavePrefs; |
| private boolean mInitDone; |
| |
| /** |
| * The starting request code given out to preference framework. |
| */ |
| private static final int FIRST_REQUEST_CODE = 100; |
| |
| private static final int MSG_BIND_PREFERENCES = 1; |
| private Handler mHandler = new Handler() { |
| @Override |
| public void handleMessage(Message msg) { |
| switch (msg.what) { |
| |
| case MSG_BIND_PREFERENCES: |
| bindPreferences(); |
| break; |
| } |
| } |
| }; |
| |
| final private Runnable mRequestFocus = new Runnable() { |
| public void run() { |
| mList.focusableViewAvailable(mList); |
| } |
| }; |
| |
| /** |
| * Interface that PreferenceFragment's containing activity should |
| * implement to be able to process preference items that wish to |
| * switch to a new fragment. |
| */ |
| public interface OnPreferenceStartFragmentCallback { |
| /** |
| * Called when the user has clicked on a Preference that has |
| * a fragment class name associated with it. The implementation |
| * to should instantiate and switch to an instance of the given |
| * fragment. |
| */ |
| boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref); |
| } |
| |
| @Override |
| public void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| mPreferenceManager = new PreferenceManager(getActivity(), FIRST_REQUEST_CODE); |
| mPreferenceManager.setFragment(this); |
| } |
| |
| @Override |
| public View onCreateView(LayoutInflater inflater, ViewGroup container, |
| Bundle savedInstanceState) { |
| return inflater.inflate(com.android.internal.R.layout.preference_list_fragment, container, |
| false); |
| } |
| |
| @Override |
| public void onActivityCreated(Bundle savedInstanceState) { |
| super.onActivityCreated(savedInstanceState); |
| getListView().setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY); |
| |
| if (mHavePrefs) { |
| bindPreferences(); |
| } |
| |
| mInitDone = true; |
| |
| if (savedInstanceState != null) { |
| Bundle container = savedInstanceState.getBundle(PREFERENCES_TAG); |
| if (container != null) { |
| final PreferenceScreen preferenceScreen = getPreferenceScreen(); |
| if (preferenceScreen != null) { |
| preferenceScreen.restoreHierarchyState(container); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void onStart() { |
| super.onStart(); |
| mPreferenceManager.setOnPreferenceTreeClickListener(this); |
| } |
| |
| @Override |
| public void onStop() { |
| super.onStop(); |
| mPreferenceManager.dispatchActivityStop(); |
| mPreferenceManager.setOnPreferenceTreeClickListener(null); |
| } |
| |
| @Override |
| public void onDestroyView() { |
| mList = null; |
| mHandler.removeCallbacks(mRequestFocus); |
| mHandler.removeMessages(MSG_BIND_PREFERENCES); |
| super.onDestroyView(); |
| } |
| |
| @Override |
| public void onDestroy() { |
| super.onDestroy(); |
| mPreferenceManager.dispatchActivityDestroy(); |
| } |
| |
| @Override |
| public void onSaveInstanceState(Bundle outState) { |
| super.onSaveInstanceState(outState); |
| |
| final PreferenceScreen preferenceScreen = getPreferenceScreen(); |
| if (preferenceScreen != null) { |
| Bundle container = new Bundle(); |
| preferenceScreen.saveHierarchyState(container); |
| outState.putBundle(PREFERENCES_TAG, container); |
| } |
| } |
| |
| @Override |
| public void onActivityResult(int requestCode, int resultCode, Intent data) { |
| super.onActivityResult(requestCode, resultCode, data); |
| |
| mPreferenceManager.dispatchActivityResult(requestCode, resultCode, data); |
| } |
| |
| /** |
| * Returns the {@link PreferenceManager} used by this fragment. |
| * @return The {@link PreferenceManager}. |
| */ |
| public PreferenceManager getPreferenceManager() { |
| return mPreferenceManager; |
| } |
| |
| /** |
| * Sets the root of the preference hierarchy that this fragment is showing. |
| * |
| * @param preferenceScreen The root {@link PreferenceScreen} of the preference hierarchy. |
| */ |
| public void setPreferenceScreen(PreferenceScreen preferenceScreen) { |
| if (mPreferenceManager.setPreferences(preferenceScreen) && preferenceScreen != null) { |
| mHavePrefs = true; |
| if (mInitDone) { |
| postBindPreferences(); |
| } |
| } |
| } |
| |
| /** |
| * Gets the root of the preference hierarchy that this fragment is showing. |
| * |
| * @return The {@link PreferenceScreen} that is the root of the preference |
| * hierarchy. |
| */ |
| public PreferenceScreen getPreferenceScreen() { |
| return mPreferenceManager.getPreferenceScreen(); |
| } |
| |
| /** |
| * Adds preferences from activities that match the given {@link Intent}. |
| * |
| * @param intent The {@link Intent} to query activities. |
| */ |
| public void addPreferencesFromIntent(Intent intent) { |
| requirePreferenceManager(); |
| |
| setPreferenceScreen(mPreferenceManager.inflateFromIntent(intent, getPreferenceScreen())); |
| } |
| |
| /** |
| * Inflates the given XML resource and adds the preference hierarchy to the current |
| * preference hierarchy. |
| * |
| * @param preferencesResId The XML resource ID to inflate. |
| */ |
| public void addPreferencesFromResource(int preferencesResId) { |
| requirePreferenceManager(); |
| |
| setPreferenceScreen(mPreferenceManager.inflateFromResource(getActivity(), |
| preferencesResId, getPreferenceScreen())); |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, |
| Preference preference) { |
| if (preference.getFragment() != null && |
| getActivity() instanceof OnPreferenceStartFragmentCallback) { |
| return ((OnPreferenceStartFragmentCallback)getActivity()).onPreferenceStartFragment( |
| this, preference); |
| } |
| return false; |
| } |
| |
| /** |
| * Finds a {@link Preference} based on its key. |
| * |
| * @param key The key of the preference to retrieve. |
| * @return The {@link Preference} with the key, or null. |
| * @see PreferenceGroup#findPreference(CharSequence) |
| */ |
| public Preference findPreference(CharSequence key) { |
| if (mPreferenceManager == null) { |
| return null; |
| } |
| return mPreferenceManager.findPreference(key); |
| } |
| |
| private void requirePreferenceManager() { |
| if (mPreferenceManager == null) { |
| throw new RuntimeException("This should be called after super.onCreate."); |
| } |
| } |
| |
| private void postBindPreferences() { |
| if (mHandler.hasMessages(MSG_BIND_PREFERENCES)) return; |
| mHandler.obtainMessage(MSG_BIND_PREFERENCES).sendToTarget(); |
| } |
| |
| private void bindPreferences() { |
| final PreferenceScreen preferenceScreen = getPreferenceScreen(); |
| if (preferenceScreen != null) { |
| preferenceScreen.bind(getListView()); |
| } |
| } |
| |
| /** @hide */ |
| public ListView getListView() { |
| ensureList(); |
| return mList; |
| } |
| |
| private void ensureList() { |
| if (mList != null) { |
| return; |
| } |
| View root = getView(); |
| if (root == null) { |
| throw new IllegalStateException("Content view not yet created"); |
| } |
| View rawListView = root.findViewById(android.R.id.list); |
| if (!(rawListView instanceof ListView)) { |
| throw new RuntimeException( |
| "Content has view with id attribute 'android.R.id.list' " |
| + "that is not a ListView class"); |
| } |
| mList = (ListView)rawListView; |
| if (mList == null) { |
| throw new RuntimeException( |
| "Your content must have a ListView whose id attribute is " + |
| "'android.R.id.list'"); |
| } |
| mList.setOnKeyListener(mListOnKeyListener); |
| mHandler.post(mRequestFocus); |
| } |
| |
| private OnKeyListener mListOnKeyListener = new OnKeyListener() { |
| |
| @Override |
| public boolean onKey(View v, int keyCode, KeyEvent event) { |
| Object selectedItem = mList.getSelectedItem(); |
| if (selectedItem instanceof Preference) { |
| View selectedView = mList.getSelectedView(); |
| return ((Preference)selectedItem).onKey( |
| selectedView, keyCode, event); |
| } |
| return false; |
| } |
| |
| }; |
| } |