michael@0: /* michael@0: * Copyright (C) 2013 The Android Open Source Project michael@0: * michael@0: * Licensed under the Apache License, Version 2.0 (the "License"); michael@0: * you may not use this file except in compliance with the License. michael@0: * You may obtain a copy of the License at michael@0: * michael@0: * http://www.apache.org/licenses/LICENSE-2.0 michael@0: * michael@0: * Unless required by applicable law or agreed to in writing, software michael@0: * distributed under the License is distributed on an "AS IS" BASIS, michael@0: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. michael@0: * See the License for the specific language governing permissions and michael@0: * limitations under the License. michael@0: */ michael@0: michael@0: package org.mozilla.gecko.background.preferences; michael@0: michael@0: import org.mozilla.gecko.R; michael@0: michael@0: import android.content.Intent; michael@0: import android.os.Bundle; michael@0: import android.os.Handler; michael@0: import android.os.Message; michael@0: import android.preference.Preference; michael@0: import android.preference.PreferenceGroup; michael@0: import android.preference.PreferenceManager; michael@0: import android.preference.PreferenceScreen; michael@0: import android.support.v4.app.Fragment; michael@0: import android.view.KeyEvent; michael@0: import android.view.LayoutInflater; michael@0: import android.view.View; michael@0: import android.view.View.OnKeyListener; michael@0: import android.view.ViewGroup; michael@0: import android.widget.ListView; michael@0: michael@0: public abstract class PreferenceFragment extends Fragment implements PreferenceManagerCompat.OnPreferenceTreeClickListener { michael@0: private static final String PREFERENCES_TAG = "android:preferences"; michael@0: michael@0: private PreferenceManager mPreferenceManager; michael@0: private ListView mList; michael@0: private boolean mHavePrefs; michael@0: private boolean mInitDone; michael@0: michael@0: /** michael@0: * The starting request code given out to preference framework. michael@0: */ michael@0: private static final int FIRST_REQUEST_CODE = 100; michael@0: michael@0: private static final int MSG_BIND_PREFERENCES = 1; michael@0: michael@0: // This triggers "This Handler class should be static or leaks might occur". michael@0: // The issue is that the Handler references the Fragment; messages targeting michael@0: // the Handler reference it; and if such messages are long lived, the Fragment michael@0: // cannot be GCed. This is not an issue for us; our messages are short-lived. michael@0: private Handler mHandler = new Handler() { michael@0: @Override michael@0: public void handleMessage(Message msg) { michael@0: switch (msg.what) { michael@0: michael@0: case MSG_BIND_PREFERENCES: michael@0: bindPreferences(); michael@0: break; michael@0: } michael@0: } michael@0: }; michael@0: michael@0: final private Runnable mRequestFocus = new Runnable() { michael@0: @Override michael@0: public void run() { michael@0: mList.focusableViewAvailable(mList); michael@0: } michael@0: }; michael@0: michael@0: /** michael@0: * Interface that PreferenceFragment's containing activity should michael@0: * implement to be able to process preference items that wish to michael@0: * switch to a new fragment. michael@0: */ michael@0: public interface OnPreferenceStartFragmentCallback { michael@0: /** michael@0: * Called when the user has clicked on a Preference that has michael@0: * a fragment class name associated with it. The implementation michael@0: * to should instantiate and switch to an instance of the given michael@0: * fragment. michael@0: */ michael@0: boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref); michael@0: } michael@0: michael@0: @Override michael@0: public void onCreate(Bundle paramBundle) { michael@0: super.onCreate(paramBundle); michael@0: mPreferenceManager = PreferenceManagerCompat.newInstance(getActivity(), FIRST_REQUEST_CODE); michael@0: PreferenceManagerCompat.setFragment(mPreferenceManager, this); michael@0: } michael@0: michael@0: @Override michael@0: public View onCreateView(LayoutInflater paramLayoutInflater, ViewGroup paramViewGroup, Bundle paramBundle) { michael@0: return paramLayoutInflater.inflate(R.layout.fxaccount_preference_list_fragment, paramViewGroup, michael@0: false); michael@0: } michael@0: michael@0: @Override michael@0: public void onActivityCreated(Bundle savedInstanceState) { michael@0: super.onActivityCreated(savedInstanceState); michael@0: michael@0: if (mHavePrefs) { michael@0: bindPreferences(); michael@0: } michael@0: michael@0: mInitDone = true; michael@0: michael@0: if (savedInstanceState != null) { michael@0: Bundle container = savedInstanceState.getBundle(PREFERENCES_TAG); michael@0: if (container != null) { michael@0: final PreferenceScreen preferenceScreen = getPreferenceScreen(); michael@0: if (preferenceScreen != null) { michael@0: preferenceScreen.restoreHierarchyState(container); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: @Override michael@0: public void onStart() { michael@0: super.onStart(); michael@0: PreferenceManagerCompat.setOnPreferenceTreeClickListener(mPreferenceManager, this); michael@0: } michael@0: michael@0: @Override michael@0: public void onStop() { michael@0: super.onStop(); michael@0: PreferenceManagerCompat.dispatchActivityStop(mPreferenceManager); michael@0: PreferenceManagerCompat.setOnPreferenceTreeClickListener(mPreferenceManager, null); michael@0: } michael@0: michael@0: @Override michael@0: public void onDestroyView() { michael@0: mList = null; michael@0: mHandler.removeCallbacks(mRequestFocus); michael@0: mHandler.removeMessages(MSG_BIND_PREFERENCES); michael@0: super.onDestroyView(); michael@0: } michael@0: michael@0: @Override michael@0: public void onDestroy() { michael@0: super.onDestroy(); michael@0: PreferenceManagerCompat.dispatchActivityDestroy(mPreferenceManager); michael@0: } michael@0: michael@0: @Override michael@0: public void onSaveInstanceState(Bundle outState) { michael@0: super.onSaveInstanceState(outState); michael@0: michael@0: final PreferenceScreen preferenceScreen = getPreferenceScreen(); michael@0: if (preferenceScreen != null) { michael@0: Bundle container = new Bundle(); michael@0: preferenceScreen.saveHierarchyState(container); michael@0: outState.putBundle(PREFERENCES_TAG, container); michael@0: } michael@0: } michael@0: michael@0: @Override michael@0: public void onActivityResult(int requestCode, int resultCode, Intent data) { michael@0: super.onActivityResult(requestCode, resultCode, data); michael@0: michael@0: PreferenceManagerCompat.dispatchActivityResult(mPreferenceManager, requestCode, resultCode, data); michael@0: } michael@0: michael@0: /** michael@0: * Returns the {@link PreferenceManager} used by this fragment. michael@0: * @return The {@link PreferenceManager}. michael@0: */ michael@0: public PreferenceManager getPreferenceManager() { michael@0: return mPreferenceManager; michael@0: } michael@0: michael@0: /** michael@0: * Sets the root of the preference hierarchy that this fragment is showing. michael@0: * michael@0: * @param preferenceScreen The root {@link PreferenceScreen} of the preference hierarchy. michael@0: */ michael@0: public void setPreferenceScreen(PreferenceScreen preferenceScreen) { michael@0: if (PreferenceManagerCompat.setPreferences(mPreferenceManager, preferenceScreen) && preferenceScreen != null) { michael@0: mHavePrefs = true; michael@0: if (mInitDone) { michael@0: postBindPreferences(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Gets the root of the preference hierarchy that this fragment is showing. michael@0: * michael@0: * @return The {@link PreferenceScreen} that is the root of the preference michael@0: * hierarchy. michael@0: */ michael@0: public PreferenceScreen getPreferenceScreen() { michael@0: return PreferenceManagerCompat.getPreferenceScreen(mPreferenceManager); michael@0: } michael@0: michael@0: /** michael@0: * Adds preferences from activities that match the given {@link Intent}. michael@0: * michael@0: * @param intent The {@link Intent} to query activities. michael@0: */ michael@0: public void addPreferencesFromIntent(Intent intent) { michael@0: requirePreferenceManager(); michael@0: michael@0: setPreferenceScreen(PreferenceManagerCompat.inflateFromIntent(mPreferenceManager, intent, getPreferenceScreen())); michael@0: } michael@0: michael@0: /** michael@0: * Inflates the given XML resource and adds the preference hierarchy to the current michael@0: * preference hierarchy. michael@0: * michael@0: * @param preferencesResId The XML resource ID to inflate. michael@0: */ michael@0: public void addPreferencesFromResource(int preferencesResId) { michael@0: requirePreferenceManager(); michael@0: michael@0: setPreferenceScreen(PreferenceManagerCompat.inflateFromResource(mPreferenceManager, getActivity(), michael@0: preferencesResId, getPreferenceScreen())); michael@0: } michael@0: michael@0: /** michael@0: * {@inheritDoc} michael@0: */ michael@0: @Override michael@0: public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, michael@0: Preference preference) { michael@0: //if (preference.getFragment() != null && michael@0: if ( michael@0: getActivity() instanceof OnPreferenceStartFragmentCallback) { michael@0: return ((OnPreferenceStartFragmentCallback)getActivity()).onPreferenceStartFragment( michael@0: this, preference); michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: /** michael@0: * Finds a {@link Preference} based on its key. michael@0: * michael@0: * @param key The key of the preference to retrieve. michael@0: * @return The {@link Preference} with the key, or null. michael@0: * @see PreferenceGroup#findPreference(CharSequence) michael@0: */ michael@0: public Preference findPreference(CharSequence key) { michael@0: if (mPreferenceManager == null) { michael@0: return null; michael@0: } michael@0: return mPreferenceManager.findPreference(key); michael@0: } michael@0: michael@0: private void requirePreferenceManager() { michael@0: if (mPreferenceManager == null) { michael@0: throw new RuntimeException("This should be called after super.onCreate."); michael@0: } michael@0: } michael@0: michael@0: private void postBindPreferences() { michael@0: if (mHandler.hasMessages(MSG_BIND_PREFERENCES)) return; michael@0: mHandler.obtainMessage(MSG_BIND_PREFERENCES).sendToTarget(); michael@0: } michael@0: michael@0: private void bindPreferences() { michael@0: final PreferenceScreen preferenceScreen = getPreferenceScreen(); michael@0: if (preferenceScreen != null) { michael@0: preferenceScreen.bind(getListView()); michael@0: } michael@0: } michael@0: michael@0: public ListView getListView() { michael@0: ensureList(); michael@0: return mList; michael@0: } michael@0: michael@0: private void ensureList() { michael@0: if (mList != null) { michael@0: return; michael@0: } michael@0: View root = getView(); michael@0: if (root == null) { michael@0: throw new IllegalStateException("Content view not yet created"); michael@0: } michael@0: View rawListView = root.findViewById(android.R.id.list); michael@0: if (!(rawListView instanceof ListView)) { michael@0: throw new RuntimeException( michael@0: "Content has view with id attribute 'android.R.id.list' " michael@0: + "that is not a ListView class"); michael@0: } michael@0: mList = (ListView)rawListView; michael@0: if (mList == null) { michael@0: throw new RuntimeException( michael@0: "Your content must have a ListView whose id attribute is " + michael@0: "'android.R.id.list'"); michael@0: } michael@0: mList.setOnKeyListener(mListOnKeyListener); michael@0: mHandler.post(mRequestFocus); michael@0: } michael@0: michael@0: private OnKeyListener mListOnKeyListener = new OnKeyListener() { michael@0: michael@0: @Override michael@0: public boolean onKey(View v, int keyCode, KeyEvent event) { michael@0: Object selectedItem = mList.getSelectedItem(); michael@0: if (selectedItem instanceof Preference) { michael@0: @SuppressWarnings("unused") michael@0: View selectedView = mList.getSelectedView(); michael@0: //return ((Preference)selectedItem).onKey( michael@0: // selectedView, keyCode, event); michael@0: return false; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: }; michael@0: }