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         mPreferenceCache = null;
    403     }
    404 
    405     protected int getCachedCount() {
    406         return mPreferenceCache != null ? mPreferenceCache.size() : 0;
    407     }
    408 
    409     private void highlightPreference(String key) {
    410         final int position = canUseListViewForHighLighting(key);
    411         if (position >= 0) {
    412             mPreferenceHighlighted = true;
    413             mLayoutManager.scrollToPosition(position);
    414 
    415             getView().postDelayed(new Runnable() {
    416                 @Override
    417                 public void run() {
    418                     mAdapter.highlight(position);
    419                 }
    420             }, DELAY_HIGHLIGHT_DURATION_MILLIS);
    421         }
    422     }
    423 
    424     private int findListPositionFromKey(PreferenceGroupAdapter adapter, String key) {
    425         final int count = adapter.getItemCount();
    426         for (int n = 0; n < count; n++) {
    427             final Preference preference = adapter.getItem(n);
    428             final String preferenceKey = preference.getKey();
    429             if (preferenceKey != null && preferenceKey.equals(key)) {
    430                 return n;
    431             }
    432         }
    433         return -1;
    434     }
    435 
    436     protected void removePreference(String key) {
    437         Preference pref = findPreference(key);
    438         if (pref != null) {
    439             getPreferenceScreen().removePreference(pref);
    440         }
    441     }
    442 
    443     /**
    444      * Override this if you want to show a help item in the menu, by returning the resource id.
    445      * @return the resource id for the help url
    446      */
    447     protected int getHelpResource() {
    448         return R.string.help_uri_default;
    449     }
    450 
    451     @Override
    452     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    453         if (mHelpUri != null && getActivity() != null) {
    454             HelpUtils.prepareHelpMenuItem(getActivity(), menu, mHelpUri, getClass().getName());
    455         }
    456     }
    457 
    458     /*
    459      * The name is intentionally made different from Activity#finish(), so that
    460      * users won't misunderstand its meaning.
    461      */
    462     public final void finishFragment() {
    463         getActivity().onBackPressed();
    464     }
    465 
    466     // Some helpers for functions used by the settings fragments when they were activities
    467 
    468     /**
    469      * Returns the ContentResolver from the owning Activity.
    470      */
    471     protected ContentResolver getContentResolver() {
    472         Context context = getActivity();
    473         if (context != null) {
    474             mContentResolver = context.getContentResolver();
    475         }
    476         return mContentResolver;
    477     }
    478 
    479     /**
    480      * Returns the specified system service from the owning Activity.
    481      */
    482     protected Object getSystemService(final String name) {
    483         return getActivity().getSystemService(name);
    484     }
    485 
    486     /**
    487      * Returns the PackageManager from the owning Activity.
    488      */
    489     protected PackageManager getPackageManager() {
    490         return getActivity().getPackageManager();
    491     }
    492 
    493     @Override
    494     public void onDetach() {
    495         if (isRemoving()) {
    496             if (mDialogFragment != null) {
    497                 mDialogFragment.dismiss();
    498                 mDialogFragment = null;
    499             }
    500         }
    501         super.onDetach();
    502     }
    503 
    504     // Dialog management
    505 
    506     protected void showDialog(int dialogId) {
    507         if (mDialogFragment != null) {
    508             Log.e(TAG, "Old dialog fragment not null!");
    509         }
    510         mDialogFragment = new SettingsDialogFragment(this, dialogId);
    511         mDialogFragment.show(getChildFragmentManager(), Integer.toString(dialogId));
    512     }
    513 
    514     public Dialog onCreateDialog(int dialogId) {
    515         return null;
    516     }
    517 
    518     protected void removeDialog(int dialogId) {
    519         // mDialogFragment may not be visible yet in parent fragment's onResume().
    520         // To be able to dismiss dialog at that time, don't check
    521         // mDialogFragment.isVisible().
    522         if (mDialogFragment != null && mDialogFragment.getDialogId() == dialogId) {
    523             mDialogFragment.dismissAllowingStateLoss();
    524         }
    525         mDialogFragment = null;
    526     }
    527 
    528     /**
    529      * Sets the OnCancelListener of the dialog shown. This method can only be
    530      * called after showDialog(int) and before removeDialog(int). The method
    531      * does nothing otherwise.
    532      */
    533     protected void setOnCancelListener(DialogInterface.OnCancelListener listener) {
    534         if (mDialogFragment != null) {
    535             mDialogFragment.mOnCancelListener = listener;
    536         }
    537     }
    538 
    539     /**
    540      * Sets the OnDismissListener of the dialog shown. This method can only be
    541      * called after showDialog(int) and before removeDialog(int). The method
    542      * does nothing otherwise.
    543      */
    544     protected void setOnDismissListener(DialogInterface.OnDismissListener listener) {
    545         if (mDialogFragment != null) {
    546             mDialogFragment.mOnDismissListener = listener;
    547         }
    548     }
    549 
    550     public void onDialogShowing() {
    551         // override in subclass to attach a dismiss listener, for instance
    552     }
    553 
    554     @Override
    555     public void onDisplayPreferenceDialog(Preference preference) {
    556         if (preference.getKey() == null) {
    557             // Auto-key preferences that don't have a key, so the dialog can find them.
    558             preference.setKey(UUID.randomUUID().toString());
    559         }
    560         DialogFragment f = null;
    561         if (preference instanceof RestrictedListPreference) {
    562             f = RestrictedListPreference.RestrictedListPreferenceDialogFragment
    563                     .newInstance(preference.getKey());
    564         } else if (preference instanceof CustomListPreference) {
    565             f = CustomListPreference.CustomListPreferenceDialogFragment
    566                     .newInstance(preference.getKey());
    567         } else if (preference instanceof CustomDialogPreference) {
    568             f = CustomDialogPreference.CustomPreferenceDialogFragment
    569                     .newInstance(preference.getKey());
    570         } else if (preference instanceof CustomEditTextPreference) {
    571             f = CustomEditTextPreference.CustomPreferenceDialogFragment
    572                     .newInstance(preference.getKey());
    573         } else {
    574             super.onDisplayPreferenceDialog(preference);
    575             return;
    576         }
    577         f.setTargetFragment(this, 0);
    578         f.show(getFragmentManager(), "dialog_preference");
    579         onDialogShowing();
    580     }
    581 
    582     public static class SettingsDialogFragment extends DialogFragment {
    583         private static final String KEY_DIALOG_ID = "key_dialog_id";
    584         private static final String KEY_PARENT_FRAGMENT_ID = "key_parent_fragment_id";
    585 
    586         private int mDialogId;
    587 
    588         private Fragment mParentFragment;
    589 
    590         private DialogInterface.OnCancelListener mOnCancelListener;
    591         private DialogInterface.OnDismissListener mOnDismissListener;
    592 
    593         public SettingsDialogFragment() {
    594             /* do nothing */
    595         }
    596 
    597         public SettingsDialogFragment(DialogCreatable fragment, int dialogId) {
    598             mDialogId = dialogId;
    599             if (!(fragment instanceof Fragment)) {
    600                 throw new IllegalArgumentException("fragment argument must be an instance of "
    601                         + Fragment.class.getName());
    602             }
    603             mParentFragment = (Fragment) fragment;
    604         }
    605 
    606         @Override
    607         public void onSaveInstanceState(Bundle outState) {
    608             super.onSaveInstanceState(outState);
    609             if (mParentFragment != null) {
    610                 outState.putInt(KEY_DIALOG_ID, mDialogId);
    611                 outState.putInt(KEY_PARENT_FRAGMENT_ID, mParentFragment.getId());
    612             }
    613         }
    614 
    615         @Override
    616         public void onStart() {
    617             super.onStart();
    618 
    619             if (mParentFragment != null && mParentFragment instanceof SettingsPreferenceFragment) {
    620                 ((SettingsPreferenceFragment) mParentFragment).onDialogShowing();
    621             }
    622         }
    623 
    624         @Override
    625         public Dialog onCreateDialog(Bundle savedInstanceState) {
    626             if (savedInstanceState != null) {
    627                 mDialogId = savedInstanceState.getInt(KEY_DIALOG_ID, 0);
    628                 mParentFragment = getParentFragment();
    629                 int mParentFragmentId = savedInstanceState.getInt(KEY_PARENT_FRAGMENT_ID, -1);
    630                 if (mParentFragment == null) {
    631                     mParentFragment = getFragmentManager().findFragmentById(mParentFragmentId);
    632                 }
    633                 if (!(mParentFragment instanceof DialogCreatable)) {
    634                     throw new IllegalArgumentException(
    635                             (mParentFragment != null
    636                                     ? mParentFragment.getClass().getName()
    637                                     : mParentFragmentId)
    638                                     + " must implement "
    639                                     + DialogCreatable.class.getName());
    640                 }
    641                 // This dialog fragment could be created from non-SettingsPreferenceFragment
    642                 if (mParentFragment instanceof SettingsPreferenceFragment) {
    643                     // restore mDialogFragment in mParentFragment
    644                     ((SettingsPreferenceFragment) mParentFragment).mDialogFragment = this;
    645                 }
    646             }
    647             return ((DialogCreatable) mParentFragment).onCreateDialog(mDialogId);
    648         }
    649 
    650         @Override
    651         public void onCancel(DialogInterface dialog) {
    652             super.onCancel(dialog);
    653             if (mOnCancelListener != null) {
    654                 mOnCancelListener.onCancel(dialog);
    655             }
    656         }
    657 
    658         @Override
    659         public void onDismiss(DialogInterface dialog) {
    660             super.onDismiss(dialog);
    661             if (mOnDismissListener != null) {
    662                 mOnDismissListener.onDismiss(dialog);
    663             }
    664         }
    665 
    666         public int getDialogId() {
    667             return mDialogId;
    668         }
    669 
    670         @Override
    671         public void onDetach() {
    672             super.onDetach();
    673 
    674             // This dialog fragment could be created from non-SettingsPreferenceFragment
    675             if (mParentFragment instanceof SettingsPreferenceFragment) {
    676                 // in case the dialog is not explicitly removed by removeDialog()
    677                 if (((SettingsPreferenceFragment) mParentFragment).mDialogFragment == this) {
    678                     ((SettingsPreferenceFragment) mParentFragment).mDialogFragment = null;
    679                 }
    680             }
    681         }
    682     }
    683 
    684     protected boolean hasNextButton() {
    685         return ((ButtonBarHandler)getActivity()).hasNextButton();
    686     }
    687 
    688     protected Button getNextButton() {
    689         return ((ButtonBarHandler)getActivity()).getNextButton();
    690     }
    691 
    692     public void finish() {
    693         Activity activity = getActivity();
    694         if (activity == null) return;
    695         if (getFragmentManager().getBackStackEntryCount() > 0) {
    696             getFragmentManager().popBackStack();
    697         } else {
    698             activity.finish();
    699         }
    700     }
    701 
    702     protected Intent getIntent() {
    703         if (getActivity() == null) {
    704             return null;
    705         }
    706         return getActivity().getIntent();
    707     }
    708 
    709     protected void setResult(int result, Intent intent) {
    710         if (getActivity() == null) {
    711             return;
    712         }
    713         getActivity().setResult(result, intent);
    714     }
    715 
    716     protected void setResult(int result) {
    717         if (getActivity() == null) {
    718             return;
    719         }
    720         getActivity().setResult(result);
    721     }
    722 
    723     protected final Context getPrefContext() {
    724         return getPreferenceManager().getContext();
    725     }
    726 
    727     public boolean startFragment(Fragment caller, String fragmentClass, int titleRes,
    728             int requestCode, Bundle extras) {
    729         final Activity activity = getActivity();
    730         if (activity instanceof SettingsActivity) {
    731             SettingsActivity sa = (SettingsActivity) activity;
    732             sa.startPreferencePanel(fragmentClass, extras, titleRes, null, caller, requestCode);
    733             return true;
    734         } else {
    735             Log.w(TAG,
    736                     "Parent isn't SettingsActivity nor PreferenceActivity, thus there's no way to "
    737                     + "launch the given Fragment (name: " + fragmentClass
    738                     + ", requestCode: " + requestCode + ")");
    739             return false;
    740         }
    741     }
    742 
    743     public static class HighlightablePreferenceGroupAdapter extends PreferenceGroupAdapter {
    744 
    745         private int mHighlightPosition = -1;
    746 
    747         public HighlightablePreferenceGroupAdapter(PreferenceGroup preferenceGroup) {
    748             super(preferenceGroup);
    749         }
    750 
    751         public void highlight(int position) {
    752             mHighlightPosition = position;
    753             notifyDataSetChanged();
    754         }
    755 
    756         @Override
    757         public void onBindViewHolder(PreferenceViewHolder holder, int position) {
    758             super.onBindViewHolder(holder, position);
    759             if (position == mHighlightPosition) {
    760                 View v = holder.itemView;
    761                 if (v.getBackground() != null) {
    762                     final int centerX = v.getWidth() / 2;
    763                     final int centerY = v.getHeight() / 2;
    764                     v.getBackground().setHotspot(centerX, centerY);
    765                 }
    766                 v.setPressed(true);
    767                 v.setPressed(false);
    768                 mHighlightPosition = -1;
    769             }
    770         }
    771     }
    772 }
    773