Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a98129b9 authored by Amith Yamasani's avatar Amith Yamasani
Browse files

Fix preference caching to increase recycling of preference views.

Add extra safety measures - no recycling of derived preferences as
they may override onCreateView or getView
parent 7d7ab5ae
Loading
Loading
Loading
Loading
+13 −14
Original line number Diff line number Diff line
@@ -188,17 +188,7 @@ public class Preference implements Comparable<Preference>, OnDependencyChangeLis
        mContext = context;

        TypedArray a = context.obtainStyledAttributes(attrs,
                com.android.internal.R.styleable.Preference);
        if (a.hasValue(com.android.internal.R.styleable.Preference_layout) ||
                a.hasValue(com.android.internal.R.styleable.Preference_widgetLayout)) {
            // This preference has a custom layout defined (not one taken from
            // the default style)
            mHasSpecifiedLayout = true;
        }
        a.recycle();
        
        a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Preference,
                defStyle, 0);
                com.android.internal.R.styleable.Preference, defStyle, 0);
        for (int i = a.getIndexCount(); i >= 0; i--) {
            int attr = a.getIndex(i); 
            switch (attr) {
@@ -252,6 +242,11 @@ public class Preference implements Comparable<Preference>, OnDependencyChangeLis
            }
        }
        a.recycle();

        if (!getClass().getName().startsWith("android.preference")) {
            // For subclasses not in this package, assume the worst and don't cache views
            mHasSpecifiedLayout = true;
        }
    }
    
    /**
@@ -332,8 +327,8 @@ public class Preference implements Comparable<Preference>, OnDependencyChangeLis
     * @see #setWidgetLayoutResource(int)
     */
    public void setLayoutResource(int layoutResId) {
        
        if (!mHasSpecifiedLayout) {
        if (layoutResId != mLayoutResId) {
            // Layout changed
            mHasSpecifiedLayout = true;
        }

@@ -360,6 +355,10 @@ public class Preference implements Comparable<Preference>, OnDependencyChangeLis
     * @see #setLayoutResource(int)
     */
    public void setWidgetLayoutResource(int widgetLayoutResId) {
        if (widgetLayoutResId != mWidgetLayoutResId) {
            // Layout changed
            mHasSpecifiedLayout = true;
        }
        mWidgetLayoutResId = widgetLayoutResId;
    }

+59 −24
Original line number Diff line number Diff line
@@ -69,7 +69,9 @@ class PreferenceGroupAdapter extends BaseAdapter implements OnPreferenceChangeIn
     * count once--when the adapter is being set). We will not recycle views for
     * Preference subclasses seen after the count has been returned.
     */
    private List<String> mPreferenceClassNames;
    private ArrayList<PreferenceLayout> mPreferenceLayouts;

    private PreferenceLayout mTempPreferenceLayout = new PreferenceLayout();

    /**
     * Blocks the mPreferenceClassNames from being changed anymore.
@@ -86,13 +88,36 @@ class PreferenceGroupAdapter extends BaseAdapter implements OnPreferenceChangeIn
        }
    };

    private static class PreferenceLayout implements Comparable<PreferenceLayout> {
        private int resId;
        private int widgetResId;
        private String name;

        public int compareTo(PreferenceLayout other) {
            int compareNames = name.compareTo(other.name);
            if (compareNames == 0) {
                if (resId == other.resId) {
                    if (widgetResId == other.widgetResId) {
                        return 0;
                    } else {
                        return widgetResId - other.widgetResId;
                    }
                } else {
                    return resId - other.resId;
                }
            } else {
                return compareNames;
            }
        }
    }

    public PreferenceGroupAdapter(PreferenceGroup preferenceGroup) {
        mPreferenceGroup = preferenceGroup;
        // If this group gets or loses any children, let us know
        mPreferenceGroup.setOnPreferenceChangeInternalListener(this);

        mPreferenceList = new ArrayList<Preference>();
        mPreferenceClassNames = new ArrayList<String>();
        mPreferenceLayouts = new ArrayList<PreferenceLayout>();

        syncMyPreferences();
    }
@@ -128,7 +153,7 @@ class PreferenceGroupAdapter extends BaseAdapter implements OnPreferenceChangeIn
            
            preferences.add(preference);
            
            if (!mHasReturnedViewTypeCount) {
            if (!mHasReturnedViewTypeCount && !preference.hasSpecifiedLayout()) {
                addPreferenceClassName(preference);
            }
            
@@ -143,15 +168,28 @@ class PreferenceGroupAdapter extends BaseAdapter implements OnPreferenceChangeIn
        }
    }

    /**
     * Creates a string that includes the preference name, layout id and widget layout id.
     * If a particular preference type uses 2 different resources, they will be treated as
     * different view types.
     */
    private PreferenceLayout createPreferenceLayout(Preference preference, PreferenceLayout in) {
        PreferenceLayout pl = in != null? in : new PreferenceLayout();
        pl.name = preference.getClass().getName();
        pl.resId = preference.getLayoutResource();
        pl.widgetResId = preference.getWidgetLayoutResource();
        return pl;
    }

    private void addPreferenceClassName(Preference preference) {
        final String name = preference.getClass().getName();
        int insertPos = Collections.binarySearch(mPreferenceClassNames, name);
        final PreferenceLayout pl = createPreferenceLayout(preference, null);
        int insertPos = Collections.binarySearch(mPreferenceLayouts, pl);

        // Only insert if it doesn't exist (when it is negative).
        if (insertPos < 0) {
            // Convert to insert index
            insertPos = insertPos * -1 - 1;
            mPreferenceClassNames.add(insertPos, name);
            mPreferenceLayouts.add(insertPos, pl);
        }
    }
    
@@ -171,18 +209,14 @@ class PreferenceGroupAdapter extends BaseAdapter implements OnPreferenceChangeIn

    public View getView(int position, View convertView, ViewGroup parent) {
        final Preference preference = this.getItem(position);
        // Build a PreferenceLayout to compare with known ones that are cacheable.
        mTempPreferenceLayout = createPreferenceLayout(preference, mTempPreferenceLayout);

        if (preference.hasSpecifiedLayout()) {
            // If the preference had specified a layout (as opposed to the
            // default), don't use convert views.
            convertView = null;
        } else {
            // TODO: better way of doing this
            final String name = preference.getClass().getName();
            if (Collections.binarySearch(mPreferenceClassNames, name) < 0) {
        // If it's not one of the cached ones, set the convertView to null so that 
        // the layout gets re-created by the Preference.
        if (Collections.binarySearch(mPreferenceLayouts, mTempPreferenceLayout) < 0) {
            convertView = null;
        }
        }

        return preference.getView(convertView, parent);
    }
@@ -225,8 +259,9 @@ class PreferenceGroupAdapter extends BaseAdapter implements OnPreferenceChangeIn
            return IGNORE_ITEM_VIEW_TYPE;
        }

        final String name = preference.getClass().getName();
        int viewType = Collections.binarySearch(mPreferenceClassNames, name);
        mTempPreferenceLayout = createPreferenceLayout(preference, mTempPreferenceLayout);

        int viewType = Collections.binarySearch(mPreferenceLayouts, mTempPreferenceLayout);
        if (viewType < 0) {
            // This is a class that was seen after we returned the count, so
            // don't recycle it.
@@ -242,7 +277,7 @@ class PreferenceGroupAdapter extends BaseAdapter implements OnPreferenceChangeIn
            mHasReturnedViewTypeCount = true;
        }
        
        return Math.max(1, mPreferenceClassNames.size());
        return Math.max(1, mPreferenceLayouts.size());
    }

}