Merge "Don't treat prefetched cards as visible."
diff --git a/carousel/java/com/android/ex/carousel/carousel.rs b/carousel/java/com/android/ex/carousel/carousel.rs
index 39ab445..5398851 100644
--- a/carousel/java/com/android/ex/carousel/carousel.rs
+++ b/carousel/java/com/android/ex/carousel/carousel.rs
@@ -34,6 +34,7 @@
     int geometryState; // whether or not geometry is loaded
     int cardVisible; // not bool because of packing bug?
     int detailVisible; // not bool because of packing bug?
+    int shouldPrefetch; // not bool because of packing bug?
     int64_t textureTimeStamp; // time when this texture was last updated, in ms
     int64_t detailTextureTimeStamp; // time when this texture was last updated, in ms
     int64_t geometryTimeStamp; // time when the card itself was last updated, in ms
@@ -334,6 +335,7 @@
     card->geometryState = STATE_INVALID;
     card->cardVisible = false;
     card->detailVisible = false;
+    card->shouldPrefetch = false;
     card->textureTimeStamp = 0;
     card->detailTextureTimeStamp = 0;
     card->geometryTimeStamp = rsUptimeMillis();
@@ -1596,29 +1598,29 @@
 // Cull cards based on visibility and visibleSlotCount.
 // If visibleSlotCount is > 0, then only show those slots and cull the rest.
 // Otherwise, it should cull based on bounds of geometry.
-static int cullCards()
+static void cullCards()
 {
-    // TODO(jshuma): Instead of fully fetching prefetchCardCount cards, make a distinction between
-    // STATE_LOADED and a new STATE_PRELOADING, which will keep the textures loaded but will not
-    // attempt to actually draw them.
-    const int prefetchCardCountPerSide = prefetchCardCount / 2;
-    const float thetaFirst = slotPosition(-prefetchCardCountPerSide);
-    const float thetaSelected = slotPosition(0);
-    const float thetaHalfAngle = (thetaSelected - thetaFirst) * 0.5f;
-    const float thetaSelectedLow = thetaSelected - thetaHalfAngle;
-    const float thetaSelectedHigh = thetaSelected + thetaHalfAngle;
-    const float thetaLast = slotPosition(visibleSlotCount - 1 + prefetchCardCountPerSide);
+    // Calculate the first and last angles of visible slots.  We include 1
+    // slot on either side of visibleSlotCount to allow cards to slide in / out
+    // at either side, and rely on the view frustrum for accurate clipping.
+    const float visibleFirst = slotPosition(-1);
+    const float visibleLast = slotPosition(visibleSlotCount);
 
-    int count = 0;
+    // We'll load but not draw prefetchCardCountPerSide cards
+    // from either side of the visible slots.
+    const int prefetchCardCountPerSide = prefetchCardCount / 2;
+    const float prefetchFirst = slotPosition(-1 - prefetchCardCountPerSide);
+    const float prefetchLast = slotPosition(visibleSlotCount + prefetchCardCountPerSide);
     for (int i = 0; i < cardCount; i++) {
         if (visibleSlotCount > 0) {
-            // If visibleSlotCount is specified, then only show up to visibleSlotCount cards.
+            // If visibleSlotCount is specified then only show cards between visibleFirst and visibleLast
             float p = cardPosition(i);
-            if (p >= thetaFirst && p < thetaLast || p <= thetaFirst && p > thetaLast) {
-                cards[i].cardVisible = true;
+            if (p >= prefetchFirst && p < prefetchLast || p <= prefetchFirst && p > prefetchLast) {
+                cards[i].shouldPrefetch = true;
+                cards[i].cardVisible = p >= visibleFirst && p < visibleLast || p <= visibleFirst && p > visibleLast;
                 // cards[i].detailVisible will be set at draw time
-                count++;
             } else {
+                cards[i].shouldPrefetch = false;
                 cards[i].cardVisible = false;
                 cards[i].detailVisible = false;
             }
@@ -1627,69 +1629,82 @@
             // TODO
             cards[i].cardVisible = true;
             // cards[i].detailVisible will be set at draw time
-            count++;
         }
     }
-    return count;
+}
+
+// Request missing texture/geometry for a single card
+static void requestCardResources(int i) {
+    if (debugTextureLoading) rsDebug("*** Texture stamp: ", (int)cards[i].textureTimeStamp);
+    int data[1];
+    // request texture from client if not loaded
+    if (cards[i].textureState == STATE_INVALID) {
+        data[0] = i;
+        bool enqueued = rsSendToClient(CMD_REQUEST_TEXTURE, data, sizeof(data));
+        if (enqueued) {
+            cards[i].textureState = STATE_LOADING;
+        } else {
+            if (debugTextureLoading) rsDebug("Couldn't send CMD_REQUEST_TEXTURE", 0);
+        }
+    } else if (cards[i].textureState == STATE_STALE) {
+        data[0] = i;
+        bool enqueued = rsSendToClient(CMD_REQUEST_TEXTURE, data, sizeof(data));
+        if (enqueued) {
+            cards[i].textureState = STATE_UPDATING;
+        } else {
+            if (debugTextureLoading) rsDebug("Couldn't send CMD_REQUEST_TEXTURE", 0);
+        }
+    }
+    // request detail texture from client if not loaded
+    if (cards[i].detailTextureState == STATE_INVALID) {
+        data[0] = i;
+        bool enqueued = rsSendToClient(CMD_REQUEST_DETAIL_TEXTURE, data, sizeof(data));
+        if (enqueued) {
+            cards[i].detailTextureState = STATE_LOADING;
+        } else {
+            if (debugTextureLoading) rsDebug("Couldn't send CMD_REQUEST_DETAIL_TEXTURE", 0);
+        }
+    } else if (cards[i].detailTextureState == STATE_STALE) {
+        data[0] = i;
+        bool enqueued = rsSendToClient(CMD_REQUEST_DETAIL_TEXTURE, data, sizeof(data));
+        if (enqueued) {
+            cards[i].detailTextureState = STATE_UPDATING;
+        } else {
+            if (debugTextureLoading) rsDebug("Couldn't send CMD_REQUEST_DETAIL_TEXTURE", 0);
+        }
+    }
+    // request geometry from client if not loaded
+    if (cards[i].geometryState == STATE_INVALID) {
+        data[0] = i;
+        bool enqueued = rsSendToClient(CMD_REQUEST_GEOMETRY, data, sizeof(data));
+        if (enqueued) {
+            cards[i].geometryState = STATE_LOADING;
+        } else {
+            if (debugGeometryLoading) rsDebug("Couldn't send CMD_REQUEST_GEOMETRY", 0);
+        }
+    }
 }
 
 // Request texture/geometry for items that have come into view
 // or doesn't have a texture yet.
 static void updateCardResources(int64_t currentTime)
 {
+    // First process any visible cards
     for (int i = cardCount-1; i >= 0; --i) {
-        int data[1];
         if (cards[i].cardVisible) {
-            if (debugTextureLoading) rsDebug("*** Texture stamp: ", (int)cards[i].textureTimeStamp);
+            requestCardResources(i);
+        }
+    }
 
-            // request texture from client if not loaded
-            if (cards[i].textureState == STATE_INVALID) {
-                data[0] = i;
-                bool enqueued = rsSendToClient(CMD_REQUEST_TEXTURE, data, sizeof(data));
-                if (enqueued) {
-                    cards[i].textureState = STATE_LOADING;
-                } else {
-                    if (debugTextureLoading) rsDebug("Couldn't send CMD_REQUEST_TEXTURE", 0);
-                }
-            } else if (cards[i].textureState == STATE_STALE) {
-                data[0] = i;
-                bool enqueued = rsSendToClient(CMD_REQUEST_TEXTURE, data, sizeof(data));
-                if (enqueued) {
-                    cards[i].textureState = STATE_UPDATING;
-                } else {
-                    if (debugTextureLoading) rsDebug("Couldn't send CMD_REQUEST_TEXTURE", 0);
-                }
-            }
-            // request detail texture from client if not loaded
-            if (cards[i].detailTextureState == STATE_INVALID) {
-                data[0] = i;
-                bool enqueued = rsSendToClient(CMD_REQUEST_DETAIL_TEXTURE, data, sizeof(data));
-                if (enqueued) {
-                    cards[i].detailTextureState = STATE_LOADING;
-                } else {
-                    if (debugTextureLoading) rsDebug("Couldn't send CMD_REQUEST_DETAIL_TEXTURE", 0);
-                }
-            } else if (cards[i].detailTextureState == STATE_STALE) {
-                data[0] = i;
-                bool enqueued = rsSendToClient(CMD_REQUEST_DETAIL_TEXTURE, data, sizeof(data));
-                if (enqueued) {
-                    cards[i].detailTextureState = STATE_UPDATING;
-                } else {
-                    if (debugTextureLoading) rsDebug("Couldn't send CMD_REQUEST_DETAIL_TEXTURE", 0);
-                }
-            }
-            // request geometry from client if not loaded
-            if (cards[i].geometryState == STATE_INVALID) {
-                data[0] = i;
-                bool enqueued = rsSendToClient(CMD_REQUEST_GEOMETRY, data, sizeof(data));
-                if (enqueued) {
-                    cards[i].geometryState = STATE_LOADING;
-                } else {
-                    if (debugGeometryLoading) rsDebug("Couldn't send CMD_REQUEST_GEOMETRY", 0);
-                }
-            }
+    // Then the rest
+    for (int i = cardCount-1; i >= 0; --i) {
+        if (cards[i].cardVisible) {
+            // already requested above
+         } else if (cards[i].shouldPrefetch) {
+            requestCardResources(i);
         } else {
             // ask the host to remove the texture
+            int data[1];
             if (cards[i].textureState != STATE_INVALID) {
                 data[0] = i;
                 bool enqueued = rsSendToClient(CMD_INVALIDATE_TEXTURE, data, sizeof(data));