16904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler/*
26904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler * Copyright (C) 2015 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
146904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler * limitations under the License
156904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler */
166904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
176904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantlerpackage android.support.v7.preference;
186904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
198e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikasimport static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
208e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas
216904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantlerimport android.content.Context;
226904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantlerimport android.content.res.TypedArray;
236904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantlerimport android.os.Bundle;
24c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantlerimport android.os.Handler;
25c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viveretteimport android.support.annotation.RestrictTo;
2684765eaea7da18d0576db557959129e9d0db8e8cTony Mantlerimport android.support.v4.content.res.TypedArrayUtils;
27c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantlerimport android.support.v4.util.SimpleArrayMap;
286904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantlerimport android.text.TextUtils;
296904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantlerimport android.util.AttributeSet;
306904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
316904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantlerimport java.util.ArrayList;
326904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantlerimport java.util.Collections;
336904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantlerimport java.util.List;
346904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
356904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler/**
366904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler * A container for multiple
376904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler * {@link Preference} objects. It is a base class for  Preference objects that are
386904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler * parents, such as {@link PreferenceCategory} and {@link PreferenceScreen}.
396904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler *
406904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler * <div class="special reference">
416904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler * <h3>Developer Guides</h3>
426904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler * <p>For information about building a settings UI with Preferences,
436904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler * read the <a href="{@docRoot}guide/topics/ui/settings.html">Settings</a>
446904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler * guide.</p>
456904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler * </div>
466904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler *
47929f27aab7ac7231f3734c988d5ee7201627d535Alan Viverette * @attr name android:orderingFromXml
486904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler */
496904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantlerpublic abstract class PreferenceGroup extends Preference {
506904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    /**
516904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * The container for child {@link Preference}s. This is sorted based on the
526904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * ordering, please use {@link #addPreference(Preference)} instead of adding
536904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * to this directly.
546904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     */
556904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    private List<Preference> mPreferenceList;
566904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
576904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    private boolean mOrderingAsAdded = true;
586904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
596904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    private int mCurrentPreferenceOrder = 0;
606904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
61e61b4475e1837034d8926593aff9d35f8dfaebe2Tony Mantler    private boolean mAttachedToHierarchy = false;
626904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
63c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler    private final SimpleArrayMap<String, Long> mIdRecycleCache = new SimpleArrayMap<>();
64c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler    private final Handler mHandler = new Handler();
65c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler    private final Runnable mClearRecycleCacheRunnable = new Runnable() {
66c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler        @Override
67c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler        public void run() {
68c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler            synchronized (this) {
69c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler                mIdRecycleCache.clear();
70c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler            }
71c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler        }
72c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler    };
73c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler
746904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    public PreferenceGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
756904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        super(context, attrs, defStyleAttr, defStyleRes);
766904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
776904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        mPreferenceList = new ArrayList<>();
786904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
796904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        final TypedArray a = context.obtainStyledAttributes(
806904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler                attrs, R.styleable.PreferenceGroup, defStyleAttr, defStyleRes);
8184765eaea7da18d0576db557959129e9d0db8e8cTony Mantler
8284765eaea7da18d0576db557959129e9d0db8e8cTony Mantler        mOrderingAsAdded =
8384765eaea7da18d0576db557959129e9d0db8e8cTony Mantler                TypedArrayUtils.getBoolean(a, R.styleable.PreferenceGroup_orderingFromXml,
8484765eaea7da18d0576db557959129e9d0db8e8cTony Mantler                        R.styleable.PreferenceGroup_orderingFromXml, true);
8584765eaea7da18d0576db557959129e9d0db8e8cTony Mantler
866904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        a.recycle();
876904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
886904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
896904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    public PreferenceGroup(Context context, AttributeSet attrs, int defStyleAttr) {
906904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        this(context, attrs, defStyleAttr, 0);
916904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
926904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
936904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    public PreferenceGroup(Context context, AttributeSet attrs) {
946904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        this(context, attrs, 0);
956904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
966904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
976904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    /**
986904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * Whether to order the {@link Preference} children of this group as they
996904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * are added. If this is false, the ordering will follow each Preference
1006904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * order and default to alphabetic for those without an order.
1016904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * <p>
1026904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * If this is called after preferences are added, they will not be
1036904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * re-ordered in the order they were added, hence call this method early on.
1046904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     *
1056904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * @param orderingAsAdded Whether to order according to the order added.
1066904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * @see Preference#setOrder(int)
1076904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     */
1086904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    public void setOrderingAsAdded(boolean orderingAsAdded) {
1096904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        mOrderingAsAdded = orderingAsAdded;
1106904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
1116904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
1126904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    /**
1136904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * Whether this group is ordering preferences in the order they are added.
1146904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     *
1156904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * @return Whether this group orders based on the order the children are added.
1166904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * @see #setOrderingAsAdded(boolean)
1176904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     */
1186904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    public boolean isOrderingAsAdded() {
1196904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        return mOrderingAsAdded;
1206904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
1216904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
1226904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    /**
1236904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * Called by the inflater to add an item to this group.
1246904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     */
1256904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    public void addItemFromInflater(Preference preference) {
1266904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        addPreference(preference);
1276904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
1286904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
1296904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    /**
1306904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * Returns the number of children {@link Preference}s.
1316904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * @return The number of preference children in this group.
1326904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     */
1336904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    public int getPreferenceCount() {
1346904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        return mPreferenceList.size();
1356904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
1366904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
1376904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    /**
1386904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * Returns the {@link Preference} at a particular index.
1396904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     *
1406904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * @param index The index of the {@link Preference} to retrieve.
1416904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * @return The {@link Preference}.
1426904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     */
1436904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    public Preference getPreference(int index) {
1446904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        return mPreferenceList.get(index);
1456904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
1466904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
1476904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    /**
1486904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * Adds a {@link Preference} at the correct position based on the
1496904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * preference's order.
1506904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     *
1516904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * @param preference The preference to add.
1526904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * @return Whether the preference is now in this group.
1536904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     */
1546904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    public boolean addPreference(Preference preference) {
1556904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        if (mPreferenceList.contains(preference)) {
1566904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            // Exists
1576904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            return true;
1586904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        }
1596904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
1606904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        if (preference.getOrder() == DEFAULT_ORDER) {
1616904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            if (mOrderingAsAdded) {
1626904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler                preference.setOrder(mCurrentPreferenceOrder++);
1636904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            }
1646904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
1656904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            if (preference instanceof PreferenceGroup) {
1666904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler                // TODO: fix (method is called tail recursively when inflating,
1676904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler                // so we won't end up properly passing this flag down to children
1686904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler                ((PreferenceGroup)preference).setOrderingAsAdded(mOrderingAsAdded);
1696904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            }
1706904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        }
1716904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
1726904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        int insertionIndex = Collections.binarySearch(mPreferenceList, preference);
1736904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        if (insertionIndex < 0) {
1746904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            insertionIndex = insertionIndex * -1 - 1;
1756904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        }
1766904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
1776904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        if (!onPrepareAddPreference(preference)) {
1786904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            return false;
1796904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        }
1806904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
1816904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        synchronized(this) {
1826904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            mPreferenceList.add(insertionIndex, preference);
1836904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        }
1846904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
185c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler        final PreferenceManager preferenceManager = getPreferenceManager();
186c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler        final String key = preference.getKey();
187c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler        final long id;
188c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler        if (key != null && mIdRecycleCache.containsKey(key)) {
189c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler            id = mIdRecycleCache.get(key);
190c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler            mIdRecycleCache.remove(key);
191c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler        } else {
192c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler            id = preferenceManager.getNextId();
193c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler        }
194c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler        preference.onAttachedToHierarchy(preferenceManager, id);
19579af8fc48667f5fb21295fdd8cb2b5cbd4650eccFilip Pavlis        preference.assignParent(this);
1966904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
197e61b4475e1837034d8926593aff9d35f8dfaebe2Tony Mantler        if (mAttachedToHierarchy) {
1986904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            preference.onAttached();
1996904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        }
2006904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
2016904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        notifyHierarchyChanged();
2026904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
2036904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        return true;
2046904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
2056904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
2066904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    /**
2076904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * Removes a {@link Preference} from this group.
2086904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     *
2096904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * @param preference The preference to remove.
2106904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * @return Whether the preference was found and removed.
2116904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     */
2126904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    public boolean removePreference(Preference preference) {
2136904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        final boolean returnValue = removePreferenceInt(preference);
2146904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        notifyHierarchyChanged();
2156904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        return returnValue;
2166904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
2176904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
2186904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    private boolean removePreferenceInt(Preference preference) {
2196904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        synchronized(this) {
2206904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            preference.onPrepareForRemoval();
22179af8fc48667f5fb21295fdd8cb2b5cbd4650eccFilip Pavlis            if (preference.getParent() == this) {
22279af8fc48667f5fb21295fdd8cb2b5cbd4650eccFilip Pavlis                preference.assignParent(null);
22379af8fc48667f5fb21295fdd8cb2b5cbd4650eccFilip Pavlis            }
2248f7f0d48fac8f83e90a04ba67f9a03f93c6ed262Jason Monk            boolean success = mPreferenceList.remove(preference);
225c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler            if (success) {
226c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler                // If this preference, or another preference with the same key, gets re-added
227c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler                // immediately, we want it to have the same id so that it can be correctly tracked
228c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler                // in the adapter by RecyclerView, to make it appear as if it has only been
229c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler                // seamlessly updated. If the preference is not re-added by the time the handler
230c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler                // runs, we take that as a signal that the preference will not be re-added soon
231c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler                // in which case it does not need to retain the same id.
232c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler
233c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler                // If two (or more) preferences have the same (or null) key and both are removed
234c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler                // and then re-added, only one id will be recycled and the second (and later)
235c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler                // preferences will receive a newly generated id. This use pattern of the preference
236c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler                // API is strongly discouraged.
237c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler                final String key = preference.getKey();
238c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler                if (key != null) {
239c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler                    mIdRecycleCache.put(key, preference.getId());
240c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler                    mHandler.removeCallbacks(mClearRecycleCacheRunnable);
241c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler                    mHandler.post(mClearRecycleCacheRunnable);
242c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler                }
243c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler                if (mAttachedToHierarchy) {
244c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler                    preference.onDetached();
245c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler                }
2468f7f0d48fac8f83e90a04ba67f9a03f93c6ed262Jason Monk            }
247c4276fe96c5c73539a249bcd8c39ce8fb22859b5Tony Mantler
2488f7f0d48fac8f83e90a04ba67f9a03f93c6ed262Jason Monk            return success;
2496904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        }
2506904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
2516904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
2526904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    /**
2536904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * Removes all {@link Preference Preferences} from this group.
2546904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     */
2556904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    public void removeAll() {
2566904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        synchronized(this) {
2576904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            List<Preference> preferenceList = mPreferenceList;
2586904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            for (int i = preferenceList.size() - 1; i >= 0; i--) {
2596904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler                removePreferenceInt(preferenceList.get(0));
2606904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            }
2616904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        }
2626904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        notifyHierarchyChanged();
2636904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
2646904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
2656904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    /**
2666904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * Prepares a {@link Preference} to be added to the group.
2676904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     *
2686904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * @param preference The preference to add.
2696904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * @return Whether to allow adding the preference (true), or not (false).
2706904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     */
2716904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    protected boolean onPrepareAddPreference(Preference preference) {
2726904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        preference.onParentChanged(this, shouldDisableDependents());
2736904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        return true;
2746904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
2756904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
2766904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    /**
2776904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * Finds a {@link Preference} based on its key. If two {@link Preference}
2786904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * share the same key (not recommended), the first to appear will be
2796904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * returned (to retrieve the other preference with the same key, call this
2806904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * method on the first preference). If this preference has the key, it will
2816904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * not be returned.
2826904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * <p>
2836904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * This will recursively search for the preference into children that are
2846904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * also {@link PreferenceGroup PreferenceGroups}.
2856904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     *
2866904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * @param key The key of the preference to retrieve.
2876904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * @return The {@link Preference} with the key, or null.
2886904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     */
2896904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    public Preference findPreference(CharSequence key) {
2906904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        if (TextUtils.equals(getKey(), key)) {
2916904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            return this;
2926904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        }
2936904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        final int preferenceCount = getPreferenceCount();
2946904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        for (int i = 0; i < preferenceCount; i++) {
2956904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            final Preference preference = getPreference(i);
2966904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            final String curKey = preference.getKey();
2976904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
2986904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            if (curKey != null && curKey.equals(key)) {
2996904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler                return preference;
3006904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            }
3016904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
3026904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            if (preference instanceof PreferenceGroup) {
3036904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler                final Preference returnedPreference = ((PreferenceGroup)preference)
3046904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler                        .findPreference(key);
3056904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler                if (returnedPreference != null) {
3066904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler                    return returnedPreference;
3076904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler                }
3086904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            }
3096904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        }
3106904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
3116904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        return null;
3126904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
3136904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
3146904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    /**
3156904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * Whether this preference group should be shown on the same screen as its
3166904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * contained preferences.
3176904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     *
3186904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     * @return True if the contained preferences should be shown on the same
3196904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     *         screen as this preference.
3206904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler     */
3216904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    protected boolean isOnSameScreenAsChildren() {
3226904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        return true;
3236904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
3246904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
32583deca7547a76f037c32fdc946173b4708b8d05bTony Mantler    /**
32683deca7547a76f037c32fdc946173b4708b8d05bTony Mantler     * Returns true if we're between {@link #onAttached()} and {@link #onPrepareForRemoval()}
32783deca7547a76f037c32fdc946173b4708b8d05bTony Mantler     * @hide
32883deca7547a76f037c32fdc946173b4708b8d05bTony Mantler     */
3298e10080c914d1ad0784394fa3026b85535535847Aurimas Liutikas    @RestrictTo(LIBRARY_GROUP)
33083deca7547a76f037c32fdc946173b4708b8d05bTony Mantler    public boolean isAttached() {
33183deca7547a76f037c32fdc946173b4708b8d05bTony Mantler        return mAttachedToHierarchy;
33283deca7547a76f037c32fdc946173b4708b8d05bTony Mantler    }
33383deca7547a76f037c32fdc946173b4708b8d05bTony Mantler
3346904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    @Override
335e61b4475e1837034d8926593aff9d35f8dfaebe2Tony Mantler    public void onAttached() {
3366904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        super.onAttached();
3376904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
3386904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        // Mark as attached so if a preference is later added to this group, we
3396904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        // can tell it we are already attached
340e61b4475e1837034d8926593aff9d35f8dfaebe2Tony Mantler        mAttachedToHierarchy = true;
3416904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
3426904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        // Dispatch to all contained preferences
3436904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        final int preferenceCount = getPreferenceCount();
3446904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        for (int i = 0; i < preferenceCount; i++) {
3456904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            getPreference(i).onAttached();
3466904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        }
3476904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
3486904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
3496904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    @Override
3508f7f0d48fac8f83e90a04ba67f9a03f93c6ed262Jason Monk    public void onDetached() {
3518f7f0d48fac8f83e90a04ba67f9a03f93c6ed262Jason Monk        super.onDetached();
3526904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
3536904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        // We won't be attached to the activity anymore
354e61b4475e1837034d8926593aff9d35f8dfaebe2Tony Mantler        mAttachedToHierarchy = false;
3558f7f0d48fac8f83e90a04ba67f9a03f93c6ed262Jason Monk
3568f7f0d48fac8f83e90a04ba67f9a03f93c6ed262Jason Monk        // Dispatch to all contained preferences
3578f7f0d48fac8f83e90a04ba67f9a03f93c6ed262Jason Monk        final int preferenceCount = getPreferenceCount();
3588f7f0d48fac8f83e90a04ba67f9a03f93c6ed262Jason Monk        for (int i = 0; i < preferenceCount; i++) {
3598f7f0d48fac8f83e90a04ba67f9a03f93c6ed262Jason Monk            getPreference(i).onDetached();
3608f7f0d48fac8f83e90a04ba67f9a03f93c6ed262Jason Monk        }
3616904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
3626904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
3636904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    @Override
3646904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    public void notifyDependencyChange(boolean disableDependents) {
3656904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        super.notifyDependencyChange(disableDependents);
3666904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
3676904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        // Child preferences have an implicit dependency on their containing
3686904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        // group. Dispatch dependency change to all contained preferences.
3696904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        final int preferenceCount = getPreferenceCount();
3706904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        for (int i = 0; i < preferenceCount; i++) {
3716904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            getPreference(i).onParentChanged(this, disableDependents);
3726904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        }
3736904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
3746904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
3756904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    void sortPreferences() {
3766904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        synchronized (this) {
3776904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            Collections.sort(mPreferenceList);
3786904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        }
3796904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
3806904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
3816904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    @Override
3826904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    protected void dispatchSaveInstanceState(Bundle container) {
3836904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        super.dispatchSaveInstanceState(container);
3846904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
3856904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        // Dispatch to all contained preferences
3866904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        final int preferenceCount = getPreferenceCount();
3876904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        for (int i = 0; i < preferenceCount; i++) {
3886904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            getPreference(i).dispatchSaveInstanceState(container);
3896904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        }
3906904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
3916904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
3926904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    @Override
3936904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    protected void dispatchRestoreInstanceState(Bundle container) {
3946904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        super.dispatchRestoreInstanceState(container);
3956904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
3966904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        // Dispatch to all contained preferences
3976904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        final int preferenceCount = getPreferenceCount();
3986904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        for (int i = 0; i < preferenceCount; i++) {
3996904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler            getPreference(i).dispatchRestoreInstanceState(container);
4006904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler        }
4016904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler    }
4026904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler
40366222008cbda61251014caf1442930a48561d25cTony Mantler    /**
40466222008cbda61251014caf1442930a48561d25cTony Mantler     * Interface for PreferenceGroup Adapters to implement so that
40566222008cbda61251014caf1442930a48561d25cTony Mantler     * {@link android.support.v14.preference.PreferenceFragment#scrollToPreference(String)} and
40666222008cbda61251014caf1442930a48561d25cTony Mantler     * {@link android.support.v14.preference.PreferenceFragment#scrollToPreference(Preference)} or
40766222008cbda61251014caf1442930a48561d25cTony Mantler     * {@link PreferenceFragmentCompat#scrollToPreference(String)} and
40866222008cbda61251014caf1442930a48561d25cTony Mantler     * {@link PreferenceFragmentCompat#scrollToPreference(Preference)}
40966222008cbda61251014caf1442930a48561d25cTony Mantler     * can determine the correct scroll position to request.
41066222008cbda61251014caf1442930a48561d25cTony Mantler     */
41166222008cbda61251014caf1442930a48561d25cTony Mantler    public interface PreferencePositionCallback {
41266222008cbda61251014caf1442930a48561d25cTony Mantler
41366222008cbda61251014caf1442930a48561d25cTony Mantler        /**
41466222008cbda61251014caf1442930a48561d25cTony Mantler         * Return the adapter position of the first {@link Preference} with the specified key
41566222008cbda61251014caf1442930a48561d25cTony Mantler         * @param key Key of {@link Preference} to find
41666222008cbda61251014caf1442930a48561d25cTony Mantler         * @return Adapter position of the {@link Preference} or
41766222008cbda61251014caf1442930a48561d25cTony Mantler         *         {@link android.support.v7.widget.RecyclerView#NO_POSITION} if not found
41866222008cbda61251014caf1442930a48561d25cTony Mantler         */
41966222008cbda61251014caf1442930a48561d25cTony Mantler        int getPreferenceAdapterPosition(String key);
42066222008cbda61251014caf1442930a48561d25cTony Mantler
42166222008cbda61251014caf1442930a48561d25cTony Mantler        /**
42266222008cbda61251014caf1442930a48561d25cTony Mantler         * Return the adapter position of the specified {@link Preference} object
42366222008cbda61251014caf1442930a48561d25cTony Mantler         * @param preference {@link Preference} object to find
42466222008cbda61251014caf1442930a48561d25cTony Mantler         * @return Adapter position of the {@link Preference} or
42566222008cbda61251014caf1442930a48561d25cTony Mantler         *         {@link android.support.v7.widget.RecyclerView#NO_POSITION} if not found
42666222008cbda61251014caf1442930a48561d25cTony Mantler         */
42766222008cbda61251014caf1442930a48561d25cTony Mantler        int getPreferenceAdapterPosition(Preference preference);
42866222008cbda61251014caf1442930a48561d25cTony Mantler    }
4296904f67c96a28a0e5966b4fb6d37a0ad5f136858Tony Mantler}
430