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

Commit 21560d80 authored by Roman Birg's avatar Roman Birg
Browse files

CMFM: improved search performance



Search was usable with a small number of results, but when the result
list grows to something > 1000, it can get really laggy.

A few improvemnts:
- The adapter was chunking items and adding them in bulk to the adapter,
  but it was doing all the work in the main thread. Now it will process
  one item at a time, doing all the rough work in an AsyncTask, and
  passing the final objects to the adapter.

- We don't need to do any chunking of items to add them, but sorting
  the list as soon as they are added is expensive too, so re-use the
  streaming mechanism that was used to add items in chunks, and just
  sort the list during that time.

- Cache common variables to not look them up on every result

- Use a List instead of generating a new DataHolder array _every time_ a
  new object was added. It would end up GCing thousands of items
  every time a new result was added.

- Make the adapter be as simple as possible and just display DataHolder
  objects so it doesn't have to do any processing.

Ref: QRDL-950

Change-Id: I17dd606246556cdf2701bbf2b06933f138588d74
Signed-off-by: default avatarRoman Birg <roman@cyngn.com>
parent bd79d582
Loading
Loading
Loading
Loading
+14 −7
Original line number Diff line number Diff line
@@ -478,6 +478,8 @@ public class NavigationActivity extends Activity

    private boolean mNeedsEasyMode = false;

    private boolean mDisplayingSearchResults;

    /**
     * @hide
     */
@@ -630,10 +632,6 @@ public class NavigationActivity extends Activity
    protected void onStart() {
        super.onStart();

        if (mSearchView.getVisibility() == View.VISIBLE) {
            closeSearch();
        }

        // Check restrictions
        if (!FileManagerApplication.checkRestrictSecondaryUsersAccess(this, mChRooted)) {
            return;
@@ -649,7 +647,13 @@ public class NavigationActivity extends Activity
                onRequestBookmarksRefresh();
                removeUnmountedHistory();
                removeUnmountedSelection();
            }

            if (mDisplayingSearchResults) {
                mDisplayingSearchResults = false;
                closeSearch();
            } else {
                getCurrentNavigationView().refresh(true);
                Intent intent = new Intent();
                intent.putExtra(EXTRA_ADD_TO_HISTORY, false);
                initNavigation(NavigationActivity.this.mCurrentNavigationView, false, intent);
@@ -1577,15 +1581,17 @@ public class NavigationActivity extends Activity
     * @hide
     */
    void initNavigation(final int viewId, final boolean restore, final Intent intent) {
        if (mDisplayingSearchResults || restore) {
            return;
        }

        final NavigationView navigationView = getNavigationView(viewId);
        this.mHandler.post(new Runnable() {
            @Override
            public void run() {
                //Is necessary navigate?
                if (!restore) {
                applyInitialDir(navigationView, intent);
            }
            }
        });
    }

@@ -1935,6 +1941,7 @@ public class NavigationActivity extends Activity
                                //Goto to new directory
                                getCurrentNavigationView().open(fso, searchInfo);
                                performHideEasyMode();
                                mDisplayingSearchResults = true;
                            }
                        }
                    } else if (resultCode == RESULT_CANCELED) {
+209 −31
Original line number Diff line number Diff line
@@ -29,7 +29,9 @@ import android.content.IntentFilter;
import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Parcelable;
import android.preference.PreferenceActivity;
@@ -37,7 +39,6 @@ import android.provider.SearchRecentSuggestions;
import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
@@ -69,9 +70,11 @@ import com.cyanogenmod.filemanager.model.SearchResult;
import com.cyanogenmod.filemanager.model.Symlink;
import com.cyanogenmod.filemanager.parcelables.SearchInfoParcelable;
import com.cyanogenmod.filemanager.preferences.AccessMode;
import com.cyanogenmod.filemanager.preferences.DisplayRestrictions;
import com.cyanogenmod.filemanager.preferences.FileManagerSettings;
import com.cyanogenmod.filemanager.preferences.Preferences;
import com.cyanogenmod.filemanager.providers.RecentSearchesContentProvider;
import com.cyanogenmod.filemanager.ui.IconHolder;
import com.cyanogenmod.filemanager.ui.ThemeManager;
import com.cyanogenmod.filemanager.ui.ThemeManager.Theme;
import com.cyanogenmod.filemanager.ui.dialogs.ActionsDialog;
@@ -91,11 +94,15 @@ import com.cyanogenmod.filemanager.util.MimeTypeHelper.MimeTypeCategory;
import com.cyanogenmod.filemanager.util.SearchHelper;
import com.cyanogenmod.filemanager.util.StorageHelper;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.Serializable;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

/**
 * An activity for search files and folders.
@@ -181,7 +188,7 @@ public class SearchActivity extends Activity
            try {
                // Response if the item can be removed
                SearchResultAdapter adapter = (SearchResultAdapter)parent.getAdapter();
                SearchResult result = adapter.getItem(position);
                SearchResult result = adapter.getItem(position).getSearchResult();
                if (result != null && result.getFso() != null) {
                    if (result.getFso() instanceof ParentDirectory) {
                        // This is not possible ...
@@ -202,7 +209,7 @@ public class SearchActivity extends Activity
            try {
                // Response if the item can be removed
                SearchResultAdapter adapter = (SearchResultAdapter)parent.getAdapter();
                SearchResult result = adapter.getItem(position);
                SearchResult result = adapter.getItem(position).getSearchResult();
                if (result != null && result.getFso() != null) {
                    DeleteActionPolicy.removeFileSystemObject(
                            SearchActivity.this,
@@ -377,6 +384,12 @@ public class SearchActivity extends Activity
    private SearchResultAdapter mAdapter;
    private ProgressBar mStreamingSearchProgress;
    private boolean mSearchInProgress;
    private String mSearchFoundString;
    private boolean mHighlightTerms;
    private boolean mShowRelevanceWidget;
    private int mHighlightColor;
    private ArrayList<DataHolder> mAdapterList = new ArrayList<>();
    private IconHolder mIconHolder;

    /**
     * {@inheritDoc}
@@ -390,6 +403,13 @@ public class SearchActivity extends Activity
        // Check if app is running in chrooted mode
        this.mChRooted = FileManagerApplication.getAccessMode().compareTo(AccessMode.SAFE) == 0;

        final boolean displayThumbs = Preferences.getSharedPreferences().getBoolean(
                FileManagerSettings.SETTINGS_DISPLAY_THUMBS.getId(),
                ((Boolean)FileManagerSettings.SETTINGS_DISPLAY_THUMBS.getDefaultValue()).booleanValue());
        mIconHolder = new IconHolder(this, displayThumbs);
        mIconHolder.getDrawable("ic_fso_folder_drawable"); //$NON-NLS-1$
        mIconHolder.getDrawable("ic_fso_default_drawable"); //$NON-NLS-1$

        // Register the broadcast receiver
        IntentFilter filter = new IntentFilter();
        filter.addAction(FileManagerSettings.INTENT_SETTING_CHANGED);
@@ -431,6 +451,10 @@ public class SearchActivity extends Activity
            }
        }

        mSearchFoundString = getString(R.string.search_found_items_in_directory);
        //$NON-NLS-1$
        mHighlightColor = theme.getColor(this, "search_highlight_color");

        //Save state
        super.onCreate(state);
    }
@@ -720,6 +744,7 @@ public class SearchActivity extends Activity
                                back(true, null, false);
                            }
                       });
        dialog.setCancelable(false);
        DialogHelper.delegateDialogShow(this, dialog);
    }

@@ -733,6 +758,15 @@ public class SearchActivity extends Activity
     */
    void doSearch(
            final boolean voiceQuery, final Query query, final String searchDirectory) {
        // Load settings
        this.mHighlightTerms = Preferences.getSharedPreferences().getBoolean(
                FileManagerSettings.SETTINGS_HIGHLIGHT_TERMS.getId(),
                ((Boolean)FileManagerSettings.SETTINGS_HIGHLIGHT_TERMS.
                        getDefaultValue()).booleanValue());
        this.mShowRelevanceWidget = Preferences.getSharedPreferences().getBoolean(
                FileManagerSettings.SETTINGS_SHOW_RELEVANCE_WIDGET.getId(),
                ((Boolean)FileManagerSettings.SETTINGS_SHOW_RELEVANCE_WIDGET.
                        getDefaultValue()).booleanValue());

        // Recovers the user preferences about save suggestions
        boolean saveSuggestions = Preferences.getSharedPreferences().getBoolean(
@@ -761,7 +795,8 @@ public class SearchActivity extends Activity
        this.mResultList = new ArrayList<FileSystemObject>();
        mAdapter =
                new SearchResultAdapter(this,
                        new ArrayList<SearchResult>(), R.layout.search_item, this.mQuery);
                        mAdapterList, R.layout.search_item,
                        this.mQuery, mIconHolder);
        this.mSearchListView.setAdapter(mAdapter);

        //Set terms
@@ -811,25 +846,84 @@ public class SearchActivity extends Activity
        });
    }

    /**
     * Ensures the search result meets user preferences and passes it to the adapter for display
     *
     * @param result FileSystemObject that matches the search result criteria
     */
    private void showSearchResult(FileSystemObject result) {
    private static class ProcessSearchResult extends AsyncTask<FileSystemObject, Void, Boolean> {

        private WeakReference<SearchActivity> mActivity;

        private SearchResult mResult;
        private DataHolder mHolder;

        public ProcessSearchResult(SearchActivity parent) {
            super();
            mActivity = new WeakReference<SearchActivity>(parent);
        }

        @Override
        protected Boolean doInBackground(FileSystemObject... params) {
            SearchActivity activity = mActivity.get();
            if (activity == null) {
                return false;
            }

            FileSystemObject result = params[0];
            // check against user's display preferences
        if ( !FileHelper.compliesWithDisplayPreferences(result, null, mChRooted) ) {
            return;
            if ( !FileHelper.compliesWithDisplayPreferences(result, null, activity.mChRooted) ) {
                return false;
            }

            // resolve sym links
        FileHelper.resolveSymlink(this, result);
            FileHelper.resolveSymlink(activity, result);

            // convert to search result
        SearchResult searchResult = SearchHelper.convertToResult(result, mQuery);
            mResult = SearchHelper.convertToResult(result, activity.mQuery);

            mHolder = activity.generateDataHolder(mResult);

            return mHolder != null && mResult != null;
        }

        @Override
        protected void onPostExecute(Boolean sucess) {
            SearchActivity activity = mActivity.get();
            if (activity == null) {
                return;
            }
            if (sucess) {
                // add to adapter
        mAdapter.addNewItem(searchResult);
                activity.mAdapter.addNewItem(mHolder);
            }
        }
    }

    /**
     * Ensures the search result meets user preferences and passes it to the adapter for display
     *
     * @param result FileSystemObject that matches the search result criteria
     */
    private void showSearchResult(FileSystemObject result) {
        new ProcessSearchResult(this).execute(result);
    }

    private DataHolder generateDataHolder(SearchResult result) {
        //Build the data holder
        final FileSystemObject fso = result.getFso();
        final Drawable icon = mIconHolder.getDrawable(
                MimeTypeHelper.getIcon(this, fso));
        final CharSequence highlightedName;
        if (mHighlightTerms) {
            highlightedName = SearchHelper.getHighlightedName(result, mQuery.getQueries(),
                    mHighlightColor);
        } else {
            highlightedName = SearchHelper.getNonHighlightedName(result);
        }
        final String parent = new File(result.getFso().getFullPath()).getParent();
        Float relevance = mShowRelevanceWidget ? ((float)result.getRelevance() * 100)
                / SearchResult.MAX_RELEVANCE : null;
        final MimeTypeHelper.MimeTypeCategory category = MimeTypeHelper.getCategory(this, fso);

        SearchActivity.DataHolder holder = new SearchActivity.DataHolder(result, icon,
                highlightedName, parent, relevance, category);
        return holder;
    }

    /**
@@ -841,6 +935,12 @@ public class SearchActivity extends Activity
            public void run() {
                //Toggle results
                List<SearchResult> list = SearchActivity.this.mRestoreState.getSearchResultList();
                mAdapterList.clear();
                for (SearchResult searchResult : list) {
                    mAdapterList.add(generateDataHolder(searchResult));
                }


                String directory = SearchActivity.this.mRestoreState.getSearchDirectory();
                SearchActivity.this.toggleResults(list.size() > 0, true);
                setFoundItems(list.size(), directory);
@@ -869,9 +969,9 @@ public class SearchActivity extends Activity
                    SearchResultAdapter adapter =
                            new SearchResultAdapter(
                                                SearchActivity.this.mSearchListView.getContext(),
                                                list,
                                                mAdapterList,
                                                R.layout.search_item,
                                                query);
                                                query, mIconHolder);
                    SearchActivity.this.mSearchListView.setAdapter(adapter);
                    SearchActivity.this.mSearchListView.setSelection(0);

@@ -960,11 +1060,8 @@ public class SearchActivity extends Activity
                            getResources().
                                getQuantityString(
                                    R.plurals.search_found_items, items, Integer.valueOf(items));
                    SearchActivity.this.mSearchFoundItems.setText(
                                            getString(
                                                R.string.search_found_items_in_directory,
                                                foundItems,
                                                directory));
                    SearchActivity.this.mSearchFoundItems.setText(String.format(mSearchFoundString,
                            foundItems, directory));
                }
            });
        }
@@ -1010,7 +1107,8 @@ public class SearchActivity extends Activity
        if (mSearchInProgress) mExecutable.end();

        try {
            SearchResult result = ((SearchResultAdapter)parent.getAdapter()).getItem(position);
            SearchResult result = ((SearchResultAdapter)parent.getAdapter()).getItem(position)
                    .getSearchResult();
            FileSystemObject fso = result.getFso();
            if (fso instanceof Directory) {
                back(false, fso, false);
@@ -1097,7 +1195,7 @@ public class SearchActivity extends Activity

        // Get the adapter, the search result and the fso
        SearchResultAdapter adapter = ((SearchResultAdapter)parent.getAdapter());
        SearchResult searchResult = adapter.getItem(position);
        SearchResult searchResult = adapter.getItem(position).getSearchResult();
        FileSystemObject fso = searchResult.getFso();

        // Open the actions menu
@@ -1147,8 +1245,7 @@ public class SearchActivity extends Activity
        if (adapter != null) {
            int pos = adapter.getPosition(fso);
            if (pos != -1) {
                SearchResult sr = adapter.getItem(pos);
                adapter.remove(sr);
                adapter.remove(adapter.getItem(pos));
            }

            // Toggle resultset?
@@ -1171,7 +1268,7 @@ public class SearchActivity extends Activity
                FileSystemObject fso = (FileSystemObject)o;
                int pos = adapter.getPosition(fso);
                if (pos >= 0) {
                    SearchResult sr = adapter.getItem(pos);
                    SearchResult sr = adapter.getItem(pos).getSearchResult();
                    sr.setFso(fso);
                }
            } else if (o == null) {
@@ -1302,7 +1399,6 @@ public class SearchActivity extends Activity
     * Method that navigate to the file system used the intent (NavigationActivity)
     *
     * @param fso The file system object to navigate to
     * @param intent The intent used to navigate to
     * @return boolean If the action implies finish this activity
     */
    boolean navigateTo(FileSystemObject fso) {
@@ -1379,10 +1475,46 @@ public class SearchActivity extends Activity

    @Override
    public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
        String category = MimeTypeHelper.MimeTypeCategory.names()[i];
        final String category = MimeTypeHelper.MimeTypeCategory.names()[i];
        SearchResultAdapter adapter = ((SearchResultAdapter) this.mSearchListView.getAdapter());
        if (adapter != null) {
            adapter.setMimeFilter(category);
            new AsyncTask<String, Void, Void>() {
                List<DataHolder> mNewList = new ArrayList<>();
                @Override
                protected Void doInBackground(String... params) {
                    // Are we in ChRooted environment?
                    boolean chRooted =
                            FileManagerApplication.getAccessMode().compareTo(AccessMode.SAFE) == 0;

                    // Create display restrictions
                    Map<DisplayRestrictions, Object> restrictions =
                            new HashMap<DisplayRestrictions, Object>();
                    restrictions.put(
                            DisplayRestrictions.MIME_TYPE_RESTRICTION, MimeTypeHelper.ALL_MIME_TYPES);

                    List<SearchResult> newResults = SearchHelper.convertToResults(
                            FileHelper.applyUserPreferences(
                                    mAdapter.getFiles(), restrictions, true, chRooted), new Query().fillSlots(mQuery.getQueries()));

                    for (SearchResult result : newResults) {
                        // Only show results that are within our category, or all if no filter is set
                        if (TextUtils.equals(category, MimeTypeHelper.MimeTypeCategory.NONE.name()) ||
                                MimeTypeHelper.getCategory(SearchActivity.this, result.getFso()) ==
                                        MimeTypeHelper.MimeTypeCategory.valueOf(category)) {
                            mNewList.add(generateDataHolder(result));
                        }
                    }
                    return null;
                }

                @Override
                protected void onPostExecute(Void aVoid) {
                    super.onPostExecute(aVoid);
                    mAdapterList.clear();
                    mAdapterList.addAll(mNewList);
                    mAdapter.notifyDataSetChanged();
                }
            };
        }
    }

@@ -1390,5 +1522,51 @@ public class SearchActivity extends Activity
    public void onNothingSelected(AdapterView<?> adapterView) {
        //ignore
    }

    /**
     * A class that holds the full data information.
     */
    public static class DataHolder {
        private SearchResult mSearchResult;
        Drawable mDwIcon;
        CharSequence mName;
        String mParentDir;
        Float mRelevance;
        MimeTypeCategory mMimeTypeCategory;

        public DataHolder(SearchResult result, Drawable icon, CharSequence name, String parentDir,
                          Float revelence, MimeTypeCategory category) {
            mSearchResult = result;
            mDwIcon = icon;
            mName = name;
            mParentDir = parentDir;
            mRelevance = revelence;
            mMimeTypeCategory = category;
        }

        public SearchResult getSearchResult() {
            return mSearchResult;
        }

        public Drawable getDwIcon() {
            return mDwIcon;
        }

        public CharSequence getName() {
            return mName;
        }

        public String getParentDir() {
            return mParentDir;
        }

        public Float getRelevance() {
            return mRelevance;
        }

        public MimeTypeCategory getMimeTypeCategory() {
            return mMimeTypeCategory;
        }
    }
}
+37 −159

File changed.

Preview size limit exceeded, changes collapsed.