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

Commit df55584e authored by herriojr's avatar herriojr
Browse files

ANR Caused by passes over Data Set on notifyDataSetChanged()

Logic existed which took a pass over the data on the UI thread each
time notifyDataSetChange was called which can cause an ANR on a large
data set. This fix removes a lot of the unnecessary logic associated
with this and moves it to getView(). Performance still seems good on
the OnePlus for scrolling.

Change-Id: Idde74f0688fef0ba7e4b560b06d9494896a24174
Ticket: QRDL-931
parent 85ff3da0
Loading
Loading
Loading
Loading
+71 −196
Original line number Diff line number Diff line
@@ -41,6 +41,8 @@ import com.cyanogenmod.filemanager.util.FileHelper;
import com.cyanogenmod.filemanager.util.MimeTypeHelper;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;

/**
@@ -80,34 +82,13 @@ public class FileSystemObjectAdapter
        Boolean mHasSelectedBg;
    }

    /**
     * A class that holds the full data information.
     */
    private static class DataHolder {
        /**
         * @hide
         */
        public DataHolder() {
            super();
        }
        boolean mSelected;
        Drawable mDwCheck;
        Drawable mDwIcon;
        String mName;
        String mSummary;
        String mSize;
    }

    private DataHolder[] mData;
    private IconHolder mIconHolder;
    private final int mItemViewResourceId;
    private List<FileSystemObject> mSelectedItems;
    private HashSet<FileSystemObject> mSelectedItems;
    private final boolean mPickable;

    private OnSelectionChangedListener mOnSelectionChangedListener;

    private boolean mDisposed;

    //The resource of the item check
    private static final int RESOURCE_ITEM_CHECK = R.id.navigation_view_item_check;
    //The resource of the item icon
@@ -132,13 +113,10 @@ public class FileSystemObjectAdapter
            Context context, List<FileSystemObject> files,
            int itemViewResourceId, boolean pickable) {
        super(context, RESOURCE_ITEM_NAME, files);
        this.mDisposed  = false;
        this.mItemViewResourceId = itemViewResourceId;
        this.mSelectedItems = new ArrayList<FileSystemObject>();
        this.mSelectedItems = new HashSet<FileSystemObject>();
        this.mPickable = pickable;
        notifyThemeChanged(); // Reload icons

        processData();
    }

    /**
@@ -159,25 +137,11 @@ public class FileSystemObjectAdapter
        this.mIconHolder.getDrawable("ic_fso_default_drawable"); //$NON-NLS-1$
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void notifyDataSetChanged() {
        if (this.mDisposed) {
            return;
        }
        processData();
        super.notifyDataSetChanged();
    }

    /**
     * Method that dispose the elements of the adapter.
     */
    public void dispose() {
        this.mDisposed = true;
        clear();
        this.mData = null;
        if (mIconHolder != null) {
            mIconHolder.cleanup();
            mIconHolder = null;
@@ -203,52 +167,6 @@ public class FileSystemObjectAdapter
        return null;
    }

    /**
     * Method that process the data before use {@link #getView} method.
     */
    private void processData() {
        Theme theme = ThemeManager.getCurrentTheme(getContext());
        Resources res = getContext().getResources();
        int cc = getCount();

        this.mData = new DataHolder[cc];

        for (int i = 0; i < cc; i++) {
            //File system object info
            FileSystemObject fso = getItem(i);

            //Parse the last modification time and permissions
            StringBuilder sbSummary = new StringBuilder();
            if (fso instanceof ParentDirectory) {
                sbSummary.append(res.getString(R.string.parent_dir));
            } else {
                sbSummary.append(
                        FileHelper.formatFileTime(
                                getContext(), fso.getLastModifiedTime()));
                sbSummary.append("   "); //$NON-NLS-1$
                sbSummary.append(fso.toRawPermissionString());
            }

            //Build the data holder
            this.mData[i] = new FileSystemObjectAdapter.DataHolder();
            this.mData[i].mSelected = this.mSelectedItems.contains(fso);
            if (this.mData[i].mSelected) {
                this.mData[i].mDwCheck =
                        theme.getDrawable(
                                getContext(), "checkbox_selected_drawable"); //$NON-NLS-1$
            } else {
                this.mData[i].mDwCheck =
                        theme.getDrawable(
                                getContext(), "checkbox_deselected_drawable"); //$NON-NLS-1$
            }
            this.mData[i].mDwIcon = this.mIconHolder.getDrawable(
                    MimeTypeHelper.getIcon(getContext(), fso));
            this.mData[i].mName = fso.getName();
            this.mData[i].mSummary = sbSummary.toString();
            this.mData[i].mSize = FileHelper.getHumanReadableSize(fso);
        }
    }

    /**
     * {@inheritDoc}
     */
@@ -278,47 +196,60 @@ public class FileSystemObjectAdapter
            v.setTag(viewHolder);
        }

        //Retrieve data holder
        if (mData == null || this.mData[position] == null) {
            return v;
        }
        final DataHolder dataHolder = this.mData[position];

        //Retrieve the view holder
        ViewHolder viewHolder = (ViewHolder)v.getTag();
        if (this.mPickable) {
            theme.setBackgroundDrawable(getContext(), v, "background_drawable"); //$NON-NLS-1$
        }

        //Set the data
        mIconHolder.loadDrawable(viewHolder.mIvIcon, getItem(position), dataHolder.mDwIcon);
        FileSystemObject fso = getItem(position);

        viewHolder.mTvName.setText(dataHolder.mName);
        Drawable dwIcon = this.mIconHolder.getDrawable(MimeTypeHelper.getIcon(getContext(), fso));
        mIconHolder.loadDrawable(viewHolder.mIvIcon, fso, dwIcon);

        viewHolder.mTvName.setText(fso.getName());
        theme.setTextColor(getContext(), viewHolder.mTvName, "text_color"); //$NON-NLS-1$
        if (viewHolder.mTvSummary != null) {
            viewHolder.mTvSummary.setText(dataHolder.mSummary);
            StringBuilder sbSummary = new StringBuilder();
            if (fso instanceof ParentDirectory) {
                sbSummary.append(getContext().getResources().getString(R.string.parent_dir));
            } else {
                sbSummary.append(
                        FileHelper.formatFileTime(
                                getContext(), fso.getLastModifiedTime()));
                sbSummary.append("   "); //$NON-NLS-1$
                sbSummary.append(fso.toRawPermissionString());
            }
            viewHolder.mTvSummary.setText(sbSummary);
            theme.setTextColor(getContext(), viewHolder.mTvSummary, "text_color"); //$NON-NLS-1$
        }
        if (viewHolder.mTvSize != null) {
            viewHolder.mTvSize.setText(dataHolder.mSize);
            viewHolder.mTvSize.setText(FileHelper.getHumanReadableSize(fso));
            theme.setTextColor(getContext(), viewHolder.mTvSize, "text_color"); //$NON-NLS-1$
        }
        if (!this.mPickable) {
            viewHolder.mBtCheck.setVisibility(
                    TextUtils.equals(dataHolder.mName, FileHelper.PARENT_DIRECTORY) ?
                    TextUtils.equals(fso.getName(), FileHelper.PARENT_DIRECTORY) ?
                            View.INVISIBLE : View.VISIBLE);

            viewHolder.mBtCheck.setImageDrawable(dataHolder.mDwCheck);
            viewHolder.mBtCheck.setTag(Integer.valueOf(position));
            boolean selected = mSelectedItems.contains(fso);
            Drawable dwCheck;
            if (selected) {
                dwCheck = theme.getDrawable(getContext(), "checkbox_selected_drawable"); //$NON-NLS-1$
            } else {
                dwCheck = theme.getDrawable(getContext(), "checkbox_deselected_drawable"); //$NON-NLS-1$
            }
            viewHolder.mBtCheck.setImageDrawable(dwCheck);
            viewHolder.mBtCheck.setTag(position);

            if (viewHolder.mHasSelectedBg == null
                    || viewHolder.mHasSelectedBg != dataHolder.mSelected) {
                String drawableId = dataHolder.mSelected
                    || viewHolder.mHasSelectedBg != selected) {
                String drawableId = selected
                        ? "selectors_selected_drawable" //$NON-NLS-1$
                        : "selectors_deselected_drawable"; //$NON-NLS-1$

                theme.setBackgroundDrawable(getContext(), v, drawableId);
                viewHolder.mHasSelectedBg = dataHolder.mSelected;
                viewHolder.mHasSelectedBg = selected;
            }
        }

@@ -333,7 +264,7 @@ public class FileSystemObjectAdapter
     * @return boolean If the item of the passed position is selected
     */
    public boolean isSelected(int position) {
        return this.mData[position].mSelected;
        return mSelectedItems.contains(getItem(position));
    }

    /**
@@ -352,56 +283,23 @@ public class FileSystemObjectAdapter
     * @param fso The file system object to select
     */
    private void toggleSelection(View v, FileSystemObject fso) {
        if (this.mData != null) {
        Theme theme = ThemeManager.getCurrentTheme(getContext());
            int cc = this.mData.length;
            for (int i = 0; i < cc; i++) {
                DataHolder data = this.mData[i];
                if (data.mName.compareTo(fso.getName()) == 0) {
                    //Select/Deselect the item
                    data.mSelected = !data.mSelected;
                    if (v != null) {
                        ((View)v.getParent()).setSelected(data.mSelected);
                    }
                    if (data.mSelected) {
                        data.mDwCheck =
                                theme.getDrawable(
                                        getContext(), "checkbox_selected_drawable"); //$NON-NLS-1$
                    } else {
                        data.mDwCheck =
                                theme.getDrawable(
                                        getContext(),
                                            "checkbox_deselected_drawable"); //$NON-NLS-1$
                    }

                    //Add or remove from the global selected items
                    final List<FileSystemObject> selectedItems =
                            FileSystemObjectAdapter.this.mSelectedItems;
                    if (data.mSelected) {
                        if (!selectedItems.contains(fso)) {
                            selectedItems.add(fso);
                        }
                    } else {
                        if (selectedItems.contains(fso)) {
                            selectedItems.remove(fso);
        boolean selected = !mSelectedItems.remove(fso);
        if (selected) {
            mSelectedItems.add(fso);
        }
        if (v != null) {
            ((View)v.getParent()).setSelected(selected);
        }

        //Communicate event
        if (this.mOnSelectionChangedListener != null) {
                        List<FileSystemObject> selection =
                                new ArrayList<FileSystemObject>(selectedItems);
                        this.mOnSelectionChangedListener.onSelectionChanged(selection);
            this.mOnSelectionChangedListener.onSelectionChanged(
                    new ArrayList<FileSystemObject>(mSelectedItems));
        }

                    // The internal structure was update, only super adapter need to be notified
                    super.notifyDataSetChanged();

                    //Found
                    return;
                }
            }
        }
        notifyDataSetChanged();
    }

    /**
@@ -432,52 +330,27 @@ public class FileSystemObjectAdapter
     * @param select Indicates if select (true) or deselect (false) all items.
     */
    private void doSelectDeselectAllVisibleItems(boolean select) {
        if (this.mData != null && this.mData.length > 0) {
            Theme theme = ThemeManager.getCurrentTheme(getContext());
            int cc = this.mData.length;
        int cc = getCount();
        for (int i = 0; i < cc; i++) {
                DataHolder data = this.mData[i];
                if (data.mName.compareTo(FileHelper.PARENT_DIRECTORY) == 0) {
            FileSystemObject fso = getItem(i);
            if (fso.getName().compareTo(FileHelper.PARENT_DIRECTORY) == 0) {
                // No select the parent directory
                continue;
            }
                data.mSelected = select;
                if (data.mSelected) {
                    data.mDwCheck =
                            theme.getDrawable(
                                    getContext(), "checkbox_selected_drawable"); //$NON-NLS-1$
                } else {
                    data.mDwCheck =
                            theme.getDrawable(
                                    getContext(), "checkbox_deselected_drawable"); //$NON-NLS-1$
                }

                //Add or remove from the global selected items
                FileSystemObject fso = getItem(i);
                final List<FileSystemObject> selectedItems =
                        FileSystemObjectAdapter.this.mSelectedItems;
                if (data.mSelected) {
                    if (!selectedItems.contains(fso)) {
                        selectedItems.add(fso);
                    }
            if (select) {
                mSelectedItems.add(fso);
            } else {
                    if (selectedItems.contains(fso)) {
                        selectedItems.remove(fso);
                    }
                mSelectedItems.remove(fso);
            }
        }

        //Communicate event
        if (this.mOnSelectionChangedListener != null) {
                List<FileSystemObject> selection =
                        new ArrayList<FileSystemObject>(
                                FileSystemObjectAdapter.this.mSelectedItems);
                this.mOnSelectionChangedListener.onSelectionChanged(selection);
            this.mOnSelectionChangedListener.onSelectionChanged(
                    new ArrayList<FileSystemObject>(mSelectedItems));
        }

            // The internal structure was update, only super adapter need to be notified
            super.notifyDataSetChanged();
        }
        notifyDataSetChanged();
    }

    /**
@@ -495,7 +368,9 @@ public class FileSystemObjectAdapter
     * @param selectedItems The selected items
     */
    public void setSelectedItems(List<FileSystemObject> selectedItems) {
        this.mSelectedItems = selectedItems;
        mSelectedItems.clear();
        mSelectedItems.addAll(selectedItems);
        notifyDataSetChanged();
    }

    /**