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
onCreate(Bundle icicle)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
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)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
onCreatePreferences(Bundle savedInstanceState, String rootKey)139     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
140     }
141 
142     @Override
addPreferencesFromResource(@mlRes int preferencesResId)143     public void addPreferencesFromResource(@XmlRes int preferencesResId) {
144         super.addPreferencesFromResource(preferencesResId);
145         checkAvailablePrefs(getPreferenceScreen());
146     }
147 
checkAvailablePrefs(PreferenceGroup preferenceGroup)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 
getFloatingActionButton()161     public FloatingActionButton getFloatingActionButton() {
162         return mFloatingActionButton;
163     }
164 
getButtonBar()165     public ViewGroup getButtonBar() {
166         return mButtonBar;
167     }
168 
setPinnedHeaderView(int layoutResId)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 
setPinnedHeaderView(View pinnedHeader)177     public void setPinnedHeaderView(View pinnedHeader) {
178         mPinnedHeaderFrameLayout.addView(pinnedHeader);
179         mPinnedHeaderFrameLayout.setVisibility(View.VISIBLE);
180     }
181 
182     @Override
onSaveInstanceState(Bundle outState)183     public void onSaveInstanceState(Bundle outState) {
184         super.onSaveInstanceState(outState);
185 
186         outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mPreferenceHighlighted);
187     }
188 
189     @Override
onActivityCreated(Bundle savedInstanceState)190     public void onActivityCreated(Bundle savedInstanceState) {
191         super.onActivityCreated(savedInstanceState);
192         setHasOptionsMenu(true);
193     }
194 
195     @Override
onResume()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
onBindPreferences()207     protected void onBindPreferences() {
208         registerObserverIfNeeded();
209     }
210 
211     @Override
onUnbindPreferences()212     protected void onUnbindPreferences() {
213         unregisterObserverIfNeeded();
214     }
215 
showLoadingWhenEmpty()216     public void showLoadingWhenEmpty() {
217         View loading = getView().findViewById(R.id.loading_container);
218         setEmptyView(loading);
219     }
220 
setLoading(boolean loading, boolean animate)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 
registerObserverIfNeeded()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 
unregisterObserverIfNeeded()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 
highlightPreferenceIfNeeded()248     public void highlightPreferenceIfNeeded() {
249         if (isAdded() && !mPreferenceHighlighted &&!TextUtils.isEmpty(mPreferenceKey)) {
250             highlightPreference(mPreferenceKey);
251         }
252     }
253 
onDataSetChanged()254     protected void onDataSetChanged() {
255         highlightPreferenceIfNeeded();
256         updateEmptyView();
257     }
258 
getHeaderView()259     public LayoutPreference getHeaderView() {
260         return mHeader;
261     }
262 
getFooterView()263     public LayoutPreference getFooterView() {
264         return mFooter;
265     }
266 
setHeaderView(int resource)267     protected void setHeaderView(int resource) {
268         mHeader = new LayoutPreference(getPrefContext(), resource);
269         addPreferenceToTop(mHeader);
270     }
271 
setHeaderView(View view)272     protected void setHeaderView(View view) {
273         mHeader = new LayoutPreference(getPrefContext(), view);
274         addPreferenceToTop(mHeader);
275     }
276 
addPreferenceToTop(LayoutPreference preference)277     private void addPreferenceToTop(LayoutPreference preference) {
278         preference.setOrder(ORDER_FIRST);
279         if (getPreferenceScreen() != null) {
280             getPreferenceScreen().addPreference(preference);
281         }
282     }
283 
setFooterView(int resource)284     protected void setFooterView(int resource) {
285         setFooterView(resource != 0 ? new LayoutPreference(getPrefContext(), resource) : null);
286     }
287 
setFooterView(View v)288     protected void setFooterView(View v) {
289         setFooterView(v != null ? new LayoutPreference(getPrefContext(), v) : null);
290     }
291 
setFooterView(LayoutPreference footer)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
setPreferenceScreen(PreferenceScreen preferenceScreen)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 
updateEmptyView()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 
setEmptyView(View v)336     public void setEmptyView(View v) {
337         if (mEmptyView != null) {
338             mEmptyView.setVisibility(View.GONE);
339         }
340         mEmptyView = v;
341         updateEmptyView();
342     }
343 
getEmptyView()344     public View getEmptyView() {
345         return mEmptyView;
346     }
347 
348     /**
349      * Return a valid ListView position or -1 if none is found
350      */
canUseListViewForHighLighting(String key)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
onCreateLayoutManager()367     public RecyclerView.LayoutManager onCreateLayoutManager() {
368         mLayoutManager = new LinearLayoutManager(getContext());
369         return mLayoutManager;
370     }
371 
372     @Override
onCreateAdapter(PreferenceScreen preferenceScreen)373     protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) {
374         mAdapter = new HighlightablePreferenceGroupAdapter(preferenceScreen);
375         return mAdapter;
376     }
377 
setAnimationAllowed(boolean animationAllowed)378     protected void setAnimationAllowed(boolean animationAllowed) {
379         mAnimationAllowed = animationAllowed;
380     }
381 
cacheRemoveAllPrefs(PreferenceGroup group)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 
getCachedPreference(String key)394     protected Preference getCachedPreference(String key) {
395         return mPreferenceCache != null ? mPreferenceCache.remove(key) : null;
396     }
397 
removeCachedPrefs(PreferenceGroup group)398     protected void removeCachedPrefs(PreferenceGroup group) {
399         for (Preference p : mPreferenceCache.values()) {
400             group.removePreference(p);
401         }
402     }
403 
getCachedCount()404     protected int getCachedCount() {
405         return mPreferenceCache.size();
406     }
407 
highlightPreference(String key)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 
findListPositionFromKey(PreferenceGroupAdapter adapter, String key)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 
removePreference(String key)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      */
getHelpResource()446     protected int getHelpResource() {
447         return R.string.help_uri_default;
448     }
449 
450     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)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      */
finishFragment()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      */
getContentResolver()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      */
getSystemService(final String name)481     protected Object getSystemService(final String name) {
482         return getActivity().getSystemService(name);
483     }
484 
485     /**
486      * Returns the PackageManager from the owning Activity.
487      */
getPackageManager()488     protected PackageManager getPackageManager() {
489         return getActivity().getPackageManager();
490     }
491 
492     @Override
onDetach()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 
showDialog(int dialogId)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 
onCreateDialog(int dialogId)513     public Dialog onCreateDialog(int dialogId) {
514         return null;
515     }
516 
removeDialog(int dialogId)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      */
setOnCancelListener(DialogInterface.OnCancelListener listener)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      */
setOnDismissListener(DialogInterface.OnDismissListener listener)543     protected void setOnDismissListener(DialogInterface.OnDismissListener listener) {
544         if (mDialogFragment != null) {
545             mDialogFragment.mOnDismissListener = listener;
546         }
547     }
548 
onDialogShowing()549     public void onDialogShowing() {
550         // override in subclass to attach a dismiss listener, for instance
551     }
552 
553     @Override
onDisplayPreferenceDialog(Preference preference)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 
SettingsDialogFragment()592         public SettingsDialogFragment() {
593             /* do nothing */
594         }
595 
SettingsDialogFragment(DialogCreatable fragment, int dialogId)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
onSaveInstanceState(Bundle outState)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
onStart()615         public void onStart() {
616             super.onStart();
617 
618             if (mParentFragment != null && mParentFragment instanceof SettingsPreferenceFragment) {
619                 ((SettingsPreferenceFragment) mParentFragment).onDialogShowing();
620             }
621         }
622 
623         @Override
onCreateDialog(Bundle savedInstanceState)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
onCancel(DialogInterface dialog)650         public void onCancel(DialogInterface dialog) {
651             super.onCancel(dialog);
652             if (mOnCancelListener != null) {
653                 mOnCancelListener.onCancel(dialog);
654             }
655         }
656 
657         @Override
onDismiss(DialogInterface dialog)658         public void onDismiss(DialogInterface dialog) {
659             super.onDismiss(dialog);
660             if (mOnDismissListener != null) {
661                 mOnDismissListener.onDismiss(dialog);
662             }
663         }
664 
getDialogId()665         public int getDialogId() {
666             return mDialogId;
667         }
668 
669         @Override
onDetach()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 
hasNextButton()683     protected boolean hasNextButton() {
684         return ((ButtonBarHandler)getActivity()).hasNextButton();
685     }
686 
getNextButton()687     protected Button getNextButton() {
688         return ((ButtonBarHandler)getActivity()).getNextButton();
689     }
690 
finish()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 
getIntent()701     protected Intent getIntent() {
702         if (getActivity() == null) {
703             return null;
704         }
705         return getActivity().getIntent();
706     }
707 
setResult(int result, Intent intent)708     protected void setResult(int result, Intent intent) {
709         if (getActivity() == null) {
710             return;
711         }
712         getActivity().setResult(result, intent);
713     }
714 
setResult(int result)715     protected void setResult(int result) {
716         if (getActivity() == null) {
717             return;
718         }
719         getActivity().setResult(result);
720     }
721 
getPrefContext()722     protected final Context getPrefContext() {
723         return getPreferenceManager().getContext();
724     }
725 
startFragment(Fragment caller, String fragmentClass, int titleRes, int requestCode, Bundle extras)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 
HighlightablePreferenceGroupAdapter(PreferenceGroup preferenceGroup)746         public HighlightablePreferenceGroupAdapter(PreferenceGroup preferenceGroup) {
747             super(preferenceGroup);
748         }
749 
highlight(int position)750         public void highlight(int position) {
751             mHighlightPosition = position;
752             notifyDataSetChanged();
753         }
754 
755         @Override
onBindViewHolder(PreferenceViewHolder holder, int position)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