OSDN Git Service

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