Implement new physics model for scroll.
diff --git a/res/raw/rollo.c b/res/raw/rollo.c
index 5ff3308..98e5738 100644
--- a/res/raw/rollo.c
+++ b/res/raw/rollo.c
@@ -5,13 +5,82 @@
 
 #define PI 3.14159f
 
-float deceleration;
+
+// Attraction to center values from page edge to page center.
+float g_AttractionTable[9];
+float g_FrictionTable[9];
+float g_PhysicsTableSize;
+
+float g_PosPage;
+float g_PosVelocity;
+float g_LastPositionX;
+int g_LastTouchDown;
+float g_DT;
+int g_LastTime;
+int g_Rendering;
+int g_PageCount;
 
 // Drawing constants, should be parameters ======
 #define VIEW_ANGLE 1.28700222f
 
 void init() {
-    deceleration = 0;
+    g_AttractionTable[0] = 4.5f;
+    g_AttractionTable[1] = 4.5f;
+    g_AttractionTable[2] = 5.0f;
+    g_AttractionTable[3] = 4.0f;
+    g_AttractionTable[4] = -4.0f;
+    g_AttractionTable[5] = -5.0f;
+    g_AttractionTable[6] = -4.5f;
+    g_AttractionTable[7] = -4.5f;
+    g_AttractionTable[8] = -4.5f;  // dup 7 to avoid a clamp later
+    g_FrictionTable[0] = 3.5f;
+    g_FrictionTable[1] = 3.6f;
+    g_FrictionTable[2] = 3.7f;
+    g_FrictionTable[3] = 3.8f;
+    g_FrictionTable[4] = 3.8f;
+    g_FrictionTable[5] = 3.7f;
+    g_FrictionTable[6] = 3.6f;
+    g_FrictionTable[7] = 3.5f;
+    g_FrictionTable[8] = 3.5f;  // dup 7 to avoid a clamp later
+    g_PhysicsTableSize = 7;
+
+    g_PosVelocity = 0;
+    g_PosPage = 0;
+    g_LastTouchDown = 0;
+    g_LastPositionX = 0;
+}
+
+void clampPosition() {
+    if (g_PosPage < 0) {
+        g_PosPage = 0;
+        g_PosVelocity = 0;
+    }
+    if (g_PosPage > (g_PageCount - 1)) {
+        g_PosPage = (g_PageCount - 1);
+        g_PosVelocity = 0;
+    }
+}
+
+void move() {
+    if (g_LastTouchDown) {
+        float dx = -(state->newPositionX - g_LastPositionX);
+        g_PosVelocity = 0;
+        g_PosPage += dx;
+    }
+    g_LastTouchDown = state->newTouchDown;
+    g_LastPositionX = state->newPositionX;
+    clampPosition();
+}
+
+void fling() {
+    g_LastTouchDown = 0;
+    g_PosVelocity = -state->flingVelocityX;
+    if (g_PosPage <= 0) {
+        g_PosVelocity = maxf(0, g_PosVelocity);
+    }
+    if (g_PosPage > (g_PageCount - 1)) {
+        g_PosVelocity = minf(0, (g_PageCount - 1) - g_PosPage);
+    }
 }
 
 int g_lastFrameTime = 0;
@@ -41,6 +110,76 @@
     return x-(y*floorf(x/y));
 }
 
+void updatePos() {
+    if (g_LastTouchDown) {
+        return;
+    }
+
+    //debugF("g_PosPage", g_PosPage);
+    //debugF("  g_PosVelocity", g_PosVelocity);
+
+    float tablePosNorm = fracf(g_PosPage + 0.5f);
+    float tablePosF = tablePosNorm * g_PhysicsTableSize;
+    int tablePosI = tablePosF;
+    float tablePosFrac = tablePosF - tablePosI;
+    //debugF("tablePosNorm", tablePosNorm);
+    //debugF("tablePosF", tablePosF);
+    //debugF("tablePosI", tablePosI);
+    //debugF("tablePosFrac", tablePosFrac);
+
+    float accel = lerpf(g_AttractionTable[tablePosI],
+                        g_AttractionTable[tablePosI + 1],
+                        tablePosFrac) * g_DT;
+    float friction = lerpf(g_FrictionTable[tablePosI],
+                           g_FrictionTable[tablePosI + 1],
+                           tablePosFrac) * g_DT;
+    //debugF("  accel", accel);
+    //debugF("  friction", friction);
+
+    g_PosVelocity += accel;
+    if ((friction > fabsf(g_PosVelocity)) && (friction > fabsf(accel))) {
+        // Special get back to center and overcome friction physics.
+        float t = tablePosNorm - 0.5f;
+        if (fabsf(t) < (friction * g_DT)) {
+            // really close, just snap
+            g_PosPage = roundf(g_PosPage);
+            g_PosVelocity = 0;
+        } else {
+            if (t > 0) {
+                g_PosVelocity = -friction;
+            } else {
+                g_PosVelocity = friction;
+            }
+        }
+    } else {
+        // Normal physics
+        if (g_PosVelocity > 0) {
+            g_PosVelocity -= friction;
+            if (g_PosVelocity < 0) {
+                g_PosVelocity = 0;
+            }
+        } else {
+            g_PosVelocity += friction;
+            if (g_PosVelocity > 0) {
+                g_PosVelocity = 0;
+            }
+        }
+    }
+    g_PosPage += g_PosVelocity * g_DT;
+
+    // Check for out of boundry conditions.
+    if (g_PosPage < 0 && g_PosVelocity < 0) {
+        float damp = 1.0 + (g_PosPage * 3);
+        damp = clampf(damp, 0.f, 0.9f);
+        g_PosVelocity *= damp;
+    }
+    if (g_PosPage > (g_PageCount-1) && g_PosVelocity > 0) {
+        float damp = 1.0 - ((g_PosPage - g_PageCount + 1) * 3);
+        damp = clampf(damp, 0.f, 0.9f);
+        g_PosVelocity *= damp;
+    }
+}
+
 float
 far_size(float sizeAt0)
 {
@@ -53,6 +192,7 @@
     int row;
     int col;
 
+    //debugF("center angle", centerAngle);
     float scale = 1.0f - state->zoom;
 
     float iconTextureWidth = ICON_WIDTH_PX / (float)ICON_TEXTURE_WIDTH_PX;
@@ -142,6 +282,11 @@
     // Clear to transparent
     pfClearColor(0.0f, 0.0f, 0.0f, 0.0f);
 
+    int newTime = uptimeMillis();
+    g_DT = (newTime - g_LastTime) / 1000.f;
+    g_LastTime = newTime;
+    //debugF("*** dt ", g_DT);
+
     // If we're not supposed to be showing, don't do anything.
     if (!state->visible) {
         return 0;
@@ -149,128 +294,13 @@
 
     // icons & labels
     int iconCount = state->iconCount;
-    int pageCount = count_pages(iconCount);
+    g_PageCount = count_pages(iconCount);
 
-    float scrollXPx = state->scrollX;
-    float maxScrollXPx = -(pageCount-1) * SCREEN_WIDTH_PX;
-    int done = 0;
+    updatePos(0.1f);
+    state->readPosX = g_PosPage;
+    state->readVel = g_PosVelocity;
 
-    // Clamp -- because java doesn't know how big the icons are
-    if (scrollXPx > 0) {
-        scrollXPx = 0;
-    }
-    if (scrollXPx < maxScrollXPx) {
-        scrollXPx = maxScrollXPx;
-    }
-
-    // If we've been given a velocity, start a fling
-    float flingVelocityPxMs = state->flingVelocityX;
-    if (flingVelocityPxMs != 0) {
-        // how many screens will this velocity do? TODO: use long
-        // G * ppi * friction // why G? // friction = 0.015
-        float flingDurationMs;
-        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;
-            }
-            // minimum velocity
-            if (flingVelocityPxMs < 0) {
-                if (flingVelocityPxMs > -500) {
-                    flingVelocityPxMs = -500;
-                }
-            } else {
-                if (flingVelocityPxMs < 500) {
-                    flingVelocityPxMs = 500;
-                }
-            }
-
-            // v' = v + at --> t = -v / a
-            // x' = x + vt + .5 a t^2
-            flingDurationMs = - flingVelocityPxMs / deceleration;
-            float endPos = scrollXPx + (flingVelocityPxMs*flingDurationMs)
-                    + ((deceleration*flingDurationMs*flingDurationMs)/2);
-
-            if (endPos > 0) {
-                endPos = 0;
-            }
-            if (endPos < maxScrollXPx) {
-                endPos = maxScrollXPx;
-            }
-            float scrollOnPage = modf(endPos, SCREEN_WIDTH_PX);
-            int endPage = -endPos/SCREEN_WIDTH_PX;
-
-            if (flingVelocityPxMs < 0) {
-                if (scrollOnPage < (SCREEN_WIDTH_PX/2)) {
-                    // adjust the deceleration so we align on the page boundary
-                    // a = 2(x-x0-v0t)/t^2
-                    endPos = -(endPage+1) * SCREEN_WIDTH_PX;
-                    debugI32("endPos case 1", endPos);
-                } else {
-                    // TODO: bounce
-                    endPos = -(endPage+1) * SCREEN_WIDTH_PX;
-                    debugI32("endPos case 2", endPos);
-                }
-            } else {
-                if (scrollOnPage >= (SCREEN_WIDTH_PX/2)) {
-                    // adjust the deceleration so we align on the page boundary
-                    endPos = -endPage * SCREEN_WIDTH_PX;
-                    debugI32("endPos case 3", endPos);
-                } else {
-                    // TODO: bounce
-                    endPos = -endPage * SCREEN_WIDTH_PX;
-                    debugI32("endPos case 4", endPos);
-                }
-            }
-            // v = v0 + at --> (v - v0) / t
-            deceleration = 2*(endPos-scrollXPx-(flingVelocityPxMs*flingDurationMs))
-                    / (flingDurationMs*flingDurationMs);
-            endPos = scrollXPx + (flingVelocityPxMs*flingDurationMs)
-                    + ((deceleration*flingDurationMs*flingDurationMs)/2);
-
-            state->flingDuration = flingDurationMs;
-            state->flingEndPos = endPos;
-        } else {
-            flingDurationMs = state->flingDuration;
-        }
-
-        // adjust the deceleration so we always hit a page boundary
-
-        int now = uptimeMillis();
-        float elapsedTime = (now - state->flingTimeMs) / 1000.0f;
-        int animEndTime = -flingVelocityPxMs / deceleration;
-
-        int flingOffsetPx = (flingVelocityPxMs * elapsedTime)
-                + (deceleration * elapsedTime * elapsedTime / 2.0f);
-        scrollXPx += flingOffsetPx;
-
-        if (elapsedTime > flingDurationMs) {
-            scrollXPx = state->flingEndPos;
-            done = 1;
-        }
-    } else {
-        done = 1;
-    }
-
-    // Clamp
-    if (scrollXPx > 0) {
-        scrollXPx = 0;
-    }
-    if (scrollXPx < maxScrollXPx) {
-        scrollXPx = maxScrollXPx;
-    }
-
-    state->currentScrollX = scrollXPx;
-    if (done) {
-        state->scrollX = scrollXPx;
-        state->flingVelocityX = 0;
-        deceleration = 0.f;
-    }
+    //debugF("    draw g_PosPage", g_PosPage);
 
     // Draw the icons ========================================
     bindProgramVertex(NAMED_PV);
@@ -282,9 +312,8 @@
 
     int lastIcon = iconCount-1;
 
-    float currentPage = -scrollXPx / (float)SCREEN_WIDTH_PX;
-    int page = currentPage;
-    float currentPagePosition = currentPage - page;
+    int page = g_PosPage;
+    float currentPagePosition = g_PosPage - page;
 
     int iconsPerPage = COLUMNS_PER_PAGE * ROWS_PER_PAGE;
     int icon = clamp(iconsPerPage * page, 0, lastIcon);
@@ -292,6 +321,7 @@
     draw_page(icon, lastIcon, -VIEW_ANGLE*currentPagePosition);
     draw_page(icon+iconsPerPage, lastIcon, (-VIEW_ANGLE*currentPagePosition)+VIEW_ANGLE);
 
+
     // Draw the border lines for debugging ========================================
     /*
     bindProgramVertex(NAMED_PVOrtho);
@@ -320,8 +350,13 @@
     drawRect(handleLeft, handleTop, handleLeft+handleWidth, handleTop+handleHeight, 0.0f);
     */
 
-    print_frame_rate();
+    //print_frame_rate();
 
-    return !done;
+    // Bug workaround where the last frame is not always displayed
+    // So we render the last frame twice.
+    int rendering = g_Rendering;
+    g_Rendering = (g_PosVelocity != 0) || fracf(g_PosPage);
+    rendering |= g_Rendering;
+    return rendering;
 }
 
diff --git a/src/com/android/launcher2/AllAppsView.java b/src/com/android/launcher2/AllAppsView.java
index 95f36d6..03734c0 100644
--- a/src/com/android/launcher2/AllAppsView.java
+++ b/src/com/android/launcher2/AllAppsView.java
@@ -85,11 +85,11 @@
     private int mPageCount;
     private boolean mStartedScrolling;
     private VelocityTracker mVelocity;
-    private int mLastScrollX;
     private int mLastMotionX;
     private int mMotionDownRawX;
     private int mMotionDownRawY;
     private int mScrollHandleTop;
+    private long mTouchTime;
 
     static class Defines {
         private static float farSize(float sizeAt0) {
@@ -191,16 +191,17 @@
             mMotionDownRawY = (int)ev.getRawY();
             mLastMotionX = x;
             mRollo.mState.read();
-            mRollo.mState.startScrollX = mRollo.mState.scrollX = mLastScrollX
-                    = mRollo.mState.currentScrollX;
-            if (mRollo.mState.flingVelocityX != 0) {
+
+            mRollo.mState.newPositionX = ev.getRawX() / Defines.SCREEN_WIDTH_PX;
+            mRollo.mState.newTouchDown = 1;
+
+            if (mRollo.mState.readVel != 0) {
                 mRollo.clearSelectedIcon();
             } else {
-                mRollo.selectIcon(x, (int)ev.getY(), mRollo.mState.startScrollX,
-                        (-mRollo.mState.startScrollX / Defines.SCREEN_WIDTH_PX));
+                mRollo.selectIcon(x, (int)ev.getY(), mRollo.mState.readPosX);
             }
-            mRollo.mState.flingVelocityX = 0;
             mRollo.mState.save();
+            mRollo.mInvokeMove.execute();
             mVelocity = VelocityTracker.obtain();
             mVelocity.addMovement(ev);
             mStartedScrolling = false;
@@ -212,25 +213,31 @@
                 // don't update mLastMotionX so slop is right and when we do start scrolling
                 // below, we get the right delta.
             } else {
+
+                mRollo.mState.newPositionX = ev.getRawX() / Defines.SCREEN_WIDTH_PX;
+                mRollo.mState.newTouchDown = 1;
+                mRollo.mInvokeMove.execute();
+
                 mStartedScrolling = true;
                 mRollo.clearSelectedIcon();
                 deltaX = x - mLastMotionX;
                 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:
+
+            mRollo.mState.newPositionX = ev.getRawX() / Defines.SCREEN_WIDTH_PX;
+            mRollo.mState.newTouchDown = 0;
+
             mVelocity.computeCurrentVelocity(1000 /* px/sec */,
                     mConfig.getScaledMaximumFlingVelocity());
-            mRollo.mState.flingTimeMs = (int)SystemClock.uptimeMillis(); // TODO: use long
-            mRollo.mState.flingVelocityX = (int)mVelocity.getXVelocity();
+            mRollo.mState.flingVelocityX = mVelocity.getXVelocity() / Defines.SCREEN_WIDTH_PX;
             mRollo.clearSelectedIcon();
             mRollo.mState.save();
+            mRollo.mInvokeFling.execute();
             mLastMotionX = -10000;
             mVelocity.recycle();
             mVelocity = null;
@@ -354,6 +361,10 @@
 
         private Resources mRes;
         private Script mScript;
+
+        private Script.Invokable mInvokeMove;
+        private Script.Invokable mInvokeFling;
+
         private Sampler mSampler;
         private Sampler mSamplerText;
         private ProgramStore mPSBackground;
@@ -417,14 +428,13 @@
         }
 
         class State extends BaseAlloc {
+            public float newPositionX;
+            public int newTouchDown;
+            public float readPosX;
+            public float readVel;
+            public float flingVelocityX;
             public int iconCount;
             public int scrollX;
-            public int flingTimeMs;
-            public float flingVelocityX;
-            public int currentScrollX;
-            public int flingDuration;
-            public int flingEndPos;
-            public int startScrollX;
             public int selectedIconIndex = -1;
             public int selectedIconTexture;
             public int visible;
@@ -564,6 +574,8 @@
             sb.addDefines(Defines.class);
             sb.setType(mParams.mType, "params", Defines.ALLOC_PARAMS);
             sb.setType(mState.mType, "state", Defines.ALLOC_STATE);
+            mInvokeMove = sb.addInvokable("move");
+            mInvokeFling = sb.addInvokable("fling");
             mScript = sb.create();
             mScript.setClearColor(0.0f, 0.0f, 0.0f, 0.0f);
 
@@ -653,7 +665,9 @@
             mAllocTouchXBorders.data(mTouchXBorders);
         }
 
-        int chooseTappedIcon(int x, int y, int scrollX, int currentPage) {
+        int chooseTappedIcon(int x, int y, float page) {
+            int currentPage = (int)page;
+
             int col = -1;
             int row = -1;
 
@@ -681,8 +695,8 @@
         /**
          * You need to call save() on mState on your own after calling this.
          */
-        void selectIcon(int x, int y, int scrollX, int currentPage) {
-            int index = chooseTappedIcon(x, y, scrollX, currentPage);
+        void selectIcon(int x, int y, float pos) {
+            int index = chooseTappedIcon(x, y, pos);
             selectIcon(index);
         }