Integrate band selection into the files app.

This consists of:
1) Moving both BandSelect* classes into MultiSelectManager as nested
classes. This removes extra layers of indirection and makes code easier
to comprehend.
2) Fixing an issue where band selection could start within an item
instead of only within margins between items.
3) Adding "provisional selection" support to the Selection class. This
gives band select the ability to select some items "temporarily" such
that when the band select no longer covers those items, they are
deselected.
4) Adding glue code between the classes.

Bug: 23081429, 23083146, 20669231

Change-Id: I0dc57e2c0d2ccedb3e1218f0e496de637be227a2
diff --git a/tests/src/com/android/documentsui/BandSelectMatrixTest.java b/tests/src/com/android/documentsui/BandSelectModelTest.java
similarity index 71%
rename from tests/src/com/android/documentsui/BandSelectMatrixTest.java
rename to tests/src/com/android/documentsui/BandSelectModelTest.java
index f15a643..20c4548 100644
--- a/tests/src/com/android/documentsui/BandSelectMatrixTest.java
+++ b/tests/src/com/android/documentsui/BandSelectModelTest.java
@@ -18,7 +18,7 @@
 
 import static org.junit.Assert.*;
 
-import com.android.documentsui.BandSelectMatrix;
+import com.android.documentsui.MultiSelectManager.BandSelectModel;
 
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -28,13 +28,13 @@
 import org.junit.After;
 import org.junit.Test;
 
-public class BandSelectMatrixTest {
+public class BandSelectModelTest {
 
     private static final int VIEW_PADDING_PX = 5;
     private static final int CHILD_VIEW_EDGE_PX = 100;
     private static final int VIEWPORT_HEIGHT = 500;
 
-    private static BandSelectMatrix matrix;
+    private static BandSelectModel model;
     private static TestHelper helper;
     private static SparseBooleanArray lastSelection;
     private static int viewWidth;
@@ -42,8 +42,8 @@
     private static void setUp(int numChildren, int numColumns) {
         helper = new TestHelper(numChildren, numColumns);
         viewWidth = VIEW_PADDING_PX + numColumns * (VIEW_PADDING_PX + CHILD_VIEW_EDGE_PX);
-        matrix = new BandSelectMatrix(helper);
-        matrix.addOnSelectionChangedListener(new BandSelectMatrix.OnSelectionChangedListener() {
+        model = new BandSelectModel(helper);
+        model.addOnSelectionChangedListener(new BandSelectModel.OnSelectionChangedListener() {
 
             @Override
             public void onSelectionChanged(SparseBooleanArray updatedSelection) {
@@ -54,7 +54,7 @@
 
     @After
     public void tearDown() {
-        matrix = null;
+        model = null;
         helper = null;
         lastSelection = null;
     }
@@ -62,111 +62,120 @@
     @Test
     public void testSelectionLeftOfItems() {
         setUp(20, 5);
-        matrix.startSelection(new Point(0, 10));
-        matrix.resizeSelection(new Point(1, 11));
+        model.startSelection(new Point(0, 10));
+        model.resizeSelection(new Point(1, 11));
         assertSelected(new int[0]);
+        assertEquals(BandSelectModel.NOT_SET, model.getPositionNearestOrigin());
     }
 
     @Test
     public void testSelectionRightOfItems() {
         setUp(20, 4);
-        matrix.startSelection(new Point(viewWidth - 1, 10));
-        matrix.resizeSelection(new Point(viewWidth - 2, 11));
+        model.startSelection(new Point(viewWidth - 1, 10));
+        model.resizeSelection(new Point(viewWidth - 2, 11));
         assertSelected(new int[0]);
+        assertEquals(BandSelectModel.NOT_SET, model.getPositionNearestOrigin());
     }
 
     @Test
     public void testSelectionAboveItems() {
         setUp(20, 4);
-        matrix.startSelection(new Point(10, 0));
-        matrix.resizeSelection(new Point(11, 1));
+        model.startSelection(new Point(10, 0));
+        model.resizeSelection(new Point(11, 1));
         assertSelected(new int[0]);
+        assertEquals(BandSelectModel.NOT_SET, model.getPositionNearestOrigin());
     }
 
     @Test
     public void testSelectionBelowItems() {
         setUp(5, 4);
-        matrix.startSelection(new Point(10, VIEWPORT_HEIGHT - 1));
-        matrix.resizeSelection(new Point(11, VIEWPORT_HEIGHT - 2));
+        model.startSelection(new Point(10, VIEWPORT_HEIGHT - 1));
+        model.resizeSelection(new Point(11, VIEWPORT_HEIGHT - 2));
         assertSelected(new int[0]);
+        assertEquals(BandSelectModel.NOT_SET, model.getPositionNearestOrigin());
     }
 
     @Test
     public void testVerticalSelectionBetweenItems() {
         setUp(20, 4);
-        matrix.startSelection(new Point(106, 0));
-        matrix.resizeSelection(new Point(107, 200));
+        model.startSelection(new Point(106, 0));
+        model.resizeSelection(new Point(107, 200));
         assertSelected(new int[0]);
+        assertEquals(BandSelectModel.NOT_SET, model.getPositionNearestOrigin());
     }
 
     @Test
     public void testHorizontalSelectionBetweenItems() {
         setUp(20, 4);
-        matrix.startSelection(new Point(0, 105));
-        matrix.resizeSelection(new Point(200, 106));
+        model.startSelection(new Point(0, 105));
+        model.resizeSelection(new Point(200, 106));
         assertSelected(new int[0]);
+        assertEquals(BandSelectModel.NOT_SET, model.getPositionNearestOrigin());
     }
 
     @Test
     public void testGrowingAndShrinkingSelection() {
         setUp(20, 4);
-        matrix.startSelection(new Point(0, 0));
-        matrix.resizeSelection(new Point(5, 5));
+        model.startSelection(new Point(0, 0));
+        model.resizeSelection(new Point(5, 5));
         assertSelected(new int[] {0});
-        matrix.resizeSelection(new Point(109, 109));
+        model.resizeSelection(new Point(109, 109));
         assertSelected(new int[] {0});
-        matrix.resizeSelection(new Point(110, 109));
+        model.resizeSelection(new Point(110, 109));
         assertSelected(new int[] {0, 1});
-        matrix.resizeSelection(new Point(110, 110));
+        model.resizeSelection(new Point(110, 110));
         assertSelected(new int[] {0, 1, 4, 5});
-        matrix.resizeSelection(new Point(214, 214));
+        model.resizeSelection(new Point(214, 214));
         assertSelected(new int[] {0, 1, 4, 5});
-        matrix.resizeSelection(new Point(215, 214));
+        model.resizeSelection(new Point(215, 214));
         assertSelected(new int[] {0, 1, 2, 4, 5, 6});
-        matrix.resizeSelection(new Point(214, 214));
+        model.resizeSelection(new Point(214, 214));
         assertSelected(new int[] {0, 1, 4, 5});
-        matrix.resizeSelection(new Point(110, 110));
+        model.resizeSelection(new Point(110, 110));
         assertSelected(new int[] {0, 1, 4, 5});
-        matrix.resizeSelection(new Point(110, 109));
+        model.resizeSelection(new Point(110, 109));
         assertSelected(new int[] {0, 1});
-        matrix.resizeSelection(new Point(109, 109));
+        model.resizeSelection(new Point(109, 109));
         assertSelected(new int[] {0});
-        matrix.resizeSelection(new Point(5, 5));
+        model.resizeSelection(new Point(5, 5));
         assertSelected(new int[] {0});
-        matrix.resizeSelection(new Point(0, 0));
+        model.resizeSelection(new Point(0, 0));
         assertSelected(new int[0]);
+        assertEquals(BandSelectModel.NOT_SET, model.getPositionNearestOrigin());
     }
 
     @Test
     public void testSelectionMovingAroundOrigin() {
         setUp(16, 4);
-        matrix.startSelection(new Point(210, 210));
-        matrix.resizeSelection(new Point(viewWidth - 1, 0));
+        model.startSelection(new Point(210, 210));
+        model.resizeSelection(new Point(viewWidth - 1, 0));
         assertSelected(new int[] {2, 3, 6, 7});
-        matrix.resizeSelection(new Point(0, 0));
+        model.resizeSelection(new Point(0, 0));
         assertSelected(new int[] {0, 1, 4, 5});
-        matrix.resizeSelection(new Point(0, 420));
+        model.resizeSelection(new Point(0, 420));
         assertSelected(new int[] {8, 9, 12, 13});
-        matrix.resizeSelection(new Point(viewWidth - 1, 420));
+        model.resizeSelection(new Point(viewWidth - 1, 420));
         assertSelected(new int[] {10, 11, 14, 15});
+        assertEquals(10, model.getPositionNearestOrigin());
     }
 
     @Test
     public void testScrollingBandSelect() {
         setUp(40, 4);
-        matrix.startSelection(new Point(0, 0));
-        matrix.resizeSelection(new Point(100, VIEWPORT_HEIGHT - 1));
+        model.startSelection(new Point(0, 0));
+        model.resizeSelection(new Point(100, VIEWPORT_HEIGHT - 1));
         assertSelected(new int[] {0, 4, 8, 12, 16});
         scroll(CHILD_VIEW_EDGE_PX);
         assertSelected(new int[] {0, 4, 8, 12, 16, 20});
-        matrix.resizeSelection(new Point(200, VIEWPORT_HEIGHT - 1));
+        model.resizeSelection(new Point(200, VIEWPORT_HEIGHT - 1));
         assertSelected(new int[] {0, 1, 4, 5, 8, 9, 12, 13, 16, 17, 20, 21});
         scroll(CHILD_VIEW_EDGE_PX);
         assertSelected(new int[] {0, 1, 4, 5, 8, 9, 12, 13, 16, 17, 20, 21, 24, 25});
         scroll(-2 * CHILD_VIEW_EDGE_PX);
         assertSelected(new int[] {0, 1, 4, 5, 8, 9, 12, 13, 16, 17});
-        matrix.resizeSelection(new Point(100, VIEWPORT_HEIGHT - 1));
+        model.resizeSelection(new Point(100, VIEWPORT_HEIGHT - 1));
         assertSelected(new int[] {0, 4, 8, 12, 16});
+        assertEquals(0, model.getPositionNearestOrigin());
     }
 
     private static void assertSelected(int[] selectedPositions) {
@@ -179,10 +188,10 @@
     private static void scroll(int dy) {
         assertTrue(helper.verticalOffset + VIEWPORT_HEIGHT + dy <= helper.getTotalHeight());
         helper.verticalOffset += dy;
-        matrix.onScrolled(null, 0, dy);
+        model.onScrolled(null, 0, dy);
     }
 
-    private static final class TestHelper implements BandSelectMatrix.RecyclerViewHelper {
+    private static final class TestHelper implements MultiSelectManager.BandModelHelper {
 
         public int horizontalOffset = 0;
         public int verticalOffset = 0;
diff --git a/tests/src/com/android/documentsui/MultiSelectManagerTest.java b/tests/src/com/android/documentsui/MultiSelectManagerTest.java
index 03ad3d4..b82251c 100644
--- a/tests/src/com/android/documentsui/MultiSelectManagerTest.java
+++ b/tests/src/com/android/documentsui/MultiSelectManagerTest.java
@@ -19,12 +19,12 @@
 import static org.junit.Assert.*;
 
 import android.support.v7.widget.RecyclerView;
+import android.util.SparseBooleanArray;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 
-import com.android.documentsui.MultiSelectManager.RecyclerViewHelper;
 import com.android.documentsui.MultiSelectManager.Selection;
 
 import org.junit.Before;
@@ -79,7 +79,7 @@
 
     @Test
     public void mouseClick_NoPosition_ClearsSelection() {
-        mManager.onLongPress(7);
+        mManager.onLongPress(7, 0);
         click(11);
         click(RecyclerView.NO_POSITION);
         assertSelection();
@@ -95,27 +95,27 @@
 
     @Test
     public void longPress_StartsSelectionMode() {
-        mManager.onLongPress(7);
+        mManager.onLongPress(7, 0);
         assertSelection(7);
     }
 
     @Test
     public void longPress_SecondPressExtendsSelection() {
-        mManager.onLongPress(7);
-        mManager.onLongPress(99);
+        mManager.onLongPress(7, 0);
+        mManager.onLongPress(99, 0);
         assertSelection(7, 99);
     }
 
     @Test
     public void singleTapUp_UnselectsSelectedItem() {
-        mManager.onLongPress(7);
+        mManager.onLongPress(7, 0);
         tap(7);
         assertSelection();
     }
 
     @Test
     public void singleTapUp_NoPosition_ClearsSelection() {
-        mManager.onLongPress(7);
+        mManager.onLongPress(7, 0);
         tap(11);
         tap(RecyclerView.NO_POSITION);
         assertSelection();
@@ -123,7 +123,7 @@
 
     @Test
     public void singleTapUp_ExtendsSelection() {
-        mManager.onLongPress(99);
+        mManager.onLongPress(99, 0);
         tap(7);
         tap(13);
         tap(129899);
@@ -132,21 +132,21 @@
 
     @Test
     public void singleTapUp_ShiftCreatesRangeSelection() {
-        mManager.onLongPress(7);
+        mManager.onLongPress(7, 0);
         shiftTap(17);
         assertRangeSelection(7, 17);
     }
 
     @Test
     public void singleTapUp_ShiftCreatesRangeSeletion_Backwards() {
-        mManager.onLongPress(17);
+        mManager.onLongPress(17, 0);
         shiftTap(7);
         assertRangeSelection(7, 17);
     }
 
     @Test
     public void singleTapUp_SecondShiftClickExtendsSelection() {
-        mManager.onLongPress(7);
+        mManager.onLongPress(7, 0);
         shiftTap(11);
         shiftTap(17);
         assertRangeSelection(7, 17);
@@ -154,7 +154,7 @@
 
     @Test
     public void singleTapUp_MultipleContiguousRangesSelected() {
-        mManager.onLongPress(7);
+        mManager.onLongPress(7, 0);
         shiftTap(11);
         tap(20);
         shiftTap(25);
@@ -165,7 +165,7 @@
 
     @Test
     public void singleTapUp_ShiftReducesSelectionRange_FromPreviousShiftClick() {
-        mManager.onLongPress(7);
+        mManager.onLongPress(7, 0);
         shiftTap(17);
         shiftTap(10);
         assertRangeSelection(7, 10);
@@ -173,7 +173,7 @@
 
     @Test
     public void singleTapUp_ShiftReducesSelectionRange_FromPreviousShiftClick_Backwards() {
-        mManager.onLongPress(17);
+        mManager.onLongPress(17, 0);
         shiftTap(7);
         shiftTap(14);
         assertRangeSelection(14, 17);
@@ -182,7 +182,7 @@
 
     @Test
     public void singleTapUp_ShiftReversesSelectionDirection() {
-        mManager.onLongPress(7);
+        mManager.onLongPress(7, 0);
         shiftTap(17);
         shiftTap(0);
         assertRangeSelection(0, 7);
@@ -206,6 +206,36 @@
         assertSelection(20);
     }
 
+    @Test
+    public void provisionaSelection() {
+        Selection s = mManager.getSelection();
+        assertSelection();
+
+        SparseBooleanArray provisional = new SparseBooleanArray();
+        provisional.append(1, true);
+        provisional.append(2, true);
+        s.setProvisionalSelection(provisional);
+        assertSelection(1, 2);
+
+        provisional.delete(1);
+        provisional.append(3, true);
+        s.setProvisionalSelection(provisional);
+        assertSelection(2, 3);
+
+        s.applyProvisionalSelection();
+        assertSelection(2, 3);
+
+        provisional.clear();
+        provisional.append(3, true);
+        provisional.append(4, true);
+        s.setProvisionalSelection(provisional);
+        assertSelection(2, 3, 4);
+
+        provisional.delete(3);
+        s.setProvisionalSelection(provisional);
+        assertSelection(2, 3, 4);
+    }
+
     private void tap(int position) {
         mManager.onSingleTapUp(position, 0, MotionEvent.TOOL_TYPE_MOUSE);
     }
@@ -252,7 +282,8 @@
         assertEquals(selection.toString(), expected, selection.size());
     }
 
-    private static final class EventHelper implements RecyclerViewHelper {
+    private static final class EventHelper implements MultiSelectManager.MultiSelectHelper {
+
         @Override
         public int findEventPosition(MotionEvent e) {
             throw new UnsupportedOperationException();
diff --git a/tests/src/com/android/documentsui/UnitTests.java b/tests/src/com/android/documentsui/UnitTests.java
index 0553270..d90130f 100644
--- a/tests/src/com/android/documentsui/UnitTests.java
+++ b/tests/src/com/android/documentsui/UnitTests.java
@@ -22,7 +22,7 @@
 
 @RunWith(Suite.class)
 @SuiteClasses({
-        BandSelectMatrixTest.class,
+        BandSelectModelTest.class,
         MultiSelectManager_SelectionTest.class,
         MultiSelectManagerTest.class
 })