Merge changes I5e500724,I40cfb12c into nyc-dev
* changes: Restrict selection to 1000 items in DocumentsUI. Cancel band selection on directory change.
This commit is contained in:
committed by
Android (Google) Code Review
commit
0fb51357af
@@ -247,4 +247,9 @@
|
|||||||
<item quantity="one">Delete <xliff:g id="count" example="1">%1$d</xliff:g> item?</item>
|
<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>
|
<item quantity="other">Delete <xliff:g id="count" example="3">%1$d</xliff:g> items?</item>
|
||||||
</plurals>
|
</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>
|
</resources>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.android.documentsui;
|
package com.android.documentsui;
|
||||||
|
|
||||||
import static com.android.documentsui.Shared.DEBUG;
|
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 static com.android.documentsui.model.DocumentInfo.getCursorString;
|
||||||
|
|
||||||
import android.content.ClipData;
|
import android.content.ClipData;
|
||||||
@@ -46,7 +47,6 @@ import java.util.List;
|
|||||||
final class QuickViewIntentBuilder {
|
final class QuickViewIntentBuilder {
|
||||||
|
|
||||||
private static final String TAG = "QuickViewIntentBuilder";
|
private static final String TAG = "QuickViewIntentBuilder";
|
||||||
private static final int MAX_CLIP_ITEMS = 1000;
|
|
||||||
|
|
||||||
private final DocumentInfo mDocument;
|
private final DocumentInfo mDocument;
|
||||||
private final Model mModel;
|
private final Model mModel;
|
||||||
@@ -165,11 +165,11 @@ final class QuickViewIntentBuilder {
|
|||||||
int firstSibling;
|
int firstSibling;
|
||||||
int lastSibling;
|
int lastSibling;
|
||||||
if (documentLocation < uris.size() / 2) {
|
if (documentLocation < uris.size() / 2) {
|
||||||
firstSibling = Math.max(0, documentLocation - MAX_CLIP_ITEMS / 2);
|
firstSibling = Math.max(0, documentLocation - MAX_DOCS_IN_INTENT / 2);
|
||||||
lastSibling = Math.min(uris.size() - 1, firstSibling + MAX_CLIP_ITEMS - 1);
|
lastSibling = Math.min(uris.size() - 1, firstSibling + MAX_DOCS_IN_INTENT - 1);
|
||||||
} else {
|
} else {
|
||||||
lastSibling = Math.min(uris.size() - 1, documentLocation + MAX_CLIP_ITEMS / 2);
|
lastSibling = Math.min(uris.size() - 1, documentLocation + MAX_DOCS_IN_INTENT / 2);
|
||||||
firstSibling = Math.max(0, lastSibling - MAX_CLIP_ITEMS + 1);
|
firstSibling = Math.max(0, lastSibling - MAX_DOCS_IN_INTENT + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DEBUG) Log.d(TAG, "Copmuted siblings from index: " + firstSibling
|
if (DEBUG) Log.d(TAG, "Copmuted siblings from index: " + firstSibling
|
||||||
|
|||||||
@@ -104,6 +104,11 @@ public final class Shared {
|
|||||||
*/
|
*/
|
||||||
public static final String EXTRA_BENCHMARK = "com.android.documentsui.benchmark";
|
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;
|
private static final Collator sCollator;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.android.documentsui.dirlist;
|
package com.android.documentsui.dirlist;
|
||||||
|
|
||||||
import static com.android.documentsui.Shared.DEBUG;
|
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_GRID;
|
||||||
import static com.android.documentsui.State.MODE_LIST;
|
import static com.android.documentsui.State.MODE_LIST;
|
||||||
import static com.android.documentsui.State.SORT_ORDER_UNKNOWN;
|
import static com.android.documentsui.State.SORT_ORDER_UNKNOWN;
|
||||||
@@ -108,9 +109,11 @@ import java.lang.annotation.Retention;
|
|||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Display the documents inside a single directory.
|
* Display the documents inside a single directory.
|
||||||
@@ -475,8 +478,18 @@ public class DirectoryFragment extends Fragment
|
|||||||
|
|
||||||
final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
|
final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
|
||||||
final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1108,9 +1121,17 @@ public class DirectoryFragment extends Fragment
|
|||||||
public void selectAllFiles() {
|
public void selectAllFiles() {
|
||||||
Metrics.logUserAction(getContext(), Metrics.USER_ACTION_SELECT_ALL);
|
Metrics.logUserAction(getContext(), Metrics.USER_ACTION_SELECT_ALL);
|
||||||
|
|
||||||
// Exclude disabled files
|
// Exclude disabled files.
|
||||||
List<String> enabled = new ArrayList<String>();
|
Set<String> enabled = new HashSet<String>();
|
||||||
for (String id : mAdapter.getModelIds()) {
|
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);
|
Cursor cursor = getModel().getItem(id);
|
||||||
if (cursor == null) {
|
if (cursor == null) {
|
||||||
Log.w(TAG, "Skipping selection. Can't obtain cursor for modeId: " + id);
|
Log.w(TAG, "Skipping selection. Can't obtain cursor for modeId: " + id);
|
||||||
@@ -1118,7 +1139,15 @@ public class DirectoryFragment extends Fragment
|
|||||||
}
|
}
|
||||||
String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
|
String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE);
|
||||||
int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS);
|
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);
|
enabled.add(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,6 +154,10 @@ public final class MultiSelectManager {
|
|||||||
// Update the selection to remove any disappeared IDs.
|
// Update the selection to remove any disappeared IDs.
|
||||||
mSelection.cancelProvisionalSelection();
|
mSelection.cancelProvisionalSelection();
|
||||||
mSelection.intersect(mModelIds);
|
mSelection.intersect(mModelIds);
|
||||||
|
|
||||||
|
if (mBandManager != null && mBandManager.isActive()) {
|
||||||
|
mBandManager.endBandSelect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -940,6 +944,10 @@ public final class MultiSelectManager {
|
|||||||
* Layout items are excluded from the GridModel.
|
* Layout items are excluded from the GridModel.
|
||||||
*/
|
*/
|
||||||
boolean isLayoutItem(int adapterPosition);
|
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. */
|
/** Recycler view facade implementation backed by good ol' RecyclerView. */
|
||||||
@@ -1061,6 +1069,11 @@ public final class MultiSelectManager {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasView(int pos) {
|
||||||
|
return mView.findViewHolderForAdapterPosition(pos) != null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface Callback {
|
public interface Callback {
|
||||||
@@ -1473,10 +1486,14 @@ public final class MultiSelectManager {
|
|||||||
* y-value.
|
* y-value.
|
||||||
*/
|
*/
|
||||||
void startSelection(Point relativeOrigin) {
|
void startSelection(Point relativeOrigin) {
|
||||||
|
recordVisibleChildren();
|
||||||
|
if (isEmpty()) {
|
||||||
|
// The selection band logic works only if there is at least one visible child.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
mIsActive = true;
|
mIsActive = true;
|
||||||
mPointer = mHelper.createAbsolutePoint(relativeOrigin);
|
mPointer = mHelper.createAbsolutePoint(relativeOrigin);
|
||||||
|
|
||||||
recordVisibleChildren();
|
|
||||||
mRelativeOrigin = new RelativePoint(mPointer);
|
mRelativeOrigin = new RelativePoint(mPointer);
|
||||||
mRelativePointer = new RelativePoint(mPointer);
|
mRelativePointer = new RelativePoint(mPointer);
|
||||||
computeCurrentSelection();
|
computeCurrentSelection();
|
||||||
@@ -1530,7 +1547,11 @@ public final class MultiSelectManager {
|
|||||||
private void recordVisibleChildren() {
|
private void recordVisibleChildren() {
|
||||||
for (int i = 0; i < mHelper.getVisibleChildCount(); i++) {
|
for (int i = 0; i < mHelper.getVisibleChildCount(); i++) {
|
||||||
int adapterPosition = mHelper.getAdapterPositionAt(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.get(adapterPosition)) {
|
||||||
mKnownPositions.put(adapterPosition, true);
|
mKnownPositions.put(adapterPosition, true);
|
||||||
recordItemData(mHelper.getAbsoluteRectForChildViewAt(i), adapterPosition);
|
recordItemData(mHelper.getAbsoluteRectForChildViewAt(i), adapterPosition);
|
||||||
@@ -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.
|
* Updates the limits lists and column map with the given item metadata.
|
||||||
* @param absoluteChildRect The absolute rectangle for the child view being processed.
|
* @param absoluteChildRect The absolute rectangle for the child view being processed.
|
||||||
|
|||||||
@@ -448,6 +448,11 @@ public class MultiSelectManager_GridModelTest extends AndroidTestCase {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasView(int adapterPosition) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public static final class Item {
|
public static final class Item {
|
||||||
public String name;
|
public String name;
|
||||||
public Rect rect;
|
public Rect rect;
|
||||||
|
|||||||
@@ -100,4 +100,9 @@ public class TestSelectionEnvironment implements SelectionEnvironment {
|
|||||||
public boolean isLayoutItem(int adapterPosition) {
|
public boolean isLayoutItem(int adapterPosition) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasView(int adapterPosition) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user