fling
diff --git a/res/raw/rollo.c b/res/raw/rollo.c
index 7648270..bae390f 100644
--- a/res/raw/rollo.c
+++ b/res/raw/rollo.c
@@ -5,13 +5,6 @@
 
 #define PI 3.14159f
 
-// Allocations ======
-#define ALLOC_PARAMS    0
-#define ALLOC_STATE     1
-#define ALLOC_SCRATCH   2
-#define ALLOC_ICON_IDS  3
-#define ALLOC_LABEL_IDS 4
-
 // Variables from java ======
 
 // Parameters ======
@@ -23,11 +16,13 @@
 // State ======
 #define STATE_ICON_COUNT                0
 #define STATE_SCROLL_X                  1
+#define STATE_FLING_TIME                2
+#define STATE_FLING_VELOCITY_X          3
+#define STATE_ADJUSTED_DECELERATION     4
+#define STATE_CURRENT_SCROLL_X          5 /* with fling offset applied */
 
 // Scratch variables ======
-#define SCRATCH_FADE 0
-#define SCRATCH_ZOOM 1
-#define SCRATCH_ROT 2
+#define SCRATCH_ADJUSTED_DECELERATION   0
 
 // Drawing constants, should be parameters ======
 #define SCREEN_WIDTH 480
@@ -49,19 +44,31 @@
 #define COLUMN_GUTTER_PX 5
 #define LABEL_WIDTH_PX 105
 
+
 int
 count_pages(int iconCount)
 {
     int iconsPerPage = COLUMNS_PER_PAGE * ROWS_PER_PAGE;
     int pages = iconCount / iconsPerPage;
     if (pages*iconsPerPage != iconCount) {
-        iconCount++;
+        pages++;
     }
-    return iconCount;
+    return pages;
+}
+
+int current_page(float scrollXPx)
+{
+    return -scrollXPx / SCREEN_WIDTH;
+}
+
+float
+modf(float x, float y)
+{
+    return x-(y*floorf(x/y));
 }
 
 int
-main(void* con, int ft, int launchID)
+main(int launchID)
 {
     // Clear to transparent
     pfClearColor(0.0f, 0.0f, 0.0f, 0.0f);
@@ -91,25 +98,104 @@
     float labelTextureWidth = loadI32(ALLOC_PARAMS, PARAM_BUBBLE_BITMAP_WIDTH) * densityScale;
     float labelTextureHeight = loadI32(ALLOC_PARAMS, PARAM_BUBBLE_BITMAP_HEIGHT) * densityScale;
 
-    float pageLeft = -1;
-    int icon = 0;
+    float scrollXPx = loadI32(ALLOC_STATE, STATE_SCROLL_X);
+    float maxScrollX = -(pageCount-1) * SCREEN_WIDTH;
+    int done = 0;
+
+    // If we've been given a velocity, start a fling
+    float flingVelocityPxMs = loadI32(ALLOC_STATE, STATE_FLING_VELOCITY_X);
+    if (flingVelocityPxMs != 0) {
+        // how many screens will this velocity do? TODO: use long
+        // G * ppi * friction // why G? // friction = 0.015
+        float deceleration = loadF(ALLOC_STATE, STATE_ADJUSTED_DECELERATION);
+        if (deceleration == 0) {
+            // On the first frame, calculate which animation we're going to do.  If it's
+            // going to end up less than halfway into a page, we'll bounce back the previous
+            // page.  Otherwise, we'll adjust the deceleration so it just makes it to the
+            // page boundary.
+            if (flingVelocityPxMs > 0) {
+                deceleration = -1000;
+            } else {
+                deceleration = 1000;
+            }
+            // v' = v + at --> t = -v / a
+            // x' = x + vt + .5 a t^2 --> x' = x + (-v^2/a) + (v^2/2a)
+            float endPos = scrollXPx + (-(flingVelocityPxMs*flingVelocityPxMs)/deceleration)
+                    + ((flingVelocityPxMs*flingVelocityPxMs)/(2.0f*deceleration));
+
+            if (endPos > 0) {
+                endPos = 0;
+            }
+            if (endPos < maxScrollX) {
+                endPos = maxScrollX;
+            }
+            float screenWidthFloat = SCREEN_WIDTH;
+            float scrollOnPage = modf(endPos, screenWidthFloat);
+        }
+
+        // adjust the deceleration so we always hit a page boundary
+
+        int flingTime = loadI32(ALLOC_STATE, STATE_FLING_TIME);
+        int now = uptimeMillis();
+        float elapsedTime = (now - flingTime) / 1000.0f;
+        int animEndTime = -flingVelocityPxMs / deceleration;
+
+        done = elapsedTime >= animEndTime;
+        if (done) {
+            // clamp the time to the end value
+            elapsedTime = animEndTime;
+        }
+
+        float tension = 2.0f;
+        float interp = elapsedTime * elapsedTime * ((tension + 1) * elapsedTime + tension) + 1.0f; // normalized 0..1
+
+        int flingOffsetPx = (flingVelocityPxMs * elapsedTime)
+                + (deceleration * elapsedTime * elapsedTime / 2.0f);
+        scrollXPx += flingOffsetPx;
+
+    }
+
+    if (scrollXPx > 0) {
+        scrollXPx = 0;
+    }
+    if (scrollXPx < maxScrollX) {
+        scrollXPx = maxScrollX;
+    }
+    
+    storeI32(ALLOC_STATE, STATE_CURRENT_SCROLL_X, scrollXPx);
+    if (done) {
+        storeI32(ALLOC_STATE, STATE_SCROLL_X, scrollXPx);
+        storeI32(ALLOC_STATE, STATE_FLING_VELOCITY_X, 0);
+        storeF(ALLOC_STATE, STATE_ADJUSTED_DECELERATION, 0);
+    }
+
+    // don't draw everything, just the page before and after what we're viewing.
+    int currentPage = current_page(scrollXPx);
+    float screenWidth = SCREEN_WIDTH * densityScale;
+
+    float pageLeft = -1 + ((currentPage-1)*screenWidth);
+    int iconsPerPage = COLUMNS_PER_PAGE * ROWS_PER_PAGE;
+    int icon = (currentPage-1) * iconsPerPage;
+    if (icon < 0) {
+        icon = 0;
+    }
     int page;
-
-    int scrollXPx = loadI32(ALLOC_STATE, STATE_SCROLL_X);
-    debugI32("scrollXPx", scrollXPx);
+    int lastIcon = icon + (iconsPerPage*3);
+    if (lastIcon >= iconCount) {
+        lastIcon = iconCount-1;
+    }
     pageLeft += scrollXPx * densityScale;
-
-    for (page=0; page<pageCount; page++) {
+    for (page=currentPage-1; page<pageCount && page<=(currentPage+1); page++) {
         // Bug makes 1.0f alpha fail.
         color(1.0f, 1.0f, 1.0f, 0.99f);
         
         float cellTop = pagePaddingTop;
         int row;
-        for (row=0; row<ROWS_PER_PAGE && icon<iconCount; row++) {
+        for (row=0; row<ROWS_PER_PAGE && icon<=lastIcon; row++) {
             float s = pageLeft; // distance along the linear strip of icons in normalized coords
             s += pagePaddingLeft;
             int col;
-            for (col=0; col<COLUMNS_PER_PAGE && icon<iconCount; col++) {
+            for (col=0; col<COLUMNS_PER_PAGE && icon<=lastIcon; col++) {
                 // icon
                 float iconLeft = s + ((cellWidth-iconWidth)/2.0f);
                 float iconRight = iconLeft + iconWidth;
@@ -141,7 +227,6 @@
         pageLeft += 2.0f;
     }
 
-    return 1;
+    return !done;
 }
 
-
diff --git a/src/com/android/launcher2/AllAppsView.java b/src/com/android/launcher2/AllAppsView.java
index 78e3f1a..662a9dc 100644
--- a/src/com/android/launcher2/AllAppsView.java
+++ b/src/com/android/launcher2/AllAppsView.java
@@ -44,13 +44,16 @@
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.os.Message;
+import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
 import android.view.Surface;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.ViewConfiguration;
 import android.graphics.PixelFormat;
 
 
@@ -58,12 +61,16 @@
     private RenderScript mRS;
     private RolloRS mRollo;
 
+    private ViewConfiguration mConfig;
+    private VelocityTracker mVelocity;
+    private int mLastScrollX;
     private int mLastMotionX;
 
     public AllAppsView(Context context) {
         super(context);
         setFocusable(true);
         getHolder().setFormat(PixelFormat.TRANSLUCENT);
+        mConfig = ViewConfiguration.get(context);
     }
 
     public AllAppsView(Context context, AttributeSet attrs) {
@@ -98,18 +105,34 @@
         switch (ev.getAction()) {
             case MotionEvent.ACTION_DOWN:
                 mLastMotionX = x;
+                mRollo.mState.read();
+                mRollo.mState.scrollX = mLastScrollX = mRollo.mState.currentScrollX;
+                mRollo.mState.flingVelocityX = 0;
+                mRollo.mState.adjustedDeceleration = 0;
+                mRollo.mState.save();
+                mVelocity = VelocityTracker.obtain();
+                mVelocity.addMovement(ev);
                 break;
             case MotionEvent.ACTION_MOVE:
             case MotionEvent.ACTION_OUTSIDE:
                 deltaX = x - mLastMotionX;
-                mRollo.mState.scrollX += deltaX;
-                Log.d(Launcher.LOG_TAG, "updated scrollX=" + mRollo.mState.scrollX);
+                mVelocity.addMovement(ev);
+                mRollo.mState.currentScrollX = mLastScrollX;
+                mLastScrollX += deltaX;
+                mRollo.mState.scrollX = mLastScrollX;
                 mRollo.mState.save();
                 mLastMotionX = x;
                 break;
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
+                mVelocity.computeCurrentVelocity(1000 /* px/sec */,
+                        mConfig.getScaledMaximumFlingVelocity());
+                mRollo.mState.flingTimeMs = (int)SystemClock.uptimeMillis(); // TODO: use long
+                mRollo.mState.flingVelocityX = (int)mVelocity.getXVelocity();
+                mRollo.mState.save();
                 mLastMotionX = -10000;
+                mVelocity.recycle();
+                mVelocity = null;
                 break;
         }
         return true;
@@ -131,11 +154,6 @@
     public class RolloRS {
 
         // Allocations ======
-        static final int ALLOC_PARAMS = 0;
-        static final int ALLOC_STATE = 1;
-        static final int ALLOC_SCRATCH = 2;
-        static final int ALLOC_ICON_IDS = 3;
-        static final int ALLOC_LABEL_IDS = 4;
 
         private int mWidth;
         private int mHeight;
@@ -168,6 +186,14 @@
         Params mParams;
         State mState;
 
+        class Defines {
+            public static final int ALLOC_PARAMS = 0;
+            public static final int ALLOC_STATE = 1;
+            public static final int ALLOC_SCRATCH = 2;
+            public static final int ALLOC_ICON_IDS = 3;
+            public static final int ALLOC_LABEL_IDS = 4;
+        }
+
         class Params extends IntAllocation {
             Params(RenderScript rs) {
                 super(rs);
@@ -184,6 +210,10 @@
             }
             @AllocationIndex(0) public int iconCount;
             @AllocationIndex(1) public int scrollX;
+            @AllocationIndex(2) public int flingTimeMs;
+            @AllocationIndex(3) public int flingVelocityX;
+            @AllocationIndex(4) public int adjustedDeceleration;
+            @AllocationIndex(5) public int currentScrollX;
         }
 
         public RolloRS() {
@@ -261,15 +291,14 @@
             mRS.contextBindProgramVertex(mPV);
 
             mAllocScratchBuf = new int[32];
-            mAllocScratch = Allocation.createSized(mRS,
-                Element.USER_I32, mAllocScratchBuf.length);
+            mAllocScratch = Allocation.createSized(mRS, Element.USER_I32, mAllocScratchBuf.length);
             mAllocScratch.data(mAllocScratchBuf);
 
             Log.e("rs", "Done loading named");
         }
         
         private void initData() {
-            final int count = 29;
+            final int count = 100;
             mParams = new Params(mRS);
             mState = new State(mRS);
 
@@ -324,14 +353,15 @@
             ScriptC.Builder sb = new ScriptC.Builder(mRS);
             sb.setScript(mRes, R.raw.rollo);
             sb.setRoot(true);
+            sb.addDefines(Defines.class);
             mScript = sb.create();
             mScript.setClearColor(0.0f, 0.0f, 0.0f, 0.0f);
 
-            mScript.bindAllocation(mParams.getAllocation(), ALLOC_PARAMS);
-            mScript.bindAllocation(mState.getAllocation(), ALLOC_STATE);
-            mScript.bindAllocation(mAllocIconID, ALLOC_ICON_IDS);
-            mScript.bindAllocation(mAllocScratch, ALLOC_SCRATCH);
-            mScript.bindAllocation(mAllocLabelID, ALLOC_LABEL_IDS);
+            mScript.bindAllocation(mParams.getAllocation(), Defines.ALLOC_PARAMS);
+            mScript.bindAllocation(mState.getAllocation(), Defines.ALLOC_STATE);
+            mScript.bindAllocation(mAllocIconID, Defines.ALLOC_ICON_IDS);
+            mScript.bindAllocation(mAllocScratch, Defines.ALLOC_SCRATCH);
+            mScript.bindAllocation(mAllocLabelID, Defines.ALLOC_LABEL_IDS);
 
             mRS.contextBindRootScript(mScript);
         }
diff --git a/src/com/android/launcher2/IntAllocation.java b/src/com/android/launcher2/IntAllocation.java
index ab9d915..dde3d22 100644
--- a/src/com/android/launcher2/IntAllocation.java
+++ b/src/com/android/launcher2/IntAllocation.java
@@ -81,6 +81,24 @@
         mAlloc.data(buf);
     }
 
+    public void read() {
+        int[] buf = mBuffer;
+        if (buf != null) {
+            mAlloc.readData(buf);
+            Field[] fields = this.getClass().getFields();
+            for (Field f: fields) {
+                AllocationIndex index = f.getAnnotation(AllocationIndex.class);
+                if (index != null) {
+                    try {
+                        f.setInt(this, buf[index.value()]);
+                    } catch (IllegalAccessException ex) {
+                        throw new RuntimeException(ex);
+                    }
+                }
+            }
+        }
+    }
+
     Allocation getAllocation() {
         return mAlloc;
     }