Home | History | Annotate | Download | only in settings
      1 /*
      2  * Copyright (C) 2010 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 com.android.settings;
     18 
     19 import android.app.Activity;
     20 import android.app.Dialog;
     21 import android.app.DialogFragment;
     22 import android.app.Fragment;
     23 import android.content.ContentResolver;
     24 import android.content.Context;
     25 import android.content.DialogInterface;
     26 import android.content.Intent;
     27 import android.content.pm.PackageManager;
     28 import android.os.Bundle;
     29 import android.support.annotation.XmlRes;
     30 import android.support.v7.preference.Preference;
     31 import android.support.v7.preference.PreferenceGroup;
     32 import android.support.v7.preference.PreferenceGroupAdapter;
     33 import android.support.v7.preference.PreferenceScreen;
     34 import android.support.v7.preference.PreferenceViewHolder;
     35 import android.support.v7.widget.LinearLayoutManager;
     36 import android.support.v7.widget.RecyclerView;
     37 import android.text.TextUtils;
     38 import android.util.ArrayMap;
     39 import android.util.Log;
     40 import android.view.LayoutInflater;
     41 import android.view.Menu;
     42 import android.view.MenuInflater;
     43 import android.view.View;
     44 import android.view.ViewGroup;
     45 import android.widget.Button;
     46 
     47 import com.android.settings.applications.LayoutPreference;
     48 import com.android.settings.widget.FloatingActionButton;
     49 import com.android.settingslib.HelpUtils;
     50 
     51 import java.util.UUID;
     52 
     53 /**
     54  * Base class for Settings fragments, with some helper functions and dialog management.
     55  */
     56 public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceFragment
     57         implements DialogCreatable {
     58 
     59     /**
     60      * The Help Uri Resource key. This can be passed as an extra argument when creating the
     61      * Fragment.
     62      **/
     63     public static final String HELP_URI_RESOURCE_KEY = "help_uri_resource";
     64 
     65     private static final String TAG = "SettingsPreference";
     66 
     67     private static final int DELAY_HIGHLIGHT_DURATION_MILLIS = 600;
     68 
     69     private static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted";
     70 
     71     private SettingsDialogFragment mDialogFragment;
     72 
     73     private String mHelpUri;
     74 
     75     private static final int ORDER_FIRST = -1;
     76     private static final int ORDER_LAST = Integer.MAX_VALUE -1;
     77 
     78     // Cache the content resolver for async callbacks
     79     private ContentResolver mContentResolver;
     80 
     81     private String mPreferenceKey;
     82     private boolean mPreferenceHighlighted = false;
     83 
     84     private RecyclerView.Adapter mCurrentRootAdapter;
     85     private boolean mIsDataSetObserverRegistered = false;
     86     private RecyclerView.AdapterDataObserver mDataSetObserver =
     87             new RecyclerView.AdapterDataObserver() {
     88         @Override
     89         public void onChanged() {
     90             onDataSetChanged();
     91         }
     92     };
     93 
     94     private ViewGroup mPinnedHeaderFrameLayout;
     95     private FloatingActionButton mFloatingActionButton;
     96     private ViewGroup mButtonBar;
     97 
     98     private LayoutPreference mHeader;
     99 
    100     private LayoutPreference mFooter;
    101     private View mEmptyView;
    102     private LinearLayoutManager mLayoutManager;
    103     private HighlightablePreferenceGroupAdapter mAdapter;
    104     private ArrayMap<String, Preference> mPreferenceCache;
    105     private boolean mAnimationAllowed;
    106 
    107     @Override
    108     public void onCreate(Bundle icicle) {
    109         super.onCreate(icicle);
    110 
    111         if (icicle != null) {
    112             mPreferenceHighlighted = icicle.getBoolean(SAVE_HIGHLIGHTED_KEY);
    113         }
    114 
    115         // Prepare help url and enable menu if necessary
    116         Bundle arguments = getArguments();
    117         int helpResource;
    118         if (arguments != null && arguments.containsKey(HELP_URI_RESOURCE_KEY)) {
    119             helpResource = arguments.getInt(HELP_URI_RESOURCE_KEY);
    120         } else {
    121             helpResource = getHelpResource();
    122         }
    123         if (helpResource != 0) {
    124             mHelpUri = getResources().getString(helpResource);
    125         }
    126     }
    127 
    128     @Override
    129     public View onCreateView(LayoutInflater inflater, ViewGroup container,
    130             Bundle savedInstanceState) {
    131         final View root = super.onCreateView(inflater, container, savedInstanceState);
    132         mPinnedHeaderFrameLayout = (ViewGroup) root.findViewById(R.id.pinned_header);
    133         mFloatingActionButton = (FloatingActionButton) root.findViewById(R.id.fab);
    134         mButtonBar = (ViewGroup) root.findViewById(R.id.button_bar);
    135         return root;
    136     }
    137 
    138     @Override
    139     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
    140     }
    141 
    142     @Override
    143     public void addPreferencesFromResource(@XmlRes int preferencesResId) {
    144         super.addPreferencesFromResource(preferencesResId);
    145         checkAvailablePrefs(getPreferenceScreen());
    146     }
    147 
    148     private void checkAvailablePrefs(PreferenceGroup preferenceGroup) {
    149         if (preferenceGroup == null) return;
    150         for (int i = 0; i < preferenceGroup.getPreferenceCount(); i++) {
    151             Preference pref = preferenceGroup.getPreference(i);
    152             if (pref instanceof SelfAvailablePreference
    153                     && !((SelfAvailablePreference) pref).isAvailable(getContext())) {
    154                 preferenceGroup.removePreference(pref);
    155             } else if (pref instanceof PreferenceGroup) {
    156                 checkAvailablePrefs((PreferenceGroup) pref);
    157             }
    158         }
    159     }
    160 
    161     public FloatingActionButton getFloatingActionButton() {
    162         return mFloatingActionButton;
    163     }
    164 
    165     public ViewGroup getButtonBar() {
    166         return mButtonBar;
    167     }
    168 
    169     public View setPinnedHeaderView(int layoutResId) {
    170         final LayoutInflater inflater = getActivity().getLayoutInflater();
    171         final View pinnedHeader =
    172                 inflater.inflate(layoutResId, mPinnedHeaderFrameLayout, false);
    173         setPinnedHeaderView(pinnedHeader);
    174         return pinnedHeader;
    175     }
    176 
    177     public void setPinnedHeaderView(View pinnedHeader) {
    178         mPinnedHeaderFrameLayout.addView(pinnedHeader);
    179         mPinnedHeaderFrameLayout.setVisibility(View.VISIBLE);
    180     }
    181 
    182     @Override
    183     public void onSaveInstanceState(Bundle outState) {
    184         super.onSaveInstanceState(outState);
    185 
    186         outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mPreferenceHighlighted);
    187     }
    188 
    189     @Override
    190     public void onActivityCreated(Bundle savedInstanceState) {
    191         super.onActivityCreated(savedInstanceState);
    192         setHasOptionsMenu(true);
    193     }
    194 
    195     @Override
    196     public void onResume() {
    197         super.onResume();
    198 
    199         final Bundle args = getArguments();
    200         if (args != null) {
    201             mPreferenceKey = args.getString(SettingsActivity.EXTRA_FRAGMENT_ARG_KEY);
    202             highlightPreferenceIfNeeded();
    203         }
    204     }
    205 
    206     @Override
    207     protected void onBindPreferences() {
    208         registerObserverIfNeeded();
    209     }
    210 
    211     @Override
    212     protected void onUnbindPreferences() {
    213         unregisterObserverIfNeeded();
    214     }
    215 
    216     public void showLoadingWhenEmpty() {
    217         View loading = getView().findViewById(R.id.loading_container);
    218         setEmptyView(loading);
    219     }
    220 
    221     public void setLoading(boolean loading, boolean animate) {
    222         View loading_container = getView().findViewById(R.id.loading_container);
    223         Utils.handleLoadingContainer(loading_container, getListView(), !loading, animate);
    224     }
    225 
    226     public void registerObserverIfNeeded() {
    227         if (!mIsDataSetObserverRegistered) {
    228             if (mCurrentRootAdapter != null) {
    229                 mCurrentRootAdapter.unregisterAdapterDataObserver(mDataSetObserver);
    230             }
    231             mCurrentRootAdapter = getListView().getAdapter();
    232             mCurrentRootAdapter.registerAdapterDataObserver(mDataSetObserver);
    233             mIsDataSetObserverRegistered = true;
    234             onDataSetChanged();
    235         }
    236     }
    237 
    238     public void unregisterObserverIfNeeded() {
    239         if (mIsDataSetObserverRegistered) {
    240             if (mCurrentRootAdapter != null) {
    241                 mCurrentRootAdapter.unregisterAdapterDataObserver(mDataSetObserver);
    242                 mCurrentRootAdapter = null;
    243             }
    244             mIsDataSetObserverRegistered = false;
    245         }
    246     }
    247 
    248     public void highlightPreferenceIfNeeded() {
    249         if (isAdded() && !mPreferenceHighlighted &&!TextUtils.isEmpty(mPreferenceKey)) {
    250             highlightPreference(mPreferenceKey);
    251         }
    252     }
    253 
    254     protected void onDataSetChanged() {
    255         highlightPreferenceIfNeeded();
    256         updateEmptyView();
    257     }
    258 
    259     public LayoutPreference getHeaderView() {
    260         return mHeader;
    261     }
    262 
    263     public LayoutPreference getFooterView() {
    264         return mFooter;
    265     }
    266 
    267     protected void setHeaderView(int resource) {
    268         mHeader = new LayoutPreference(getPrefContext(), resource);
    269         addPreferenceToTop(mHeader);
    270     }
    271 
    272     protected void setHeaderView(View view) {
    273         mHeader = new LayoutPreference(getPrefContext(), view);
    274         addPreferenceToTop(mHeader);
    275     }
    276 
    277     private void addPreferenceToTop(LayoutPreference preference) {
    278         preference.setOrder(ORDER_FIRST);
    279         if (getPreferenceScreen() != null) {
    280             getPreferenceScreen().addPreference(preference);
    281         }
    282     }
    283 
    284     protected void setFooterView(int resource) {
    285         setFooterView(resource != 0 ? new LayoutPreference(getPrefContext(), resource) : null);
    286     }
    287 
    288     protected void setFooterView(View v) {
    289         setFooterView(v != null ? new LayoutPreference(getPrefContext(), v) : null);
    290     }
    291 
    292     private void setFooterView(LayoutPreference footer) {
    293         if (getPreferenceScreen() != null && mFooter != null) {
    294             getPreferenceScreen().removePreference(mFooter);
    295         }
    296         if (footer != null) {
    297             mFooter = footer;
    298             mFooter.setOrder(ORDER_LAST);
    299             if (getPreferenceScreen() != null) {
    300                 getPreferenceScreen().addPreference(mFooter);
    301             }
    302         } else {
    303             mFooter = null;
    304         }
    305     }
    306 
    307     @Override
    308     public void setPreferenceScreen(PreferenceScreen preferenceScreen) {
    309         if (preferenceScreen != null && !preferenceScreen.isAttached()) {
    310             // Without ids generated, the RecyclerView won't animate changes to the preferences.
    311             preferenceScreen.setShouldUseGeneratedIds(mAnimationAllowed);
    312         }
    313         super.setPreferenceScreen(preferenceScreen);
    314         if (preferenceScreen != null) {
    315             if (mHeader != null) {
    316                 preferenceScreen.addPreference(mHeader);
    317             }
    318             if (mFooter != null) {
    319                 preferenceScreen.addPreference(mFooter);
    320             }
    321         }
    322     }
    323 
    324     private void updateEmptyView() {
    325         if (mEmptyView == null) return;
    326         if (getPreferenceScreen() != null) {
    327             boolean show = (getPreferenceScreen().getPreferenceCount()
    328                     - (mHeader != null ? 1 : 0)
    329                     - (mFooter != null ? 1 : 0)) <= 0;
    330             mEmptyView.setVisibility(show ? View.VISIBLE : View.GONE);
    331         } else {
    332             mEmptyView.setVisibility(View.VISIBLE);
    333         }
    334     }
    335 
    336     public void setEmptyView(View v) {
    337         if (mEmptyView != null) {
    338             mEmptyView.setVisibility(View.GONE);
    339         }
    340         mEmptyView = v;
    341         updateEmptyView();
    342     }
    343 
    344     public View getEmptyView() {
    345         return mEmptyView;
    346     }
    347 
    348     /**
    349      * Return a valid ListView position or -1 if none is found
    350      */
    351     private int canUseListViewForHighLighting(String key) {
    352         if (getListView() == null) {
    353             return -1;
    354         }
    355 
    356         RecyclerView listView = getListView();
    357         RecyclerView.Adapter adapter = listView.getAdapter();
    358 
    359         if (adapter != null && adapter instanceof PreferenceGroupAdapter) {
    360             return findListPositionFromKey((PreferenceGroupAdapter) adapter, key);
    361         }
    362 
    363         return -1;
    364     }
    365 
    366     @Override
    367     public RecyclerView.LayoutManager onCreateLayoutManager() {
    368         mLayoutManager = new LinearLayoutManager(getContext());
    369         return mLayoutManager;
    370     }
    371 
    372     @Override
    373     protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) {
    374         mAdapter = new HighlightablePreferenceGroupAdapter(preferenceScreen);
    375         return mAdapter;
    376     }
    377 
    378     protected void setAnimationAllowed(boolean animationAllowed) {
    379         mAnimationAllowed = animationAllowed;
    380     }
    381 
    382     protected void cacheRemoveAllPrefs(PreferenceGroup group) {
    383         mPreferenceCache = new ArrayMap<String, Preference>();
    384         final int N = group.getPreferenceCount();
    385         for (int i = 0; i < N; i++) {
    386             Preference p = group.getPreference(i);
    387             if (TextUtils.isEmpty(p.getKey())) {
    388                 continue;
    389             }
    390             mPreferenceCache.put(p.getKey(), p);
    391         }
    392     }
    393 
    394     protected Preference getCachedPreference(String key) {
    395         return mPreferenceCache != null ? mPreferenceCache.remove(key) : null;
    396     }
    397 
    398     protected void removeCachedPrefs(PreferenceGroup group) {
    399         for (Preference p : mPreferenceCache.values()) {
    400             group.removePreference(p);
    401         }
    402     }
    403 
    404     protected int getCachedCount() {
    405         return mPreferenceCache.size();
    406     }
    407 
    408     private void highlightPreference(String key) {
    409         final int position = canUseListViewForHighLighting(key);
    410         if (position >= 0) {
    411             mPreferenceHighlighted = true;
    412             mLayoutManager.scrollToPosition(position);
    413 
    414             getView().postDelayed(new Runnable() {
    415                 @Override
    416                 public void run() {
    417                     mAdapter.highlight(position);
    418                 }
    419             }, DELAY_HIGHLIGHT_DURATION_MILLIS);
    420         }
    421     }
    422 
    423     private int findListPositionFromKey(PreferenceGroupAdapter adapter, String key) {
    424         final int count = adapter.getItemCount();
    425         for (int n = 0; n < count; n++) {
    426             final Preference preference = adapter.getItem(n);
    427             final String preferenceKey = preference.getKey();
    428             if (preferenceKey != null && preferenceKey.equals(key)) {
    429                 return n;
    430             }
    431         }
    432         return -1;
    433     }
    434 
    435     protected void removePreference(String key) {
    436         Preference pref = findPreference(key);
    437         if (pref != null) {
    438             getPreferenceScreen().removePreference(pref);
    439         }
    440     }
    441 
    442     /**
    443      * Override this if you want to show a help item in the menu, by returning the resource id.
    444      * @return the resource id for the help url
    445      */
    446     protected int getHelpResource() {
    447         return R.string.help_uri_default;
    448     }
    449 
    450     @Override
    451     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    452         if (mHelpUri != null && getActivity() != null) {
    453             HelpUtils.prepareHelpMenuItem(getActivity(), menu, mHelpUri, getClass().getName());
    454         }
    455     }
    456 
    457     /*
    458      * The name is intentionally made different from Activity#finish(), so that
    459      * users won't misunderstand its meaning.
    460      */
    461     public final void finishFragment() {
    462         getActivity().onBackPressed();
    463     }
    464 
    465     // Some helpers for functions used by the settings fragments when they were activities
    466 
    467     /**
    468      * Returns the ContentResolver from the owning Activity.
    469      */
    470     protected ContentResolver getContentResolver() {
    471         Context context = getActivity();
    472         if (context != null) {
    473             mContentResolver = context.getContentResolver();
    474         }
    475         return mContentResolver;
    476     }
    477 
    478     /**
    479      * Returns the specified system service from the owning Activity.
    480      */
    481     protected Object getSystemService(final String name) {
    482         return getActivity().getSystemService(name);
    483     }
    484 
    485     /**
    486      * Returns the PackageManager from the owning Activity.
    487      */
    488     protected PackageManager getPackageManager() {
    489         return getActivity().getPackageManager();
    490     }
    491 
    492     @Override
    493     public void onDetach() {
    494         if (isRemoving()) {
    495             if (mDialogFragment != null) {
    496                 mDialogFragment.dismiss();
    497                 mDialogFragment = null;
    498             }
    499         }
    500         super.onDetach();
    501     }
    502 
    503     // Dialog management
    504 
    505     protected void showDialog(int dialogId) {
    506         if (mDialogFragment != null) {
    507             Log.e(TAG, "Old dialog fragment not null!");
    508         }
    509         mDialogFragment = new SettingsDialogFragment(this, dialogId);
    510         mDialogFragment.show(getChildFragmentManager(), Integer.toString(dialogId));
    511     }
    512 
    513     public Dialog onCreateDialog(int dialogId) {
    514         return null;
    515     }
    516 
    517     protected void removeDialog(int dialogId) {
    518         // mDialogFragment may not be visible yet in parent fragment's onResume().
    519         // To be able to dismiss dialog at that time, don't check
    520         // mDialogFragment.isVisible().
    521         if (mDialogFragment != null && mDialogFragment.getDialogId() == dialogId) {
    522             mDialogFragment.dismiss();
    523         }
    524         mDialogFragment = null;
    525     }
    526 
    527     /**
    528      * Sets the OnCancelListener of the dialog shown. This method can only be
    529      * called after showDialog(int) and before removeDialog(int). The method
    530      * does nothing otherwise.
    531      */
    532     protected void setOnCancelListener(DialogInterface.OnCancelListener listener) {
    533         if (mDialogFragment != null) {
    534             mDialogFragment.mOnCancelListener = listener;
    535         }
    536     }
    537 
    538     /**
    539      * Sets the OnDismissListener of the dialog shown. This method can only be
    540      * called after showDialog(int) and before removeDialog(int). The method
    541      * does nothing otherwise.
    542      */
    543     protected void setOnDismissListener(DialogInterface.OnDismissListener listener) {
    544         if (mDialogFragment != null) {
    545             mDialogFragment.mOnDismissListener = listener;
    546         }
    547     }
    548 
    549     public void onDialogShowing() {
    550         // override in subclass to attach a dismiss listener, for instance
    551     }
    552 
    553     @Override
    554     public void onDisplayPreferenceDialog(Preference preference) {
    555         if (preference.getKey() == null) {
    556             // Auto-key preferences that don't have a key, so the dialog can find them.
    557             preference.setKey(UUID.randomUUID().toString());
    558         }
    559         DialogFragment f = null;
    560         if (preference instanceof RestrictedListPreference) {
    561             f = RestrictedListPreference.RestrictedListPreferenceDialogFragment
    562                     .newInstance(preference.getKey());
    563         } else if (preference instanceof CustomListPreference) {
    564             f = CustomListPreference.CustomListPreferenceDialogFragment
    565                     .newInstance(preference.getKey());
    566         } else if (preference instanceof CustomDialogPreference) {
    567             f = CustomDialogPreference.CustomPreferenceDialogFragment
    568                     .newInstance(preference.getKey());
    569         } else if (preference instanceof CustomEditTextPreference) {
    570             f = CustomEditTextPreference.CustomPreferenceDialogFragment
    571                     .newInstance(preference.getKey());
    572         } else {
    573             super.onDisplayPreferenceDialog(preference);
    574             return;
    575         }
    576         f.setTargetFragment(this, 0);
    577         f.show(getFragmentManager(), "dialog_preference");
    578         onDialogShowing();
    579     }
    580 
    581     public static class SettingsDialogFragment extends DialogFragment {
    582         private static final String KEY_DIALOG_ID = "key_dialog_id";
    583         private static final String KEY_PARENT_FRAGMENT_ID = "key_parent_fragment_id";
    584 
    585         private int mDialogId;
    586 
    587         private Fragment mParentFragment;
    588 
    589         private DialogInterface.OnCancelListener mOnCancelListener;
    590         private DialogInterface.OnDismissListener mOnDismissListener;
    591 
    592         public SettingsDialogFragment() {
    593             /* do nothing */
    594         }
    595 
    596         public SettingsDialogFragment(DialogCreatable fragment, int dialogId) {
    597             mDialogId = dialogId;
    598             if (!(fragment instanceof Fragment)) {
    599                 throw new IllegalArgumentException("fragment argument must be an instance of "
    600                         + Fragment.class.getName());
    601             }
    602             mParentFragment = (Fragment) fragment;
    603         }
    604 
    605         @Override
    606         public void onSaveInstanceState(Bundle outState) {
    607             super.onSaveInstanceState(outState);
    608             if (mParentFragment != null) {
    609                 outState.putInt(KEY_DIALOG_ID, mDialogId);
    610                 outState.putInt(KEY_PARENT_FRAGMENT_ID, mParentFragment.getId());
    611             }
    612         }
    613 
    614         @Override
    615         public void onStart() {
    616             super.onStart();
    617 
    618             if (mParentFragment != null && mParentFragment instanceof SettingsPreferenceFragment) {
    619                 ((SettingsPreferenceFragment) mParentFragment).onDialogShowing();
    620             }
    621         }
    622 
    623         @Override
    624         public Dialog onCreateDialog(Bundle savedInstanceState) {
    625             if (savedInstanceState != null) {
    626                 mDialogId = savedInstanceState.getInt(KEY_DIALOG_ID, 0);
    627                 mParentFragment = getParentFragment();
    628                 int mParentFragmentId = savedInstanceState.getInt(KEY_PARENT_FRAGMENT_ID, -1);
    629                 if (mParentFragment == null) {
    630                     mParentFragment = getFragmentManager().findFragmentById(mParentFragmentId);
    631                 }
    632                 if (!(mParentFragment instanceof DialogCreatable)) {
    633                     throw new IllegalArgumentException(
    634                             (mParentFragment != null
    635                                     ? mParentFragment.getClass().getName()
    636                                     : mParentFragmentId)
    637                                     + " must implement "
    638                                     + DialogCreatable.class.getName());
    639                 }
    640                 // This dialog fragment could be created from non-SettingsPreferenceFragment
    641                 if (mParentFragment instanceof SettingsPreferenceFragment) {
    642                     // restore mDialogFragment in mParentFragment
    643                     ((SettingsPreferenceFragment) mParentFragment).mDialogFragment = this;
    644                 }
    645             }
    646             return ((DialogCreatable) mParentFragment).onCreateDialog(mDialogId);
    647         }
    648 
    649         @Override
    650         public void onCancel(DialogInterface dialog) {
    651             super.onCancel(dialog);
    652             if (mOnCancelListener != null) {
    653                 mOnCancelListener.onCancel(dialog);
    654             }
    655         }
    656 
    657         @Override
    658         public void onDismiss(DialogInterface dialog) {
    659             super.onDismiss(dialog);
    660             if (mOnDismissListener != null) {
    661                 mOnDismissListener.onDismiss(dialog);
    662             }
    663         }
    664 
    665         public int getDialogId() {
    666             return mDialogId;
    667         }
    668 
    669         @Override
    670         public void onDetach() {
    671             super.onDetach();
    672 
    673             // This dialog fragment could be created from non-SettingsPreferenceFragment
    674             if (mParentFragment instanceof SettingsPreferenceFragment) {
    675                 // in case the dialog is not explicitly removed by removeDialog()
    676                 if (((SettingsPreferenceFragment) mParentFragment).mDialogFragment == this) {
    677                     ((SettingsPreferenceFragment) mParentFragment).mDialogFragment = null;
    678                 }
    679             }
    680         }
    681     }
    682 
    683     protected boolean hasNextButton() {
    684         return ((ButtonBarHandler)getActivity()).hasNextButton();
    685     }
    686 
    687     protected Button getNextButton() {
    688         return ((ButtonBarHandler)getActivity()).getNextButton();
    689     }
    690 
    691     public void finish() {
    692         Activity activity = getActivity();
    693         if (activity == null) return;
    694         if (getFragmentManager().getBackStackEntryCount() > 0) {
    695             getFragmentManager().popBackStack();
    696         } else {
    697             activity.finish();
    698         }
    699     }
    700 
    701     protected Intent getIntent() {
    702         if (getActivity() == null) {
    703             return null;
    704         }
    705         return getActivity().getIntent();
    706     }
    707 
    708     protected void setResult(int result, Intent intent) {
    709         if (getActivity() == null) {
    710             return;
    711         }
    712         getActivity().setResult(result, intent);
    713     }
    714 
    715     protected void setResult(int result) {
    716         if (getActivity() == null) {
    717             return;
    718         }
    719         getActivity().setResult(result);
    720     }
    721 
    722     protected final Context getPrefContext() {
    723         return getPreferenceManager().getContext();
    724     }
    725 
    726     public boolean startFragment(Fragment caller, String fragmentClass, int titleRes,
    727             int requestCode, Bundle extras) {
    728         final Activity activity = getActivity();
    729         if (activity instanceof SettingsActivity) {
    730             SettingsActivity sa = (SettingsActivity) activity;
    731             sa.startPreferencePanel(fragmentClass, extras, titleRes, null, caller, requestCode);
    732             return true;
    733         } else {
    734             Log.w(TAG,
    735                     "Parent isn't SettingsActivity nor PreferenceActivity, thus there's no way to "
    736                     + "launch the given Fragment (name: " + fragmentClass
    737                     + ", requestCode: " + requestCode + ")");
    738             return false;
    739         }
    740     }
    741 
    742     public static class HighlightablePreferenceGroupAdapter extends PreferenceGroupAdapter {
    743 
    744         private int mHighlightPosition = -1;
    745 
    746         public HighlightablePreferenceGroupAdapter(PreferenceGroup preferenceGroup) {
    747             super(preferenceGroup);
    748         }
    749 
    750         public void highlight(int position) {
    751             mHighlightPosition = position;
    752             notifyDataSetChanged();
    753         }
    754 
    755         @Override
    756         public void onBindViewHolder(PreferenceViewHolder holder, int position) {
    757             super.onBindViewHolder(holder, position);
    758             if (position == mHighlightPosition) {
    759                 View v = holder.itemView;
    760                 if (v.getBackground() != null) {
    761                     final int centerX = v.getWidth() / 2;
    762                     final int centerY = v.getHeight() / 2;
    763                     v.getBackground().setHotspot(centerX, centerY);
    764                 }
    765                 v.setPressed(true);
    766                 v.setPressed(false);
    767                 mHighlightPosition = -1;
    768             }
    769         }
    770     }
    771 }
    772