Loading src/com/android/documentsui/DirectoryLoader.java +5 −7 Original line number Diff line number Diff line Loading @@ -56,7 +56,6 @@ import java.util.concurrent.Executor; public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { private static final String TAG = "DirectoryLoader"; private static final String[] SEARCH_REJECT_MIMES = new String[] { Document.MIME_TYPE_DIR }; private static final String[] PHOTO_PICKING_ACCEPT_MIMES = new String[] {Document.MIME_TYPE_DIR, MimeTypes.IMAGE_MIME}; Loading Loading @@ -178,17 +177,16 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { } cursor.registerContentObserver(mObserver); // Filter hidden files. cursor = new FilteringCursorWrapper(cursor, mState.showHiddenFiles); FilteringCursorWrapper filteringCursor = new FilteringCursorWrapper(cursor); filteringCursor.filterHiddenFiles(mState.showHiddenFiles); if (mSearchMode && !mFeatures.isFoldersInSearchResultsEnabled()) { // There is no findDocumentPath API. Enable filtering on folders in search mode. cursor = new FilteringCursorWrapper(cursor, null, SEARCH_REJECT_MIMES); filteringCursor.filterMimes(/* acceptMimes= */ null, SEARCH_REJECT_MIMES); } if (mPhotoPicking) { cursor = new FilteringCursorWrapper(cursor, PHOTO_PICKING_ACCEPT_MIMES, null); filteringCursor.filterMimes(PHOTO_PICKING_ACCEPT_MIMES, /* rejectMimes= */ null); } cursor = filteringCursor; // TODO: When API tweaks have landed, use ContentResolver.EXTRA_HONORED_ARGS // instead of checking directly for ContentResolver.QUERY_ARG_SORT_COLUMNS (won't work) Loading src/com/android/documentsui/MultiRootDocumentsLoader.java +7 −6 Original line number Diff line number Diff line Loading @@ -181,17 +181,18 @@ public abstract class MultiRootDocumentsLoader extends AsyncTaskLoader<Directory continue; } // Filter hidden files. cursor = new FilteringCursorWrapper(cursor, mState.showHiddenFiles); final FilteringCursorWrapper filtered = new FilteringCursorWrapper( cursor, mState.acceptMimes, getRejectMimes(), rejectBefore) { final FilteringCursorWrapper filteredCursor = new FilteringCursorWrapper(cursor) { @Override public void close() { // Ignored, since we manage cursor lifecycle internally } }; cursors.add(filtered); filteredCursor.filterHiddenFiles(mState.showHiddenFiles); filteredCursor.filterMimes(mState.acceptMimes, getRejectMimes()); filteredCursor.filterLastModified(rejectBefore); cursors.add(filteredCursor); } } catch (InterruptedException e) { Loading src/com/android/documentsui/base/FilteringCursorWrapper.java +64 −47 Original line number Diff line number Diff line Loading @@ -29,68 +29,62 @@ import android.provider.DocumentsContract.Document; import android.util.Log; /** * Cursor wrapper that filters MIME types not matching given list. * Cursor wrapper that filters cursor results by given conditions. */ public class FilteringCursorWrapper extends AbstractCursor { private final Cursor mCursor; private final int[] mPosition; private int[] mPositions; private int mCount; public FilteringCursorWrapper(Cursor cursor, String[] acceptMimes) { this(cursor, acceptMimes, null, Long.MIN_VALUE); public FilteringCursorWrapper(Cursor cursor) { mCursor = cursor; mCount = cursor.getCount(); mPositions = new int[mCount]; for (int i = 0; i < mCount; i++) { mPositions[i] = i; } public FilteringCursorWrapper(Cursor cursor, String[] acceptMimes, String[] rejectMimes) { this(cursor, acceptMimes, rejectMimes, Long.MIN_VALUE); } public FilteringCursorWrapper( Cursor cursor, String[] acceptMimes, String[] rejectMimes, long rejectBefore) { mCursor = cursor; final int count = cursor.getCount(); mPosition = new int[count]; cursor.moveToPosition(-1); while (cursor.moveToNext() && mCount < count) { /** * Filters cursor according to mimes. If both lists are empty, all mimes will be rejected. * * @param acceptMimes allowed list of mimes * @param rejectMimes blocked list of mimes */ public void filterMimes(String[] acceptMimes, String[] rejectMimes) { filterByCondition((cursor) -> { final String mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE); final long lastModified = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED); if (rejectMimes != null && MimeTypes.mimeMatches(rejectMimes, mimeType)) { continue; } if (lastModified < rejectBefore) { continue; } if (MimeTypes.mimeMatches(acceptMimes, mimeType)) { mPosition[mCount++] = cursor.getPosition(); return false; } return MimeTypes.mimeMatches(acceptMimes, mimeType); }); } if (DEBUG && mCount != cursor.getCount()) { Log.d(TAG, "Before filtering " + cursor.getCount() + ", after " + mCount); } /** Filters cursor according to last modified time, and reject earlier than given timestamp. */ public void filterLastModified(long rejectBeforeTimestamp) { filterByCondition((cursor) -> { final long lastModified = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED); return lastModified >= rejectBeforeTimestamp; }); } public FilteringCursorWrapper(Cursor cursor, boolean showHiddenFiles) { mCursor = cursor; final int count = cursor.getCount(); mPosition = new int[count]; cursor.moveToPosition(-1); while (cursor.moveToNext() && mCount < count) { final String documentId = getCursorString(cursor, Document.COLUMN_DOCUMENT_ID); if (!showHiddenFiles && documentId != null && (documentId.startsWith(".") || documentId.contains("/."))) { continue; } mPosition[mCount++] = cursor.getPosition(); /** Filter hidden files based on preference. */ public void filterHiddenFiles(boolean showHiddenFiles) { if (showHiddenFiles) { return; } if (DEBUG && mCount != cursor.getCount()) { Log.d(TAG, "Before filtering " + cursor.getCount() + ", after " + mCount); } filterByCondition((cursor) -> { // Judge by name and documentId separately because for some providers // e.g. DownloadProvider, documentId may not contain file name. final String name = getCursorString(cursor, Document.COLUMN_DISPLAY_NAME); final String documentId = getCursorString(cursor, Document.COLUMN_DOCUMENT_ID); boolean documentIdHidden = documentId != null && documentId.contains("/."); boolean fileNameHidden = name != null && name.startsWith("."); return !(documentIdHidden || fileNameHidden); }); } @Override Loading @@ -106,7 +100,7 @@ public class FilteringCursorWrapper extends AbstractCursor { @Override public boolean onMove(int oldPosition, int newPosition) { return mCursor.moveToPosition(mPosition[newPosition]); return mCursor.moveToPosition(mPositions[newPosition]); } @Override Loading Loading @@ -168,4 +162,27 @@ public class FilteringCursorWrapper extends AbstractCursor { public void unregisterContentObserver(ContentObserver observer) { mCursor.unregisterContentObserver(observer); } private interface FilteringCondition { boolean accept(Cursor cursor); } private void filterByCondition(FilteringCondition condition) { final int oldCount = this.getCount(); int[] newPositions = new int[oldCount]; int newCount = 0; this.moveToPosition(-1); while (this.moveToNext() && newCount < oldCount) { if (condition.accept(mCursor)) { newPositions[newCount++] = mPositions[this.getPosition()]; } } if (DEBUG && newCount != this.getCount()) { Log.d(TAG, "Before filtering " + oldCount + ", after " + newCount); } mCount = newCount; mPositions = newPositions; } } Loading
src/com/android/documentsui/DirectoryLoader.java +5 −7 Original line number Diff line number Diff line Loading @@ -56,7 +56,6 @@ import java.util.concurrent.Executor; public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { private static final String TAG = "DirectoryLoader"; private static final String[] SEARCH_REJECT_MIMES = new String[] { Document.MIME_TYPE_DIR }; private static final String[] PHOTO_PICKING_ACCEPT_MIMES = new String[] {Document.MIME_TYPE_DIR, MimeTypes.IMAGE_MIME}; Loading Loading @@ -178,17 +177,16 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { } cursor.registerContentObserver(mObserver); // Filter hidden files. cursor = new FilteringCursorWrapper(cursor, mState.showHiddenFiles); FilteringCursorWrapper filteringCursor = new FilteringCursorWrapper(cursor); filteringCursor.filterHiddenFiles(mState.showHiddenFiles); if (mSearchMode && !mFeatures.isFoldersInSearchResultsEnabled()) { // There is no findDocumentPath API. Enable filtering on folders in search mode. cursor = new FilteringCursorWrapper(cursor, null, SEARCH_REJECT_MIMES); filteringCursor.filterMimes(/* acceptMimes= */ null, SEARCH_REJECT_MIMES); } if (mPhotoPicking) { cursor = new FilteringCursorWrapper(cursor, PHOTO_PICKING_ACCEPT_MIMES, null); filteringCursor.filterMimes(PHOTO_PICKING_ACCEPT_MIMES, /* rejectMimes= */ null); } cursor = filteringCursor; // TODO: When API tweaks have landed, use ContentResolver.EXTRA_HONORED_ARGS // instead of checking directly for ContentResolver.QUERY_ARG_SORT_COLUMNS (won't work) Loading
src/com/android/documentsui/MultiRootDocumentsLoader.java +7 −6 Original line number Diff line number Diff line Loading @@ -181,17 +181,18 @@ public abstract class MultiRootDocumentsLoader extends AsyncTaskLoader<Directory continue; } // Filter hidden files. cursor = new FilteringCursorWrapper(cursor, mState.showHiddenFiles); final FilteringCursorWrapper filtered = new FilteringCursorWrapper( cursor, mState.acceptMimes, getRejectMimes(), rejectBefore) { final FilteringCursorWrapper filteredCursor = new FilteringCursorWrapper(cursor) { @Override public void close() { // Ignored, since we manage cursor lifecycle internally } }; cursors.add(filtered); filteredCursor.filterHiddenFiles(mState.showHiddenFiles); filteredCursor.filterMimes(mState.acceptMimes, getRejectMimes()); filteredCursor.filterLastModified(rejectBefore); cursors.add(filteredCursor); } } catch (InterruptedException e) { Loading
src/com/android/documentsui/base/FilteringCursorWrapper.java +64 −47 Original line number Diff line number Diff line Loading @@ -29,68 +29,62 @@ import android.provider.DocumentsContract.Document; import android.util.Log; /** * Cursor wrapper that filters MIME types not matching given list. * Cursor wrapper that filters cursor results by given conditions. */ public class FilteringCursorWrapper extends AbstractCursor { private final Cursor mCursor; private final int[] mPosition; private int[] mPositions; private int mCount; public FilteringCursorWrapper(Cursor cursor, String[] acceptMimes) { this(cursor, acceptMimes, null, Long.MIN_VALUE); public FilteringCursorWrapper(Cursor cursor) { mCursor = cursor; mCount = cursor.getCount(); mPositions = new int[mCount]; for (int i = 0; i < mCount; i++) { mPositions[i] = i; } public FilteringCursorWrapper(Cursor cursor, String[] acceptMimes, String[] rejectMimes) { this(cursor, acceptMimes, rejectMimes, Long.MIN_VALUE); } public FilteringCursorWrapper( Cursor cursor, String[] acceptMimes, String[] rejectMimes, long rejectBefore) { mCursor = cursor; final int count = cursor.getCount(); mPosition = new int[count]; cursor.moveToPosition(-1); while (cursor.moveToNext() && mCount < count) { /** * Filters cursor according to mimes. If both lists are empty, all mimes will be rejected. * * @param acceptMimes allowed list of mimes * @param rejectMimes blocked list of mimes */ public void filterMimes(String[] acceptMimes, String[] rejectMimes) { filterByCondition((cursor) -> { final String mimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE); final long lastModified = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED); if (rejectMimes != null && MimeTypes.mimeMatches(rejectMimes, mimeType)) { continue; } if (lastModified < rejectBefore) { continue; } if (MimeTypes.mimeMatches(acceptMimes, mimeType)) { mPosition[mCount++] = cursor.getPosition(); return false; } return MimeTypes.mimeMatches(acceptMimes, mimeType); }); } if (DEBUG && mCount != cursor.getCount()) { Log.d(TAG, "Before filtering " + cursor.getCount() + ", after " + mCount); } /** Filters cursor according to last modified time, and reject earlier than given timestamp. */ public void filterLastModified(long rejectBeforeTimestamp) { filterByCondition((cursor) -> { final long lastModified = getCursorLong(cursor, Document.COLUMN_LAST_MODIFIED); return lastModified >= rejectBeforeTimestamp; }); } public FilteringCursorWrapper(Cursor cursor, boolean showHiddenFiles) { mCursor = cursor; final int count = cursor.getCount(); mPosition = new int[count]; cursor.moveToPosition(-1); while (cursor.moveToNext() && mCount < count) { final String documentId = getCursorString(cursor, Document.COLUMN_DOCUMENT_ID); if (!showHiddenFiles && documentId != null && (documentId.startsWith(".") || documentId.contains("/."))) { continue; } mPosition[mCount++] = cursor.getPosition(); /** Filter hidden files based on preference. */ public void filterHiddenFiles(boolean showHiddenFiles) { if (showHiddenFiles) { return; } if (DEBUG && mCount != cursor.getCount()) { Log.d(TAG, "Before filtering " + cursor.getCount() + ", after " + mCount); } filterByCondition((cursor) -> { // Judge by name and documentId separately because for some providers // e.g. DownloadProvider, documentId may not contain file name. final String name = getCursorString(cursor, Document.COLUMN_DISPLAY_NAME); final String documentId = getCursorString(cursor, Document.COLUMN_DOCUMENT_ID); boolean documentIdHidden = documentId != null && documentId.contains("/."); boolean fileNameHidden = name != null && name.startsWith("."); return !(documentIdHidden || fileNameHidden); }); } @Override Loading @@ -106,7 +100,7 @@ public class FilteringCursorWrapper extends AbstractCursor { @Override public boolean onMove(int oldPosition, int newPosition) { return mCursor.moveToPosition(mPosition[newPosition]); return mCursor.moveToPosition(mPositions[newPosition]); } @Override Loading Loading @@ -168,4 +162,27 @@ public class FilteringCursorWrapper extends AbstractCursor { public void unregisterContentObserver(ContentObserver observer) { mCursor.unregisterContentObserver(observer); } private interface FilteringCondition { boolean accept(Cursor cursor); } private void filterByCondition(FilteringCondition condition) { final int oldCount = this.getCount(); int[] newPositions = new int[oldCount]; int newCount = 0; this.moveToPosition(-1); while (this.moveToNext() && newCount < oldCount) { if (condition.accept(mCursor)) { newPositions[newCount++] = mPositions[this.getPosition()]; } } if (DEBUG && newCount != this.getCount()) { Log.d(TAG, "Before filtering " + oldCount + ", after " + newCount); } mCount = newCount; mPositions = newPositions; } }