am 83de2e82: Merge "Leanback: use SingleRow for most widgets" into lmp-mr1-ub-dev

* commit '83de2e82354859ce689e9bb253298f58ecbd69a2':
  Leanback: use SingleRow for most widgets
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/Grid.java b/v17/leanback/src/android/support/v17/leanback/widget/Grid.java
index 5ebc4a6..ff09d7c 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/Grid.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/Grid.java
@@ -19,11 +19,9 @@
 import java.io.PrintWriter;
 
 /**
- * A grid is representation of multiple row layout data structure and algorithm.
- * Grid is the base class for both staggered case or simple non-staggered case.
+ * A grid is representation of single or multiple rows layout data structure and algorithm.
+ * Grid is the base class for single row, non-staggered grid and staggered grid.
  * <p>
- * User calls Grid.createStaggeredMutipleRows() to create an staggered instance.
- * TODO add createNonStaggeredRows().
  * To use the Grid, user must implement a Provider to create or remove visible item.
  * Grid maintains a list of visible items.  Visible items are created when
  * user calls appendVisibleItems() or prependVisibleItems() with certain limitation
@@ -121,11 +119,17 @@
     protected int mStartIndex = START_DEFAULT;
 
     /**
-     * Creates a multiple rows staggered grid.
+     * Creates a single or multiple rows (can be staggered or not staggered) grid
      */
-    public static Grid createStaggeredMultipleRows(int rows) {
-        StaggeredGridDefault grid = new StaggeredGridDefault();
-        grid.setNumRows(rows);
+    public static Grid createGrid(int rows) {
+        Grid grid;
+        if (rows == 1) {
+            grid = new SingleRow();
+        } else {
+            // TODO support non staggered multiple rows grid
+            grid = new StaggeredGridDefault();
+            grid.setNumRows(rows);
+        }
         return grid;
     }
 
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java b/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
index 5719061..8e79dfe 100644
--- a/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
+++ b/v17/leanback/src/android/support/v17/leanback/widget/GridLayoutManager.java
@@ -1044,7 +1044,7 @@
 
             if (mGrid == null || mNumRows != mGrid.getNumRows() ||
                     mReverseFlowPrimary != mGrid.isReversedFlow()) {
-                mGrid = Grid.createStaggeredMultipleRows(mNumRows);
+                mGrid = Grid.createGrid(mNumRows);
                 mGrid.setProvider(mGridProvider);
                 mGrid.setReversedFlow(mReverseFlowPrimary);
             }
diff --git a/v17/leanback/src/android/support/v17/leanback/widget/SingleRow.java b/v17/leanback/src/android/support/v17/leanback/widget/SingleRow.java
new file mode 100644
index 0000000..52666ac
--- /dev/null
+++ b/v17/leanback/src/android/support/v17/leanback/widget/SingleRow.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.support.v17.leanback.widget;
+
+import android.support.v4.util.CircularIntArray;
+
+import java.io.PrintWriter;
+
+/**
+ * A Grid with restriction to single row.
+ */
+class SingleRow extends Grid {
+
+    private final Location mTmpLocation = new Location(0);
+    private Object[] mTmpItem = new Object[1];
+
+    SingleRow() {
+        setNumRows(1);
+    }
+
+    @Override
+    public final Location getLocation(int index) {
+        // all items are on row 0, share the same Location object.
+        return mTmpLocation;
+    }
+
+    @Override
+    public final void debugPrint(PrintWriter pw) {
+        pw.print("SingleRow<");
+        pw.print(mFirstVisibleIndex);
+        pw.print(",");
+        pw.print(mLastVisibleIndex);
+        pw.print(">");
+        pw.println();
+    }
+
+    int getStartIndexForAppend() {
+        if (mLastVisibleIndex >= 0) {
+            return mLastVisibleIndex + 1;
+        } else if (mStartIndex != START_DEFAULT) {
+            return Math.min(mStartIndex, mProvider.getCount() - 1);
+        } else {
+            return 0;
+        }
+    }
+
+    int getStartIndexForPrepend() {
+        if (mFirstVisibleIndex >= 0) {
+            return mFirstVisibleIndex - 1;
+        } else if (mStartIndex != START_DEFAULT) {
+            return Math.min(mStartIndex, mProvider.getCount() - 1);
+        } else {
+            return mProvider.getCount() - 1;
+        }
+    }
+
+    @Override
+    protected final boolean prependVisibleItems(int toLimit, boolean oneColumnMode) {
+        if (mProvider.getCount() == 0) {
+            return false;
+        }
+        if (!oneColumnMode && checkPrependOverLimit(toLimit)) {
+            return false;
+        }
+        boolean filledOne = false;
+        for (int index = getStartIndexForPrepend(); index >= 0; index--) {
+            int size = mProvider.createItem(index, false, mTmpItem);
+            int edge;
+            if (mFirstVisibleIndex < 0 || mLastVisibleIndex < 0) {
+                edge = mReversedFlow ? Integer.MIN_VALUE : Integer.MAX_VALUE;
+                mLastVisibleIndex = mFirstVisibleIndex = index;
+            } else {
+                if (mReversedFlow) {
+                    edge = mProvider.getEdge(index + 1) + mMargin + size;
+                } else {
+                    edge = mProvider.getEdge(index + 1) - mMargin - size;
+                }
+                mFirstVisibleIndex = index;
+            }
+            mProvider.addItem(mTmpItem[0], index, size, 0, edge);
+            filledOne = true;
+            if (oneColumnMode || checkPrependOverLimit(toLimit)) {
+                break;
+            }
+        }
+        return filledOne;
+    }
+
+    @Override
+    protected final boolean appendVisibleItems(int toLimit, boolean oneColumnMode) {
+        if (mProvider.getCount() == 0) {
+            return false;
+        }
+        if (!oneColumnMode && checkAppendOverLimit(toLimit)) {
+            // not in one column mode, return immediately if over limit
+            return false;
+        }
+        boolean filledOne = false;
+        for (int index = getStartIndexForAppend(); index < mProvider.getCount(); index++) {
+            int size = mProvider.createItem(index, true, mTmpItem);
+            int edge;
+            if (mFirstVisibleIndex < 0 || mLastVisibleIndex< 0) {
+                edge = mReversedFlow ? Integer.MAX_VALUE : Integer.MIN_VALUE;
+                mLastVisibleIndex = mFirstVisibleIndex = index;
+            } else {
+                if (mReversedFlow) {
+                    edge = mProvider.getEdge(index - 1) - mProvider.getSize(index - 1) - mMargin;
+                } else {
+                    edge = mProvider.getEdge(index - 1) + mProvider.getSize(index - 1) + mMargin;
+                }
+                mLastVisibleIndex = index;
+            }
+            mProvider.addItem(mTmpItem[0], index, size, 0, edge);
+            filledOne = true;
+            if (oneColumnMode || checkAppendOverLimit(toLimit)) {
+                break;
+            }
+        }
+        return filledOne;
+    }
+
+    @Override
+    public final CircularIntArray[] getItemPositionsInRows(int startPos, int endPos) {
+        // all items are on the same row:
+        mTmpItemPositionsInRows[0].clear();
+        mTmpItemPositionsInRows[0].addLast(startPos);
+        mTmpItemPositionsInRows[0].addLast(endPos);
+        return mTmpItemPositionsInRows;
+    }
+
+    @Override
+    protected final int findRowMin(boolean findLarge, int indexLimit, int[] indices) {
+        if (indices != null) {
+            indices[0] = 0;
+            indices[1] = indexLimit;
+        }
+        return mReversedFlow ? mProvider.getEdge(indexLimit) - mProvider.getSize(indexLimit)
+                : mProvider.getEdge(indexLimit);
+    }
+
+    @Override
+    protected final int findRowMax(boolean findLarge, int indexLimit, int[] indices) {
+        if (indices != null) {
+            indices[0] = 0;
+            indices[1] = indexLimit;
+        }
+        return mReversedFlow ? mProvider.getEdge(indexLimit)
+                : mProvider.getEdge(indexLimit) + mProvider.getSize(indexLimit);
+    }
+
+}
diff --git a/v17/tests/src/android/support/v17/leanback/widget/SingleRowTest.java b/v17/tests/src/android/support/v17/leanback/widget/SingleRowTest.java
new file mode 100644
index 0000000..43b0a29
--- /dev/null
+++ b/v17/tests/src/android/support/v17/leanback/widget/SingleRowTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.support.v17.leanback.widget;
+
+/**
+ * Testing SingleRow algorithm
+ * @hide
+ */
+public class SingleRowTest extends GridTest {
+
+    SingleRow mSingleRow;
+
+    public void testAppendPrependRemove() throws Throwable {
+        mProvider = new Provider(new int[]{80, 80, 30, 100, 40, 10});
+
+        mSingleRow = new SingleRow();
+        mSingleRow.setMargin(20);
+        mSingleRow.setProvider(mProvider);
+        mSingleRow.appendVisibleItems(200);
+        assertEquals(dump(mSingleRow) + " Should filled 2 items", 1, mSingleRow.mLastVisibleIndex);
+
+        mSingleRow.appendVisibleItems(201);
+        assertEquals(dump(mSingleRow) + " Should filled 3 items",
+                2, mSingleRow.mLastVisibleIndex);
+
+        mSingleRow.appendVisibleItems(251);
+        assertEquals(dump(mSingleRow) + " Should filled 4 items",
+                3, mSingleRow.mLastVisibleIndex);
+
+        mSingleRow.appendVisibleItems(Integer.MAX_VALUE);
+        assertEquals(dump(mSingleRow) + " Should filled 6 items",
+               5, mSingleRow.mLastVisibleIndex);
+        assertEquals(mProvider.getEdge(0), 0);
+        assertEquals(mProvider.getEdge(1), 100);
+        assertEquals(mProvider.getEdge(2), 200);
+        assertEquals(mProvider.getEdge(3), 250);
+        assertEquals(mProvider.getEdge(4), 370);
+        assertEquals(mProvider.getEdge(5), 430);
+
+        mSingleRow.removeInvisibleItemsAtEnd(0, 200);
+        assertEquals(dump(mSingleRow) + " Should filled 2 items", 1, mSingleRow.mLastVisibleIndex);
+
+        mSingleRow.appendVisibleItems(Integer.MAX_VALUE);
+        assertEquals(dump(mSingleRow) + " Should filled 6 items",
+               5, mSingleRow.mLastVisibleIndex);
+
+        mSingleRow.removeInvisibleItemsAtFront(1000, 80);
+        assertEquals(dump(mSingleRow) + " visible index should start from 1",
+                1, mSingleRow.mFirstVisibleIndex);
+
+        mSingleRow.prependVisibleItems(0);
+        assertEquals(dump(mSingleRow) + " visible index should start from 0",
+                0, mSingleRow.mFirstVisibleIndex);
+    }
+
+    public void testAppendPrependRemoveReversed() throws Throwable {
+        mProvider = new Provider(new int[]{80, 80, 30, 100, 40, 10});
+
+        mSingleRow = new SingleRow();
+        mSingleRow.setMargin(20);
+        mSingleRow.setProvider(mProvider);
+        mSingleRow.setReversedFlow(true);
+        mSingleRow.appendVisibleItems(-200);
+        assertEquals(dump(mSingleRow) + " Should filled 2 items", 1, mSingleRow.mLastVisibleIndex);
+
+        mSingleRow.appendVisibleItems(-201);
+        assertEquals(dump(mSingleRow) + " Should filled 3 items",
+                2, mSingleRow.mLastVisibleIndex);
+
+        mSingleRow.appendVisibleItems(-251);
+        assertEquals(dump(mSingleRow) + " Should filled 4 items",
+                3, mSingleRow.mLastVisibleIndex);
+
+        mSingleRow.appendVisibleItems(Integer.MIN_VALUE);
+        assertEquals(dump(mSingleRow) + " Should filled 6 items",
+               5, mSingleRow.mLastVisibleIndex);
+        assertEquals(mProvider.getEdge(0), 0);
+        assertEquals(mProvider.getEdge(1), -100);
+        assertEquals(mProvider.getEdge(2), -200);
+        assertEquals(mProvider.getEdge(3), -250);
+        assertEquals(mProvider.getEdge(4), -370);
+        assertEquals(mProvider.getEdge(5), -430);
+
+        mSingleRow.removeInvisibleItemsAtEnd(0, -200);
+        assertEquals(dump(mSingleRow) + " Should filled 2 items", 1, mSingleRow.mLastVisibleIndex);
+
+        mSingleRow.appendVisibleItems(Integer.MIN_VALUE);
+        assertEquals(dump(mSingleRow) + " Should filled 6 items",
+               5, mSingleRow.mLastVisibleIndex);
+
+        mSingleRow.removeInvisibleItemsAtFront(1000, -80);
+        assertEquals(dump(mSingleRow) + " Should filled 6 items",
+                1, mSingleRow.mFirstVisibleIndex);
+    }
+}