Loading res/values/strings.xml +5 −0 Original line number Diff line number Diff line Loading @@ -247,4 +247,9 @@ <item quantity="one">Delete <xliff:g id="count" example="1">%1$d</xliff:g> item?</item> <item quantity="other">Delete <xliff:g id="count" example="3">%1$d</xliff:g> items?</item> </plurals> <!-- Snackbar shown to users who wanted to select more than 1000 items (files or directories). --> <string name="too_many_selected">Sorry, you can only select up to 1000 items at a time</string> <!-- Snackbar shown to users who wanted to select all, but there were too many items (files or directories). Only the first 1000 items are selected in such case. --> <string name="too_many_in_select_all">Could only select 1000 items</string> </resources> src/com/android/documentsui/QuickViewIntentBuilder.java +5 −5 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.documentsui; import static com.android.documentsui.Shared.DEBUG; import static com.android.documentsui.Shared.MAX_DOCS_IN_INTENT; import static com.android.documentsui.model.DocumentInfo.getCursorString; import android.content.ClipData; Loading Loading @@ -46,7 +47,6 @@ import java.util.List; final class QuickViewIntentBuilder { private static final String TAG = "QuickViewIntentBuilder"; private static final int MAX_CLIP_ITEMS = 1000; private final DocumentInfo mDocument; private final Model mModel; Loading Loading @@ -165,11 +165,11 @@ final class QuickViewIntentBuilder { int firstSibling; int lastSibling; if (documentLocation < uris.size() / 2) { firstSibling = Math.max(0, documentLocation - MAX_CLIP_ITEMS / 2); lastSibling = Math.min(uris.size() - 1, firstSibling + MAX_CLIP_ITEMS - 1); firstSibling = Math.max(0, documentLocation - MAX_DOCS_IN_INTENT / 2); lastSibling = Math.min(uris.size() - 1, firstSibling + MAX_DOCS_IN_INTENT - 1); } else { lastSibling = Math.min(uris.size() - 1, documentLocation + MAX_CLIP_ITEMS / 2); firstSibling = Math.max(0, lastSibling - MAX_CLIP_ITEMS + 1); lastSibling = Math.min(uris.size() - 1, documentLocation + MAX_DOCS_IN_INTENT / 2); firstSibling = Math.max(0, lastSibling - MAX_DOCS_IN_INTENT + 1); } if (DEBUG) Log.d(TAG, "Copmuted siblings from index: " + firstSibling Loading src/com/android/documentsui/Shared.java +5 −0 Original line number Diff line number Diff line Loading @@ -104,6 +104,11 @@ public final class Shared { */ public static final String EXTRA_BENCHMARK = "com.android.documentsui.benchmark"; /** * Maximum number of items in a Binder transaction packet. */ public static final int MAX_DOCS_IN_INTENT = 1000; private static final Collator sCollator; static { Loading src/com/android/documentsui/dirlist/DirectoryFragment.java +34 −5 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.documentsui.dirlist; import static com.android.documentsui.Shared.DEBUG; import static com.android.documentsui.Shared.MAX_DOCS_IN_INTENT; import static com.android.documentsui.State.MODE_GRID; import static com.android.documentsui.State.MODE_LIST; import static com.android.documentsui.State.SORT_ORDER_UNKNOWN; Loading Loading @@ -108,9 +109,11 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.Set; /** * Display the documents inside a single directory. Loading Loading @@ -475,8 +478,18 @@ public class DirectoryFragment extends Fragment final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE); final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS); if (!mTuner.canSelectType(docMimeType, docFlags)) { return false; } return mTuner.canSelectType(docMimeType, docFlags); if (mSelected.size() >= MAX_DOCS_IN_INTENT) { Snackbars.makeSnackbar( getActivity(), R.string.too_many_selected, Snackbar.LENGTH_SHORT) .show(); return false; } } return true; } Loading Loading @@ -1108,9 +1121,17 @@ public class DirectoryFragment extends Fragment public void selectAllFiles() { Metrics.logUserAction(getContext(), Metrics.USER_ACTION_SELECT_ALL); // Exclude disabled files List<String> enabled = new ArrayList<String>(); for (String id : mAdapter.getModelIds()) { // Exclude disabled files. Set<String> enabled = new HashSet<String>(); List<String> modelIds = mAdapter.getModelIds(); // Get the current selection. String[] alreadySelected = mSelectionManager.getSelection().getAll(); for (String id : alreadySelected) { enabled.add(id); } for (String id : modelIds) { Cursor cursor = getModel().getItem(id); if (cursor == null) { Log.w(TAG, "Skipping selection. Can't obtain cursor for modeId: " + id); Loading @@ -1118,7 +1139,15 @@ public class DirectoryFragment extends Fragment } String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE); int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS); if (isDocumentEnabled(docMimeType, docFlags)) { if (mTuner.canSelectType(docMimeType, docFlags)) { if (enabled.size() >= MAX_DOCS_IN_INTENT) { Snackbars.makeSnackbar( getActivity(), R.string.too_many_in_select_all, Snackbar.LENGTH_SHORT) .show(); break; } enabled.add(id); } } Loading src/com/android/documentsui/dirlist/MultiSelectManager.java +31 −3 Original line number Diff line number Diff line Loading @@ -154,6 +154,10 @@ public final class MultiSelectManager { // Update the selection to remove any disappeared IDs. mSelection.cancelProvisionalSelection(); mSelection.intersect(mModelIds); if (mBandManager != null && mBandManager.isActive()) { mBandManager.endBandSelect(); } } @Override Loading Loading @@ -940,6 +944,10 @@ public final class MultiSelectManager { * Layout items are excluded from the GridModel. */ boolean isLayoutItem(int adapterPosition); /** * Items may be in the adapter, but without an attached view. */ boolean hasView(int adapterPosition); } /** Recycler view facade implementation backed by good ol' RecyclerView. */ Loading Loading @@ -1061,6 +1069,11 @@ public final class MultiSelectManager { return true; } } @Override public boolean hasView(int pos) { return mView.findViewHolderForAdapterPosition(pos) != null; } } public interface Callback { Loading Loading @@ -1473,10 +1486,14 @@ public final class MultiSelectManager { * y-value. */ void startSelection(Point relativeOrigin) { recordVisibleChildren(); if (isEmpty()) { // The selection band logic works only if there is at least one visible child. return; } mIsActive = true; mPointer = mHelper.createAbsolutePoint(relativeOrigin); recordVisibleChildren(); mRelativeOrigin = new RelativePoint(mPointer); mRelativePointer = new RelativePoint(mPointer); computeCurrentSelection(); Loading Loading @@ -1530,7 +1547,11 @@ public final class MultiSelectManager { private void recordVisibleChildren() { for (int i = 0; i < mHelper.getVisibleChildCount(); i++) { int adapterPosition = mHelper.getAdapterPositionAt(i); if (!mHelper.isLayoutItem(adapterPosition) && // Sometimes the view is not attached, as we notify the multi selection manager // synchronously, while views are attached asynchronously. As a result items which // are in the adapter may not actually have a corresponding view (yet). if (mHelper.hasView(adapterPosition) && !mHelper.isLayoutItem(adapterPosition) && !mKnownPositions.get(adapterPosition)) { mKnownPositions.put(adapterPosition, true); recordItemData(mHelper.getAbsoluteRectForChildViewAt(i), adapterPosition); Loading @@ -1538,6 +1559,13 @@ public final class MultiSelectManager { } } /** * Checks if there are any recorded children. */ private boolean isEmpty() { return mColumnBounds.size() == 0 || mRowBounds.size() == 0; } /** * Updates the limits lists and column map with the given item metadata. * @param absoluteChildRect The absolute rectangle for the child view being processed. Loading Loading
res/values/strings.xml +5 −0 Original line number Diff line number Diff line Loading @@ -247,4 +247,9 @@ <item quantity="one">Delete <xliff:g id="count" example="1">%1$d</xliff:g> item?</item> <item quantity="other">Delete <xliff:g id="count" example="3">%1$d</xliff:g> items?</item> </plurals> <!-- Snackbar shown to users who wanted to select more than 1000 items (files or directories). --> <string name="too_many_selected">Sorry, you can only select up to 1000 items at a time</string> <!-- Snackbar shown to users who wanted to select all, but there were too many items (files or directories). Only the first 1000 items are selected in such case. --> <string name="too_many_in_select_all">Could only select 1000 items</string> </resources>
src/com/android/documentsui/QuickViewIntentBuilder.java +5 −5 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.documentsui; import static com.android.documentsui.Shared.DEBUG; import static com.android.documentsui.Shared.MAX_DOCS_IN_INTENT; import static com.android.documentsui.model.DocumentInfo.getCursorString; import android.content.ClipData; Loading Loading @@ -46,7 +47,6 @@ import java.util.List; final class QuickViewIntentBuilder { private static final String TAG = "QuickViewIntentBuilder"; private static final int MAX_CLIP_ITEMS = 1000; private final DocumentInfo mDocument; private final Model mModel; Loading Loading @@ -165,11 +165,11 @@ final class QuickViewIntentBuilder { int firstSibling; int lastSibling; if (documentLocation < uris.size() / 2) { firstSibling = Math.max(0, documentLocation - MAX_CLIP_ITEMS / 2); lastSibling = Math.min(uris.size() - 1, firstSibling + MAX_CLIP_ITEMS - 1); firstSibling = Math.max(0, documentLocation - MAX_DOCS_IN_INTENT / 2); lastSibling = Math.min(uris.size() - 1, firstSibling + MAX_DOCS_IN_INTENT - 1); } else { lastSibling = Math.min(uris.size() - 1, documentLocation + MAX_CLIP_ITEMS / 2); firstSibling = Math.max(0, lastSibling - MAX_CLIP_ITEMS + 1); lastSibling = Math.min(uris.size() - 1, documentLocation + MAX_DOCS_IN_INTENT / 2); firstSibling = Math.max(0, lastSibling - MAX_DOCS_IN_INTENT + 1); } if (DEBUG) Log.d(TAG, "Copmuted siblings from index: " + firstSibling Loading
src/com/android/documentsui/Shared.java +5 −0 Original line number Diff line number Diff line Loading @@ -104,6 +104,11 @@ public final class Shared { */ public static final String EXTRA_BENCHMARK = "com.android.documentsui.benchmark"; /** * Maximum number of items in a Binder transaction packet. */ public static final int MAX_DOCS_IN_INTENT = 1000; private static final Collator sCollator; static { Loading
src/com/android/documentsui/dirlist/DirectoryFragment.java +34 −5 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.documentsui.dirlist; import static com.android.documentsui.Shared.DEBUG; import static com.android.documentsui.Shared.MAX_DOCS_IN_INTENT; import static com.android.documentsui.State.MODE_GRID; import static com.android.documentsui.State.MODE_LIST; import static com.android.documentsui.State.SORT_ORDER_UNKNOWN; Loading Loading @@ -108,9 +109,11 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.Set; /** * Display the documents inside a single directory. Loading Loading @@ -475,8 +478,18 @@ public class DirectoryFragment extends Fragment final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE); final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS); if (!mTuner.canSelectType(docMimeType, docFlags)) { return false; } return mTuner.canSelectType(docMimeType, docFlags); if (mSelected.size() >= MAX_DOCS_IN_INTENT) { Snackbars.makeSnackbar( getActivity(), R.string.too_many_selected, Snackbar.LENGTH_SHORT) .show(); return false; } } return true; } Loading Loading @@ -1108,9 +1121,17 @@ public class DirectoryFragment extends Fragment public void selectAllFiles() { Metrics.logUserAction(getContext(), Metrics.USER_ACTION_SELECT_ALL); // Exclude disabled files List<String> enabled = new ArrayList<String>(); for (String id : mAdapter.getModelIds()) { // Exclude disabled files. Set<String> enabled = new HashSet<String>(); List<String> modelIds = mAdapter.getModelIds(); // Get the current selection. String[] alreadySelected = mSelectionManager.getSelection().getAll(); for (String id : alreadySelected) { enabled.add(id); } for (String id : modelIds) { Cursor cursor = getModel().getItem(id); if (cursor == null) { Log.w(TAG, "Skipping selection. Can't obtain cursor for modeId: " + id); Loading @@ -1118,7 +1139,15 @@ public class DirectoryFragment extends Fragment } String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE); int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS); if (isDocumentEnabled(docMimeType, docFlags)) { if (mTuner.canSelectType(docMimeType, docFlags)) { if (enabled.size() >= MAX_DOCS_IN_INTENT) { Snackbars.makeSnackbar( getActivity(), R.string.too_many_in_select_all, Snackbar.LENGTH_SHORT) .show(); break; } enabled.add(id); } } Loading
src/com/android/documentsui/dirlist/MultiSelectManager.java +31 −3 Original line number Diff line number Diff line Loading @@ -154,6 +154,10 @@ public final class MultiSelectManager { // Update the selection to remove any disappeared IDs. mSelection.cancelProvisionalSelection(); mSelection.intersect(mModelIds); if (mBandManager != null && mBandManager.isActive()) { mBandManager.endBandSelect(); } } @Override Loading Loading @@ -940,6 +944,10 @@ public final class MultiSelectManager { * Layout items are excluded from the GridModel. */ boolean isLayoutItem(int adapterPosition); /** * Items may be in the adapter, but without an attached view. */ boolean hasView(int adapterPosition); } /** Recycler view facade implementation backed by good ol' RecyclerView. */ Loading Loading @@ -1061,6 +1069,11 @@ public final class MultiSelectManager { return true; } } @Override public boolean hasView(int pos) { return mView.findViewHolderForAdapterPosition(pos) != null; } } public interface Callback { Loading Loading @@ -1473,10 +1486,14 @@ public final class MultiSelectManager { * y-value. */ void startSelection(Point relativeOrigin) { recordVisibleChildren(); if (isEmpty()) { // The selection band logic works only if there is at least one visible child. return; } mIsActive = true; mPointer = mHelper.createAbsolutePoint(relativeOrigin); recordVisibleChildren(); mRelativeOrigin = new RelativePoint(mPointer); mRelativePointer = new RelativePoint(mPointer); computeCurrentSelection(); Loading Loading @@ -1530,7 +1547,11 @@ public final class MultiSelectManager { private void recordVisibleChildren() { for (int i = 0; i < mHelper.getVisibleChildCount(); i++) { int adapterPosition = mHelper.getAdapterPositionAt(i); if (!mHelper.isLayoutItem(adapterPosition) && // Sometimes the view is not attached, as we notify the multi selection manager // synchronously, while views are attached asynchronously. As a result items which // are in the adapter may not actually have a corresponding view (yet). if (mHelper.hasView(adapterPosition) && !mHelper.isLayoutItem(adapterPosition) && !mKnownPositions.get(adapterPosition)) { mKnownPositions.put(adapterPosition, true); recordItemData(mHelper.getAbsoluteRectForChildViewAt(i), adapterPosition); Loading @@ -1538,6 +1559,13 @@ public final class MultiSelectManager { } } /** * Checks if there are any recorded children. */ private boolean isEmpty() { return mColumnBounds.size() == 0 || mRowBounds.size() == 0; } /** * Updates the limits lists and column map with the given item metadata. * @param absoluteChildRect The absolute rectangle for the child view being processed. Loading