16904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler/*
2ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * Copyright 2018 The Android Open Source Project
36904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler *
46904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler * Licensed under the Apache License, Version 2.0 (the "License");
56904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler * you may not use this file except in compliance with the License.
66904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler * You may obtain a copy of the License at
76904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler *
86904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler *      http://www.apache.org/licenses/LICENSE-2.0
96904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler *
106904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler * Unless required by applicable law or agreed to in writing, software
116904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler * distributed under the License is distributed on an "AS IS" BASIS,
126904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler * See the License for the specific language governing permissions and
14ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * limitations under the License.
156904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler */
166904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
17ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.preference;
186904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
19ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
208e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas
213fadd62b614e4a69aefe920aac640bdb629e502eJason Monkimport android.content.res.TypedArray;
223fadd62b614e4a69aefe920aac640bdb629e502eJason Monkimport android.graphics.drawable.Drawable;
236904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantlerimport android.os.Handler;
246904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantlerimport android.text.TextUtils;
256904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantlerimport android.view.LayoutInflater;
266904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantlerimport android.view.View;
276904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantlerimport android.view.ViewGroup;
286904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
29226a42d7fbfb54e2173e6e04f997d32848c4da62Louis Pullen-Freilichimport androidx.annotation.RestrictTo;
30226a42d7fbfb54e2173e6e04f997d32848c4da62Louis Pullen-Freilichimport androidx.annotation.VisibleForTesting;
31226a42d7fbfb54e2173e6e04f997d32848c4da62Louis Pullen-Freilichimport androidx.core.content.ContextCompat;
32226a42d7fbfb54e2173e6e04f997d32848c4da62Louis Pullen-Freilichimport androidx.core.view.ViewCompat;
33226a42d7fbfb54e2173e6e04f997d32848c4da62Louis Pullen-Freilichimport androidx.recyclerview.widget.DiffUtil;
34226a42d7fbfb54e2173e6e04f997d32848c4da62Louis Pullen-Freilichimport androidx.recyclerview.widget.RecyclerView;
35226a42d7fbfb54e2173e6e04f997d32848c4da62Louis Pullen-Freilich
36ca95d1c53dd086c368cd21685179517d4dadabcdAurimas Liutikasimport java.util.ArrayList;
37ca95d1c53dd086c368cd21685179517d4dadabcdAurimas Liutikasimport java.util.List;
38ca95d1c53dd086c368cd21685179517d4dadabcdAurimas Liutikas
396904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler/**
406904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler * An adapter that connects a RecyclerView to the {@link Preference} objects contained in the
416904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler * associated {@link PreferenceGroup}.
426904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler *
436904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler * @hide
446904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler */
458e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas@RestrictTo(LIBRARY_GROUP)
466904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantlerpublic class PreferenceGroupAdapter extends RecyclerView.Adapter<PreferenceViewHolder>
4766222008cbda61251014caf1442930a48561d25cTony Mantler        implements Preference.OnPreferenceChangeInternalListener,
4866222008cbda61251014caf1442930a48561d25cTony Mantler        PreferenceGroup.PreferencePositionCallback {
496904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
506904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    /**
516904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * The group that we are providing data from.
526904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     */
536904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    private PreferenceGroup mPreferenceGroup;
546904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
556904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    /**
566904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * Maps a position into this adapter -> {@link Preference}. These
576904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * {@link Preference}s don't have to be direct children of this
586904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * {@link PreferenceGroup}, they can be grand children or younger)
596904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     */
606904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    private List<Preference> mPreferenceList;
616904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
626904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    /**
636904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * Contains a sorted list of all preferences in this adapter regardless of visibility. This is
646904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * used to construct {@link #mPreferenceList}
656904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     */
666904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    private List<Preference> mPreferenceListInternal;
676904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
686904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    /**
696904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * List of unique Preference and its subclasses' names and layouts.
706904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     */
716904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    private List<PreferenceLayout> mPreferenceLayouts;
726904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
736904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
746904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    private PreferenceLayout mTempPreferenceLayout = new PreferenceLayout();
756904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
76c88dea08fa07d811eb7cfe77c7a523fd2b0ce339Doris Ling    private Handler mHandler;
77c88dea08fa07d811eb7cfe77c7a523fd2b0ce339Doris Ling
78c88dea08fa07d811eb7cfe77c7a523fd2b0ce339Doris Ling    private CollapsiblePreferenceGroupController mPreferenceGroupController;
796904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
806904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    private Runnable mSyncRunnable = new Runnable() {
81dc8e099ed130434c0238f558277c5bcb6a533121Aurimas Liutikas        @Override
826904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        public void run() {
836904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            syncMyPreferences();
846904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        }
856904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    };
866904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
876904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    private static class PreferenceLayout {
88226a42d7fbfb54e2173e6e04f997d32848c4da62Louis Pullen-Freilich        private int mResId;
89226a42d7fbfb54e2173e6e04f997d32848c4da62Louis Pullen-Freilich        private int mWidgetResId;
90226a42d7fbfb54e2173e6e04f997d32848c4da62Louis Pullen-Freilich        private String mName;
916904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
92226a42d7fbfb54e2173e6e04f997d32848c4da62Louis Pullen-Freilich        PreferenceLayout() {}
9343bd2dbe877d980e72f7e575d4477e77fe2f6d92Tony Mantler
94226a42d7fbfb54e2173e6e04f997d32848c4da62Louis Pullen-Freilich        PreferenceLayout(PreferenceLayout other) {
95226a42d7fbfb54e2173e6e04f997d32848c4da62Louis Pullen-Freilich            mResId = other.mResId;
96226a42d7fbfb54e2173e6e04f997d32848c4da62Louis Pullen-Freilich            mWidgetResId = other.mWidgetResId;
97226a42d7fbfb54e2173e6e04f997d32848c4da62Louis Pullen-Freilich            mName = other.mName;
9843bd2dbe877d980e72f7e575d4477e77fe2f6d92Tony Mantler        }
9943bd2dbe877d980e72f7e575d4477e77fe2f6d92Tony Mantler
1006904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        @Override
1016904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        public boolean equals(Object o) {
1026904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            if (!(o instanceof PreferenceLayout)) {
1036904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler                return false;
1046904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            }
1056904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            final PreferenceLayout other = (PreferenceLayout) o;
106226a42d7fbfb54e2173e6e04f997d32848c4da62Louis Pullen-Freilich            return mResId == other.mResId
107226a42d7fbfb54e2173e6e04f997d32848c4da62Louis Pullen-Freilich                    && mWidgetResId == other.mWidgetResId
108226a42d7fbfb54e2173e6e04f997d32848c4da62Louis Pullen-Freilich                    && TextUtils.equals(mName, other.mName);
1096904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        }
1106904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
1116904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        @Override
1126904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        public int hashCode() {
1136904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            int result = 17;
114226a42d7fbfb54e2173e6e04f997d32848c4da62Louis Pullen-Freilich            result = 31 * result + mResId;
115226a42d7fbfb54e2173e6e04f997d32848c4da62Louis Pullen-Freilich            result = 31 * result + mWidgetResId;
116226a42d7fbfb54e2173e6e04f997d32848c4da62Louis Pullen-Freilich            result = 31 * result + mName.hashCode();
1176904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            return result;
1186904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        }
1196904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
1206904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
1216904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    public PreferenceGroupAdapter(PreferenceGroup preferenceGroup) {
122c88dea08fa07d811eb7cfe77c7a523fd2b0ce339Doris Ling        this(preferenceGroup, new Handler());
123c88dea08fa07d811eb7cfe77c7a523fd2b0ce339Doris Ling    }
124c88dea08fa07d811eb7cfe77c7a523fd2b0ce339Doris Ling
125c88dea08fa07d811eb7cfe77c7a523fd2b0ce339Doris Ling    private PreferenceGroupAdapter(PreferenceGroup preferenceGroup, Handler handler) {
1266904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        mPreferenceGroup = preferenceGroup;
127c88dea08fa07d811eb7cfe77c7a523fd2b0ce339Doris Ling        mHandler = handler;
128c88dea08fa07d811eb7cfe77c7a523fd2b0ce339Doris Ling        mPreferenceGroupController =
129c88dea08fa07d811eb7cfe77c7a523fd2b0ce339Doris Ling                new CollapsiblePreferenceGroupController(preferenceGroup, this);
1306904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        // If this group gets or loses any children, let us know
1316904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        mPreferenceGroup.setOnPreferenceChangeInternalListener(this);
1326904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
1336904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        mPreferenceList = new ArrayList<>();
1346904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        mPreferenceListInternal = new ArrayList<>();
1356904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        mPreferenceLayouts = new ArrayList<>();
1366904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
13783deca7547a76f037c32fdc946173b4708b8d05bTony Mantler        if (mPreferenceGroup instanceof PreferenceScreen) {
13883deca7547a76f037c32fdc946173b4708b8d05bTony Mantler            setHasStableIds(((PreferenceScreen) mPreferenceGroup).shouldUseGeneratedIds());
13983deca7547a76f037c32fdc946173b4708b8d05bTony Mantler        } else {
14083deca7547a76f037c32fdc946173b4708b8d05bTony Mantler            setHasStableIds(true);
14183deca7547a76f037c32fdc946173b4708b8d05bTony Mantler        }
1426904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
1436904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        syncMyPreferences();
1446904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
1456904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
146c88dea08fa07d811eb7cfe77c7a523fd2b0ce339Doris Ling    @VisibleForTesting
147c88dea08fa07d811eb7cfe77c7a523fd2b0ce339Doris Ling    static PreferenceGroupAdapter createInstanceWithCustomHandler(PreferenceGroup preferenceGroup,
148c88dea08fa07d811eb7cfe77c7a523fd2b0ce339Doris Ling            Handler handler) {
149c88dea08fa07d811eb7cfe77c7a523fd2b0ce339Doris Ling        return new PreferenceGroupAdapter(preferenceGroup, handler);
150c88dea08fa07d811eb7cfe77c7a523fd2b0ce339Doris Ling    }
151c88dea08fa07d811eb7cfe77c7a523fd2b0ce339Doris Ling
1526904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    private void syncMyPreferences() {
153121b3474bd6a697c9b3e3177ddef28c37ed77032Tony Mantler        for (final Preference preference : mPreferenceListInternal) {
154121b3474bd6a697c9b3e3177ddef28c37ed77032Tony Mantler            // Clear out the listeners in anticipation of some items being removed. This listener
155121b3474bd6a697c9b3e3177ddef28c37ed77032Tony Mantler            // will be (re-)added to the remaining prefs when we flatten.
156121b3474bd6a697c9b3e3177ddef28c37ed77032Tony Mantler            preference.setOnPreferenceChangeInternalListener(null);
157121b3474bd6a697c9b3e3177ddef28c37ed77032Tony Mantler        }
1589c0823fe1738cae176c7b32e3364f0bec1dea13bTony Mantler        final List<Preference> fullPreferenceList = new ArrayList<>(mPreferenceListInternal.size());
1599c0823fe1738cae176c7b32e3364f0bec1dea13bTony Mantler        flattenPreferenceGroup(fullPreferenceList, mPreferenceGroup);
1606904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
161c88dea08fa07d811eb7cfe77c7a523fd2b0ce339Doris Ling        final List<Preference> visiblePreferenceList =
162226a42d7fbfb54e2173e6e04f997d32848c4da62Louis Pullen-Freilich                mPreferenceGroupController.createVisiblePreferencesList(mPreferenceGroup);
1636904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
16484352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler        final List<Preference> oldVisibleList = mPreferenceList;
1659c0823fe1738cae176c7b32e3364f0bec1dea13bTony Mantler        mPreferenceList = visiblePreferenceList;
1669c0823fe1738cae176c7b32e3364f0bec1dea13bTony Mantler        mPreferenceListInternal = fullPreferenceList;
1676904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
16884352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler        final PreferenceManager preferenceManager = mPreferenceGroup.getPreferenceManager();
16984352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler        if (preferenceManager != null
17084352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler                && preferenceManager.getPreferenceComparisonCallback() != null) {
17184352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler            final PreferenceManager.PreferenceComparisonCallback comparisonCallback =
17284352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler                    preferenceManager.getPreferenceComparisonCallback();
17384352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler            final DiffUtil.DiffResult result = DiffUtil.calculateDiff(new DiffUtil.Callback() {
17484352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler                @Override
17584352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler                public int getOldListSize() {
17684352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler                    return oldVisibleList.size();
17784352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler                }
17884352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler
17984352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler                @Override
18084352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler                public int getNewListSize() {
18184352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler                    return visiblePreferenceList.size();
18284352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler                }
18384352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler
18484352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler                @Override
18584352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler                public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
18684352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler                    return comparisonCallback.arePreferenceItemsTheSame(
18784352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler                            oldVisibleList.get(oldItemPosition),
18884352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler                            visiblePreferenceList.get(newItemPosition));
18984352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler                }
19084352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler
19184352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler                @Override
19284352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler                public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
19384352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler                    return comparisonCallback.arePreferenceContentsTheSame(
19484352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler                            oldVisibleList.get(oldItemPosition),
19584352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler                            visiblePreferenceList.get(newItemPosition));
19684352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler                }
19784352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler            });
19884352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler
19984352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler            result.dispatchUpdatesTo(this);
20084352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler        } else {
20184352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler            notifyDataSetChanged();
20284352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler        }
20384352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler
20484352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler        for (final Preference preference : fullPreferenceList) {
20584352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler            preference.clearWasDetached();
20684352192fbccf572473bb953ba3e9ebede60fbcaTony Mantler        }
2076904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
2086904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
2096904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    private void flattenPreferenceGroup(List<Preference> preferences, PreferenceGroup group) {
2106904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        group.sortPreferences();
2116904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
2126904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        final int groupSize = group.getPreferenceCount();
2136904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        for (int i = 0; i < groupSize; i++) {
2146904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            final Preference preference = group.getPreference(i);
2156904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
2166904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            preferences.add(preference);
2176904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
2186904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            addPreferenceClassName(preference);
2196904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
2206904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            if (preference instanceof PreferenceGroup) {
2216904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler                final PreferenceGroup preferenceAsGroup = (PreferenceGroup) preference;
2226904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler                if (preferenceAsGroup.isOnSameScreenAsChildren()) {
2236904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler                    flattenPreferenceGroup(preferences, preferenceAsGroup);
2246904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler                }
2256904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            }
2266904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
2276904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            preference.setOnPreferenceChangeInternalListener(this);
2286904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        }
2296904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
2306904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
2316904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    /**
2326904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * Creates a string that includes the preference name, layout id and widget layout id.
2336904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * If a particular preference type uses 2 different resources, they will be treated as
2346904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * different view types.
2356904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     */
2366904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    private PreferenceLayout createPreferenceLayout(Preference preference, PreferenceLayout in) {
237226a42d7fbfb54e2173e6e04f997d32848c4da62Louis Pullen-Freilich        PreferenceLayout pl = in != null ? in : new PreferenceLayout();
238226a42d7fbfb54e2173e6e04f997d32848c4da62Louis Pullen-Freilich        pl.mName = preference.getClass().getName();
239226a42d7fbfb54e2173e6e04f997d32848c4da62Louis Pullen-Freilich        pl.mResId = preference.getLayoutResource();
240226a42d7fbfb54e2173e6e04f997d32848c4da62Louis Pullen-Freilich        pl.mWidgetResId = preference.getWidgetLayoutResource();
2416904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        return pl;
2426904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
2436904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
2446904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    private void addPreferenceClassName(Preference preference) {
2456904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        final PreferenceLayout pl = createPreferenceLayout(preference, null);
2466904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        if (!mPreferenceLayouts.contains(pl)) {
2476904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            mPreferenceLayouts.add(pl);
2486904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        }
2496904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
2506904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
2516904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    @Override
2526904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    public int getItemCount() {
2536904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        return mPreferenceList.size();
2546904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
2556904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
2566904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    public Preference getItem(int position) {
2576904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        if (position < 0 || position >= getItemCount()) return null;
2586904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        return mPreferenceList.get(position);
2596904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
2606904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
261dc8e099ed130434c0238f558277c5bcb6a533121Aurimas Liutikas    @Override
2626904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    public long getItemId(int position) {
26383deca7547a76f037c32fdc946173b4708b8d05bTony Mantler        if (!hasStableIds()) {
26483deca7547a76f037c32fdc946173b4708b8d05bTony Mantler            return RecyclerView.NO_ID;
26583deca7547a76f037c32fdc946173b4708b8d05bTony Mantler        }
2666904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        return this.getItem(position).getId();
2676904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
2686904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
269dc8e099ed130434c0238f558277c5bcb6a533121Aurimas Liutikas    @Override
2706904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    public void onPreferenceChange(Preference preference) {
2715c58355b66d26d4992d68ec2657d6d5ad9a53590Tony Mantler        final int index = mPreferenceList.indexOf(preference);
2725c58355b66d26d4992d68ec2657d6d5ad9a53590Tony Mantler        // If we don't find the preference, we don't need to notify anyone
2735c58355b66d26d4992d68ec2657d6d5ad9a53590Tony Mantler        if (index != -1) {
2745c58355b66d26d4992d68ec2657d6d5ad9a53590Tony Mantler            // Send the pref object as a placeholder to ensure the view holder is recycled in place
2755c58355b66d26d4992d68ec2657d6d5ad9a53590Tony Mantler            notifyItemChanged(index, preference);
2765c58355b66d26d4992d68ec2657d6d5ad9a53590Tony Mantler        }
2776904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
2786904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
279dc8e099ed130434c0238f558277c5bcb6a533121Aurimas Liutikas    @Override
2806904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    public void onPreferenceHierarchyChange(Preference preference) {
2816904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        mHandler.removeCallbacks(mSyncRunnable);
2826904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        mHandler.post(mSyncRunnable);
2836904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
2846904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
2856904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    @Override
2866904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    public void onPreferenceVisibilityChange(Preference preference) {
287121b3474bd6a697c9b3e3177ddef28c37ed77032Tony Mantler        if (!mPreferenceListInternal.contains(preference)) {
288121b3474bd6a697c9b3e3177ddef28c37ed77032Tony Mantler            return;
289121b3474bd6a697c9b3e3177ddef28c37ed77032Tony Mantler        }
290c88dea08fa07d811eb7cfe77c7a523fd2b0ce339Doris Ling        if (mPreferenceGroupController.onPreferenceVisibilityChange(preference)) {
291c88dea08fa07d811eb7cfe77c7a523fd2b0ce339Doris Ling            return;
292c88dea08fa07d811eb7cfe77c7a523fd2b0ce339Doris Ling        }
2936904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        if (preference.isVisible()) {
2946904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            // The preference has become visible, we need to add it in the correct location.
2956904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
2966904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            // Index (inferred) in mPreferenceList of the item preceding the newly visible pref
2976904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            int previousVisibleIndex = -1;
2986904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            for (final Preference pref : mPreferenceListInternal) {
2996904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler                if (preference.equals(pref)) {
3006904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler                    break;
3016904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler                }
3026904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler                if (pref.isVisible()) {
3036904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler                    previousVisibleIndex++;
3046904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler                }
3056904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            }
3066904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            // Insert this preference into the active list just after the previous visible entry
3076904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            mPreferenceList.add(previousVisibleIndex + 1, preference);
3086904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
3096904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            notifyItemInserted(previousVisibleIndex + 1);
3106904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        } else {
311c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas            // The preference has become invisible. Find it in the list and remove it.
3126904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
3136904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            int removalIndex;
3146904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            final int listSize = mPreferenceList.size();
3156904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            for (removalIndex = 0; removalIndex < listSize; removalIndex++) {
3166904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler                if (preference.equals(mPreferenceList.get(removalIndex))) {
3176904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler                    break;
3186904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler                }
3196904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            }
3206904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            mPreferenceList.remove(removalIndex);
3216904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            notifyItemRemoved(removalIndex);
3226904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        }
3236904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
3246904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
3256904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    @Override
3266904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    public int getItemViewType(int position) {
3276904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        final Preference preference = this.getItem(position);
3286904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
3296904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        mTempPreferenceLayout = createPreferenceLayout(preference, mTempPreferenceLayout);
3306904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
33143bd2dbe877d980e72f7e575d4477e77fe2f6d92Tony Mantler        int viewType = mPreferenceLayouts.indexOf(mTempPreferenceLayout);
33243bd2dbe877d980e72f7e575d4477e77fe2f6d92Tony Mantler        if (viewType != -1) {
33343bd2dbe877d980e72f7e575d4477e77fe2f6d92Tony Mantler            return viewType;
33443bd2dbe877d980e72f7e575d4477e77fe2f6d92Tony Mantler        } else {
33543bd2dbe877d980e72f7e575d4477e77fe2f6d92Tony Mantler            viewType = mPreferenceLayouts.size();
33643bd2dbe877d980e72f7e575d4477e77fe2f6d92Tony Mantler            mPreferenceLayouts.add(new PreferenceLayout(mTempPreferenceLayout));
33743bd2dbe877d980e72f7e575d4477e77fe2f6d92Tony Mantler            return viewType;
33843bd2dbe877d980e72f7e575d4477e77fe2f6d92Tony Mantler        }
3396904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
3406904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
3416904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    @Override
3426904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    public PreferenceViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
3436904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        final PreferenceLayout pl = mPreferenceLayouts.get(viewType);
3446904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
3453fadd62b614e4a69aefe920aac640bdb629e502eJason Monk        TypedArray a
3463fadd62b614e4a69aefe920aac640bdb629e502eJason Monk                = parent.getContext().obtainStyledAttributes(null, R.styleable.BackgroundStyle);
3473fadd62b614e4a69aefe920aac640bdb629e502eJason Monk        Drawable background
3483fadd62b614e4a69aefe920aac640bdb629e502eJason Monk                = a.getDrawable(R.styleable.BackgroundStyle_android_selectableItemBackground);
3493fadd62b614e4a69aefe920aac640bdb629e502eJason Monk        if (background == null) {
350f82cdb0326f2cb78809fe0c536c5fbb527b7ae00Filip Pavlis            background = ContextCompat.getDrawable(parent.getContext(),
351f82cdb0326f2cb78809fe0c536c5fbb527b7ae00Filip Pavlis                    android.R.drawable.list_selector_background);
3523fadd62b614e4a69aefe920aac640bdb629e502eJason Monk        }
3533fadd62b614e4a69aefe920aac640bdb629e502eJason Monk        a.recycle();
3546904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
355226a42d7fbfb54e2173e6e04f997d32848c4da62Louis Pullen-Freilich        final View view = inflater.inflate(pl.mResId, parent, false);
35673886daee29a8a910a0f9208b7c5c885dda95eb1Jason Monk        if (view.getBackground() == null) {
35747082c30c630c34829439a9eecd1cf7e8d255a86Aurimas Liutikas            ViewCompat.setBackground(view, background);
35873886daee29a8a910a0f9208b7c5c885dda95eb1Jason Monk        }
3596904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
360d1be3655c1de9af3174d285d42f9bd5b47ce9020Todd Volkert        final ViewGroup widgetFrame = (ViewGroup) view.findViewById(android.R.id.widget_frame);
3616904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        if (widgetFrame != null) {
362226a42d7fbfb54e2173e6e04f997d32848c4da62Louis Pullen-Freilich            if (pl.mWidgetResId != 0) {
363226a42d7fbfb54e2173e6e04f997d32848c4da62Louis Pullen-Freilich                inflater.inflate(pl.mWidgetResId, widgetFrame);
3646904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            } else {
3656904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler                widgetFrame.setVisibility(View.GONE);
3666904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            }
3676904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        }
3686904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
3696904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        return new PreferenceViewHolder(view);
3706904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
3716904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
3726904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    @Override
3736904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    public void onBindViewHolder(PreferenceViewHolder holder, int position) {
3746904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        final Preference preference = getItem(position);
3756904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        preference.onBindViewHolder(holder);
3766904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
37766222008cbda61251014caf1442930a48561d25cTony Mantler
37866222008cbda61251014caf1442930a48561d25cTony Mantler    @Override
37966222008cbda61251014caf1442930a48561d25cTony Mantler    public int getPreferenceAdapterPosition(String key) {
38066222008cbda61251014caf1442930a48561d25cTony Mantler        final int size = mPreferenceList.size();
38166222008cbda61251014caf1442930a48561d25cTony Mantler        for (int i = 0; i < size; i++) {
38266222008cbda61251014caf1442930a48561d25cTony Mantler            final Preference candidate = mPreferenceList.get(i);
38366222008cbda61251014caf1442930a48561d25cTony Mantler            if (TextUtils.equals(key, candidate.getKey())) {
38466222008cbda61251014caf1442930a48561d25cTony Mantler                return i;
38566222008cbda61251014caf1442930a48561d25cTony Mantler            }
38666222008cbda61251014caf1442930a48561d25cTony Mantler        }
38766222008cbda61251014caf1442930a48561d25cTony Mantler        return RecyclerView.NO_POSITION;
38866222008cbda61251014caf1442930a48561d25cTony Mantler    }
38966222008cbda61251014caf1442930a48561d25cTony Mantler
39066222008cbda61251014caf1442930a48561d25cTony Mantler    @Override
39166222008cbda61251014caf1442930a48561d25cTony Mantler    public int getPreferenceAdapterPosition(Preference preference) {
39266222008cbda61251014caf1442930a48561d25cTony Mantler        final int size = mPreferenceList.size();
39366222008cbda61251014caf1442930a48561d25cTony Mantler        for (int i = 0; i < size; i++) {
394c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas            final Preference candidate = mPreferenceList.get(i);
395c9a859537b0871f84afeeb706a5b425fe3f2b4ddAurimas Liutikas            if (candidate != null && candidate.equals(preference)) {
39666222008cbda61251014caf1442930a48561d25cTony Mantler                return i;
39766222008cbda61251014caf1442930a48561d25cTony Mantler            }
39866222008cbda61251014caf1442930a48561d25cTony Mantler        }
39966222008cbda61251014caf1442930a48561d25cTony Mantler        return RecyclerView.NO_POSITION;
40066222008cbda61251014caf1442930a48561d25cTony Mantler    }
4016904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler}
402