PreferenceFragment.java revision 0dac8d82e2a249d7c9c42ab259389e11cac15400
1/*
2 * Copyright (C) 2015 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
17package android.support.v14.preference;
18
19import android.app.DialogFragment;
20import android.app.Fragment;
21import android.content.Context;
22import android.content.res.TypedArray;
23import android.graphics.Canvas;
24import android.graphics.Rect;
25import android.graphics.drawable.Drawable;
26import android.os.Bundle;
27import android.os.Handler;
28import android.os.Message;
29import android.support.annotation.Nullable;
30import android.support.annotation.XmlRes;
31import android.support.v4.content.res.TypedArrayUtils;
32import android.support.v4.view.ViewCompat;
33import android.support.v7.preference.AndroidResources;
34import android.support.v7.preference.DialogPreference;
35import android.support.v7.preference.EditTextPreference;
36import android.support.v7.preference.ListPreference;
37import android.support.v7.preference.Preference;
38import android.support.v7.preference.PreferenceGroupAdapter;
39import android.support.v7.preference.PreferenceManager;
40import android.support.v7.preference.PreferenceScreen;
41import android.support.v7.preference.PreferenceViewHolder;
42import android.support.v7.widget.LinearLayoutManager;
43import android.support.v7.widget.RecyclerView;
44import android.util.TypedValue;
45import android.view.ContextThemeWrapper;
46import android.view.LayoutInflater;
47import android.view.View;
48import android.view.ViewGroup;
49
50/**
51 * Shows a hierarchy of {@link Preference} objects as
52 * lists. These preferences will
53 * automatically save to {@link android.content.SharedPreferences} as the user interacts with
54 * them. To retrieve an instance of {@link android.content.SharedPreferences} that the
55 * preference hierarchy in this fragment will use, call
56 * {@link PreferenceManager#getDefaultSharedPreferences(android.content.Context)}
57 * with a context in the same package as this fragment.
58 * <p>
59 * Furthermore, the preferences shown will follow the visual style of system
60 * preferences. It is easy to create a hierarchy of preferences (that can be
61 * shown on multiple screens) via XML. For these reasons, it is recommended to
62 * use this fragment (as a superclass) to deal with preferences in applications.
63 * <p>
64 * A {@link PreferenceScreen} object should be at the top of the preference
65 * hierarchy. Furthermore, subsequent {@link PreferenceScreen} in the hierarchy
66 * denote a screen break--that is the preferences contained within subsequent
67 * {@link PreferenceScreen} should be shown on another screen. The preference
68 * framework handles this by calling {@link #onNavigateToScreen(PreferenceScreen)}.
69 * <p>
70 * The preference hierarchy can be formed in multiple ways:
71 * <li> From an XML file specifying the hierarchy
72 * <li> From different {@link android.app.Activity Activities} that each specify its own
73 * preferences in an XML file via {@link android.app.Activity} meta-data
74 * <li> From an object hierarchy rooted with {@link PreferenceScreen}
75 * <p>
76 * To inflate from XML, use the {@link #addPreferencesFromResource(int)}. The
77 * root element should be a {@link PreferenceScreen}. Subsequent elements can point
78 * to actual {@link Preference} subclasses. As mentioned above, subsequent
79 * {@link PreferenceScreen} in the hierarchy will result in the screen break.
80 * <p>
81 * To specify an object hierarchy rooted with {@link PreferenceScreen}, use
82 * {@link #setPreferenceScreen(PreferenceScreen)}.
83 * <p>
84 * As a convenience, this fragment implements a click listener for any
85 * preference in the current hierarchy, see
86 * {@link #onPreferenceTreeClick(Preference)}.
87 *
88 * <div class="special reference">
89 * <h3>Developer Guides</h3>
90 * <p>For information about using {@code PreferenceFragment},
91 * read the <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>
92 * guide.</p>
93 * </div>
94 *
95 * <a name="SampleCode"></a>
96 * <h3>Sample Code</h3>
97 *
98 * <p>The following sample code shows a simple preference fragment that is
99 * populated from a resource.  The resource it loads is:</p>
100 *
101 * {@sample development/samples/ApiDemos/res/xml/preferences.xml preferences}
102 *
103 * <p>The fragment implementation itself simply populates the preferences
104 * when created.  Note that the preferences framework takes care of loading
105 * the current values out of the app preferences and writing them when changed:</p>
106 *
107 * {@sample development/samples/ApiDemos/src/com/example/android/apis/preference/FragmentPreferences.java
108 *      fragment}
109 *
110 * @see Preference
111 * @see PreferenceScreen
112 */
113public abstract class PreferenceFragment extends Fragment implements
114        PreferenceManager.OnPreferenceTreeClickListener,
115        PreferenceManager.OnDisplayPreferenceDialogListener,
116        PreferenceManager.OnNavigateToScreenListener,
117        DialogPreference.TargetFragment {
118
119    /**
120     * Fragment argument used to specify the tag of the desired root
121     * {@link android.support.v7.preference.PreferenceScreen} object.
122     */
123    public static final String ARG_PREFERENCE_ROOT =
124            "android.support.v7.preference.PreferenceFragmentCompat.PREFERENCE_ROOT";
125
126    private static final String PREFERENCES_TAG = "android:preferences";
127
128    private static final String DIALOG_FRAGMENT_TAG =
129            "android.support.v14.preference.PreferenceFragment.DIALOG";
130
131    private PreferenceManager mPreferenceManager;
132    private RecyclerView mList;
133    private boolean mHavePrefs;
134    private boolean mInitDone;
135
136    private Context mStyledContext;
137
138    private int mLayoutResId = android.support.v7.preference.R.layout.preference_list_fragment;
139
140    private final DividerDecoration mDividerDecoration = new DividerDecoration();
141
142    private static final int MSG_BIND_PREFERENCES = 1;
143    private Handler mHandler = new Handler() {
144        @Override
145        public void handleMessage(Message msg) {
146            switch (msg.what) {
147
148                case MSG_BIND_PREFERENCES:
149                    bindPreferences();
150                    break;
151            }
152        }
153    };
154
155    final private Runnable mRequestFocus = new Runnable() {
156        public void run() {
157            mList.focusableViewAvailable(mList);
158        }
159    };
160
161    /**
162     * Interface that PreferenceFragment's containing activity should
163     * implement to be able to process preference items that wish to
164     * switch to a specified fragment.
165     */
166    public interface OnPreferenceStartFragmentCallback {
167        /**
168         * Called when the user has clicked on a Preference that has
169         * a fragment class name associated with it.  The implementation
170         * should instantiate and switch to an instance of the given
171         * fragment.
172         * @param caller The fragment requesting navigation.
173         * @param pref The preference requesting the fragment.
174         * @return true if the fragment creation has been handled
175         */
176        boolean onPreferenceStartFragment(PreferenceFragment caller, Preference pref);
177    }
178
179    /**
180     * Interface that PreferenceFragment's containing activity should
181     * implement to be able to process preference items that wish to
182     * switch to a new screen of preferences.
183     */
184    public interface OnPreferenceStartScreenCallback {
185        /**
186         * Called when the user has clicked on a PreferenceScreen item in order to navigate to a new
187         * screen of preferences.
188         * @param caller The fragment requesting navigation.
189         * @param pref The preference screen to navigate to.
190         * @return true if the screen navigation has been handled
191         */
192        boolean onPreferenceStartScreen(PreferenceFragment caller, PreferenceScreen pref);
193    }
194
195    public interface OnPreferenceDisplayDialogCallback {
196
197        /**
198         *
199         * @param caller The fragment containing the preference requesting the dialog.
200         * @param pref The preference requesting the dialog.
201         * @return true if the dialog creation has been handled.
202         */
203        boolean onPreferenceDisplayDialog(PreferenceFragment caller, Preference pref);
204    }
205
206    @Override
207    public void onCreate(Bundle savedInstanceState) {
208        super.onCreate(savedInstanceState);
209        final TypedValue tv = new TypedValue();
210        getActivity().getTheme().resolveAttribute(
211                android.support.v7.preference.R.attr.preferenceTheme, tv, true);
212        final int theme = tv.resourceId;
213        if (theme <= 0) {
214            throw new IllegalStateException("Must specify preferenceTheme in theme");
215        }
216        mStyledContext = new ContextThemeWrapper(getActivity(), theme);
217
218        mPreferenceManager = new PreferenceManager(mStyledContext);
219        mPreferenceManager.setOnNavigateToScreenListener(this);
220        final Bundle args = getArguments();
221        final String rootKey;
222        if (args != null) {
223            rootKey = getArguments().getString(ARG_PREFERENCE_ROOT);
224        } else {
225            rootKey = null;
226        }
227        onCreatePreferences(savedInstanceState, rootKey);
228    }
229
230    /**
231     * Called during {@link #onCreate(Bundle)} to supply the preferences for this fragment.
232     * Subclasses are expected to call {@link #setPreferenceScreen(PreferenceScreen)} either
233     * directly or via helper methods such as {@link #addPreferencesFromResource(int)}.
234     *
235     * @param savedInstanceState If the fragment is being re-created from
236     *                           a previous saved state, this is the state.
237     * @param rootKey If non-null, this preference fragment should be rooted at the
238     *                {@link android.support.v7.preference.PreferenceScreen} with this key.
239     */
240    public abstract void onCreatePreferences(Bundle savedInstanceState, String rootKey);
241
242    @Override
243    public View onCreateView(LayoutInflater inflater, ViewGroup container,
244            Bundle savedInstanceState) {
245
246        TypedArray a = mStyledContext.obtainStyledAttributes(null,
247                R.styleable.PreferenceFragment,
248                TypedArrayUtils.getAttr(mStyledContext,
249                        android.support.v7.preference.R.attr.preferenceFragmentStyle,
250                        AndroidResources.ANDROID_R_PREFERENCE_FRAGMENT_STYLE),
251                0);
252
253        mLayoutResId = a.getResourceId(R.styleable.PreferenceFragment_android_layout, mLayoutResId);
254
255        final Drawable divider = a.getDrawable(R.styleable.PreferenceFragment_android_divider);
256        final int dividerHeight = a.getDimensionPixelSize(
257                R.styleable.PreferenceFragment_android_dividerHeight, -1);
258
259        a.recycle();
260
261        // Need to theme the inflater to pick up the preferenceFragmentListStyle
262        final TypedValue tv = new TypedValue();
263        getActivity().getTheme().resolveAttribute(
264                android.support.v7.preference.R.attr.preferenceTheme, tv, true);
265        final int theme = tv.resourceId;
266
267        final Context themedContext = new ContextThemeWrapper(inflater.getContext(), theme);
268        final LayoutInflater themedInflater = inflater.cloneInContext(themedContext);
269
270        final View view = themedInflater.inflate(mLayoutResId, container, false);
271
272        final View rawListContainer = view.findViewById(AndroidResources.ANDROID_R_LIST_CONTAINER);
273        if (!(rawListContainer instanceof ViewGroup)) {
274            throw new RuntimeException("Content has view with id attribute "
275                    + "'android.R.id.list_container' that is not a ViewGroup class");
276        }
277
278        final ViewGroup listContainer = (ViewGroup) rawListContainer;
279
280        final RecyclerView listView = onCreateRecyclerView(themedInflater, listContainer,
281                savedInstanceState);
282        if (listView == null) {
283            throw new RuntimeException("Could not create RecyclerView");
284        }
285
286        mList = listView;
287
288        listView.addItemDecoration(mDividerDecoration);
289        setDivider(divider);
290        if (dividerHeight != -1) {
291            setDividerHeight(dividerHeight);
292        }
293
294        listContainer.addView(mList);
295        mHandler.post(mRequestFocus);
296        return view;
297    }
298
299    /**
300     * Sets the drawable that will be drawn between each item in the list.
301     * <p>
302     * <strong>Note:</strong> If the drawable does not have an intrinsic
303     * height, you should also call {@link #setDividerHeight(int)}.
304     *
305     * @param divider the drawable to use
306     * @attr ref R.styleable#PreferenceFragment_android_divider
307     */
308    public void setDivider(Drawable divider) {
309        mDividerDecoration.setDivider(divider);
310    }
311
312    /**
313     * Sets the height of the divider that will be drawn between each item in the list. Calling
314     * this will override the intrinsic height as set by {@link #setDivider(Drawable)}
315     *
316     * @param height The new height of the divider in pixels.
317     * @attr ref R.styleable#PreferenceFragment_android_dividerHeight
318     */
319    public void setDividerHeight(int height) {
320        mDividerDecoration.setDividerHeight(height);
321    }
322
323    @Override
324    public void onViewCreated(View view, Bundle savedInstanceState) {
325        super.onViewCreated(view, savedInstanceState);
326
327        if (mHavePrefs) {
328            bindPreferences();
329        }
330
331        mInitDone = true;
332    }
333
334    @Override
335    public void onActivityCreated(Bundle savedInstanceState) {
336        super.onActivityCreated(savedInstanceState);
337
338        if (savedInstanceState != null) {
339            Bundle container = savedInstanceState.getBundle(PREFERENCES_TAG);
340            if (container != null) {
341                final PreferenceScreen preferenceScreen = getPreferenceScreen();
342                if (preferenceScreen != null) {
343                    preferenceScreen.restoreHierarchyState(container);
344                }
345            }
346        }
347    }
348
349    @Override
350    public void onStart() {
351        super.onStart();
352        mPreferenceManager.setOnPreferenceTreeClickListener(this);
353        mPreferenceManager.setOnDisplayPreferenceDialogListener(this);
354    }
355
356    @Override
357    public void onStop() {
358        super.onStop();
359        mPreferenceManager.setOnPreferenceTreeClickListener(null);
360        mPreferenceManager.setOnDisplayPreferenceDialogListener(null);
361    }
362
363    @Override
364    public void onDestroyView() {
365        mHandler.removeCallbacks(mRequestFocus);
366        mHandler.removeMessages(MSG_BIND_PREFERENCES);
367        if (mHavePrefs) {
368            unbindPreferences();
369        }
370        mList = null;
371        super.onDestroyView();
372    }
373
374    @Override
375    public void onSaveInstanceState(Bundle outState) {
376        super.onSaveInstanceState(outState);
377
378        final PreferenceScreen preferenceScreen = getPreferenceScreen();
379        if (preferenceScreen != null) {
380            Bundle container = new Bundle();
381            preferenceScreen.saveHierarchyState(container);
382            outState.putBundle(PREFERENCES_TAG, container);
383        }
384    }
385
386    /**
387     * Returns the {@link PreferenceManager} used by this fragment.
388     * @return The {@link PreferenceManager}.
389     */
390    public PreferenceManager getPreferenceManager() {
391        return mPreferenceManager;
392    }
393
394    /**
395     * Sets the root of the preference hierarchy that this fragment is showing.
396     *
397     * @param preferenceScreen The root {@link PreferenceScreen} of the preference hierarchy.
398     */
399    public void setPreferenceScreen(PreferenceScreen preferenceScreen) {
400        if (mPreferenceManager.setPreferences(preferenceScreen) && preferenceScreen != null) {
401            onUnbindPreferences();
402            mHavePrefs = true;
403            if (mInitDone) {
404                postBindPreferences();
405            }
406        }
407    }
408
409    /**
410     * Gets the root of the preference hierarchy that this fragment is showing.
411     *
412     * @return The {@link PreferenceScreen} that is the root of the preference
413     *         hierarchy.
414     */
415    public PreferenceScreen getPreferenceScreen() {
416        return mPreferenceManager.getPreferenceScreen();
417    }
418
419    /**
420     * Inflates the given XML resource and adds the preference hierarchy to the current
421     * preference hierarchy.
422     *
423     * @param preferencesResId The XML resource ID to inflate.
424     */
425    public void addPreferencesFromResource(@XmlRes int preferencesResId) {
426        requirePreferenceManager();
427
428        setPreferenceScreen(mPreferenceManager.inflateFromResource(mStyledContext,
429                preferencesResId, getPreferenceScreen()));
430    }
431
432    /**
433     * Inflates the given XML resource and replaces the current preference hierarchy (if any) with
434     * the preference hierarchy rooted at {@code key}.
435     *
436     * @param preferencesResId The XML resource ID to inflate.
437     * @param key The preference key of the {@link android.support.v7.preference.PreferenceScreen}
438     *            to use as the root of the preference hierarchy, or null to use the root
439     *            {@link android.support.v7.preference.PreferenceScreen}.
440     */
441    public void setPreferencesFromResource(@XmlRes int preferencesResId, @Nullable String key) {
442        requirePreferenceManager();
443
444        final PreferenceScreen xmlRoot = mPreferenceManager.inflateFromResource(mStyledContext,
445                preferencesResId, null);
446
447        final Preference root;
448        if (key != null) {
449            root = xmlRoot.findPreference(key);
450            if (!(root instanceof PreferenceScreen)) {
451                throw new IllegalArgumentException("Preference object with key " + key
452                        + " is not a PreferenceScreen");
453            }
454        } else {
455            root = xmlRoot;
456        }
457
458        setPreferenceScreen((PreferenceScreen) root);
459    }
460
461    /**
462     * {@inheritDoc}
463     */
464    public boolean onPreferenceTreeClick(Preference preference) {
465        if (preference.getFragment() != null) {
466            boolean handled = false;
467            if (getCallbackFragment() instanceof OnPreferenceStartFragmentCallback) {
468                handled = ((OnPreferenceStartFragmentCallback) getCallbackFragment())
469                        .onPreferenceStartFragment(this, preference);
470            }
471            if (!handled && getActivity() instanceof OnPreferenceStartFragmentCallback){
472                handled = ((OnPreferenceStartFragmentCallback) getActivity())
473                        .onPreferenceStartFragment(this, preference);
474            }
475            return handled;
476        }
477        return false;
478    }
479
480    /**
481     * Called by
482     * {@link android.support.v7.preference.PreferenceScreen#onClick()} in order to navigate to a
483     * new screen of preferences. Calls
484     * {@link PreferenceFragment.OnPreferenceStartScreenCallback#onPreferenceStartScreen}
485     * if the target fragment or containing activity implements
486     * {@link PreferenceFragment.OnPreferenceStartScreenCallback}.
487     * @param preferenceScreen The {@link android.support.v7.preference.PreferenceScreen} to
488     *                         navigate to.
489     */
490    @Override
491    public void onNavigateToScreen(PreferenceScreen preferenceScreen) {
492        boolean handled = false;
493        if (getCallbackFragment() instanceof OnPreferenceStartScreenCallback) {
494            handled = ((OnPreferenceStartScreenCallback) getCallbackFragment())
495                    .onPreferenceStartScreen(this, preferenceScreen);
496        }
497        if (!handled && getActivity() instanceof OnPreferenceStartScreenCallback) {
498            ((OnPreferenceStartScreenCallback) getActivity())
499                    .onPreferenceStartScreen(this, preferenceScreen);
500        }
501    }
502
503    /**
504     * Finds a {@link Preference} based on its key.
505     *
506     * @param key The key of the preference to retrieve.
507     * @return The {@link Preference} with the key, or null.
508     * @see android.support.v7.preference.PreferenceGroup#findPreference(CharSequence)
509     */
510    public Preference findPreference(CharSequence key) {
511        if (mPreferenceManager == null) {
512            return null;
513        }
514        return mPreferenceManager.findPreference(key);
515    }
516
517    private void requirePreferenceManager() {
518        if (mPreferenceManager == null) {
519            throw new RuntimeException("This should be called after super.onCreate.");
520        }
521    }
522
523    private void postBindPreferences() {
524        if (mHandler.hasMessages(MSG_BIND_PREFERENCES)) return;
525        mHandler.obtainMessage(MSG_BIND_PREFERENCES).sendToTarget();
526    }
527
528    private void bindPreferences() {
529        final PreferenceScreen preferenceScreen = getPreferenceScreen();
530        if (preferenceScreen != null) {
531            getListView().setAdapter(onCreateAdapter(preferenceScreen));
532            preferenceScreen.onAttached();
533        }
534        onBindPreferences();
535    }
536
537    private void unbindPreferences() {
538        final PreferenceScreen preferenceScreen = getPreferenceScreen();
539        if (preferenceScreen != null) {
540            preferenceScreen.onDetached();
541        }
542        onUnbindPreferences();
543    }
544
545    /** @hide */
546    protected void onBindPreferences() {
547    }
548
549    /** @hide */
550    protected void onUnbindPreferences() {
551    }
552
553    public final RecyclerView getListView() {
554        return mList;
555    }
556
557    /**
558     * Creates the {@link android.support.v7.widget.RecyclerView} used to display the preferences.
559     * Subclasses may override this to return a customized
560     * {@link android.support.v7.widget.RecyclerView}.
561     * @param inflater The LayoutInflater object that can be used to inflate the
562     *                 {@link android.support.v7.widget.RecyclerView}.
563     * @param parent The parent {@link android.view.View} that the RecyclerView will be attached to.
564     *               This method should not add the view itself, but this can be used to generate
565     *               the LayoutParams of the view.
566     * @param savedInstanceState If non-null, this view is being re-constructed from a previous
567     *                           saved state as given here
568     * @return A new RecyclerView object to be placed into the view hierarchy
569     */
570    public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent,
571            Bundle savedInstanceState) {
572        RecyclerView recyclerView = (RecyclerView) inflater
573                .inflate(android.support.v7.preference.R.layout.preference_recyclerview,
574                        parent, false);
575
576        recyclerView.setLayoutManager(onCreateLayoutManager());
577
578        return recyclerView;
579    }
580
581    /**
582     * Called from {@link #onCreateRecyclerView} to create the
583     * {@link android.support.v7.widget.RecyclerView.LayoutManager} for the created
584     * {@link android.support.v7.widget.RecyclerView}.
585     * @return A new {@link android.support.v7.widget.RecyclerView.LayoutManager} instance.
586     */
587    public RecyclerView.LayoutManager onCreateLayoutManager() {
588        return new LinearLayoutManager(getActivity());
589    }
590
591    /**
592     * Creates the root adapter.
593     *
594     * @param preferenceScreen Preference screen object to create the adapter for.
595     * @return An adapter that contains the preferences contained in this {@link PreferenceScreen}.
596     */
597    protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) {
598        return new PreferenceGroupAdapter(preferenceScreen);
599    }
600
601    /**
602     * Called when a preference in the tree requests to display a dialog. Subclasses should
603     * override this method to display custom dialogs or to handle dialogs for custom preference
604     * classes.
605     *
606     * @param preference The Preference object requesting the dialog.
607     */
608    @Override
609    public void onDisplayPreferenceDialog(Preference preference) {
610
611        boolean handled = false;
612        if (getCallbackFragment() instanceof OnPreferenceDisplayDialogCallback) {
613            handled = ((OnPreferenceDisplayDialogCallback) getCallbackFragment())
614                    .onPreferenceDisplayDialog(this, preference);
615        }
616        if (!handled && getActivity() instanceof OnPreferenceDisplayDialogCallback) {
617            handled = ((OnPreferenceDisplayDialogCallback) getActivity())
618                    .onPreferenceDisplayDialog(this, preference);
619        }
620
621        if (handled) {
622            return;
623        }
624
625        // check if dialog is already showing
626        if (getFragmentManager().findFragmentByTag(DIALOG_FRAGMENT_TAG) != null) {
627            return;
628        }
629
630        final DialogFragment f;
631        if (preference instanceof EditTextPreference) {
632            f = EditTextPreferenceDialogFragment.newInstance(preference.getKey());
633        } else if (preference instanceof ListPreference) {
634            f = ListPreferenceDialogFragment.newInstance(preference.getKey());
635        } else if (preference instanceof MultiSelectListPreference) {
636            f = MultiSelectListPreferenceDialogFragment.newInstance(preference.getKey());
637        } else {
638            throw new IllegalArgumentException("Tried to display dialog for unknown " +
639                    "preference type. Did you forget to override onDisplayPreferenceDialog()?");
640        }
641        f.setTargetFragment(this, 0);
642        f.show(getFragmentManager(), DIALOG_FRAGMENT_TAG);
643    }
644
645    /**
646     * Basically a wrapper for getParentFragment which is v17+. Used by the leanback preference lib.
647     * @return Fragment to possibly use as a callback
648     * @hide
649     */
650    public Fragment getCallbackFragment() {
651        return null;
652    }
653
654    private class DividerDecoration extends RecyclerView.ItemDecoration {
655
656        private Drawable mDivider;
657        private int mDividerHeight;
658
659        @Override
660        public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
661            if (mDivider == null) {
662                return;
663            }
664            final int childCount = parent.getChildCount();
665            final int width = parent.getWidth();
666            for (int childViewIndex = 0; childViewIndex < childCount; childViewIndex++) {
667                final View view = parent.getChildAt(childViewIndex);
668                if (shouldDrawDividerBelow(view, parent)) {
669                    int top = (int) ViewCompat.getY(view) + view.getHeight();
670                    mDivider.setBounds(0, top, width, top + mDividerHeight);
671                    mDivider.draw(c);
672                }
673            }
674        }
675
676        @Override
677        public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
678                RecyclerView.State state) {
679            if (shouldDrawDividerBelow(view, parent)) {
680                outRect.bottom = mDividerHeight;
681            }
682        }
683
684        private boolean shouldDrawDividerBelow(View view, RecyclerView parent) {
685            final RecyclerView.ViewHolder holder = parent.getChildViewHolder(view);
686            final boolean dividerAllowedBelow = holder instanceof PreferenceViewHolder
687                    && ((PreferenceViewHolder) holder).isDividerAllowedBelow();
688            if (!dividerAllowedBelow) {
689                return false;
690            }
691            boolean nextAllowed = true;
692            int index = parent.indexOfChild(view);
693            if (index < parent.getChildCount() - 1) {
694                final View nextView = parent.getChildAt(index + 1);
695                final RecyclerView.ViewHolder nextHolder = parent.getChildViewHolder(nextView);
696                nextAllowed = nextHolder instanceof PreferenceViewHolder
697                        && ((PreferenceViewHolder) nextHolder).isDividerAllowedAbove();
698            }
699            return nextAllowed;
700        }
701
702        public void setDivider(Drawable divider) {
703            if (divider != null) {
704                mDividerHeight = divider.getIntrinsicHeight();
705            } else {
706                mDividerHeight = 0;
707            }
708            mDivider = divider;
709            mList.invalidateItemDecorations();
710        }
711
712        public void setDividerHeight(int dividerHeight) {
713            mDividerHeight = dividerHeight;
714            mList.invalidateItemDecorations();
715        }
716    }
717}
718