diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java index 35d8988244f61..8852985da5d5f 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java +++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java @@ -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 @@ -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. */ @@ -1061,6 +1069,11 @@ public final class MultiSelectManager { return true; } } + + @Override + public boolean hasView(int pos) { + return mView.findViewHolderForAdapterPosition(pos) != null; + } } public interface Callback { @@ -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(); @@ -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); @@ -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. diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java index cc119fec82678..e401de1607080 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/MultiSelectManager_GridModelTest.java @@ -448,6 +448,11 @@ public class MultiSelectManager_GridModelTest extends AndroidTestCase { return false; } + @Override + public boolean hasView(int adapterPosition) { + return true; + } + public static final class Item { public String name; public Rect rect; diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestSelectionEnvironment.java b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestSelectionEnvironment.java index 56e54a61cfc0d..f56476978a4eb 100644 --- a/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestSelectionEnvironment.java +++ b/packages/DocumentsUI/tests/src/com/android/documentsui/dirlist/TestSelectionEnvironment.java @@ -100,4 +100,9 @@ public class TestSelectionEnvironment implements SelectionEnvironment { public boolean isLayoutItem(int adapterPosition) { return false; } + + @Override + public boolean hasView(int adapterPosition) { + return true; + } }