Add the all apps button.
diff --git a/res/drawable-hdpi/all_apps_button.png b/res/drawable-hdpi/all_apps_button.png
new file mode 100644
index 0000000..945bf93
--- /dev/null
+++ b/res/drawable-hdpi/all_apps_button.png
Binary files differ
diff --git a/res/drawable-hdpi/all_apps_button_pow2.png b/res/drawable-hdpi/all_apps_button_pow2.png
new file mode 100644
index 0000000..ab93e94
--- /dev/null
+++ b/res/drawable-hdpi/all_apps_button_pow2.png
Binary files differ
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index d57d3a4..5b5c37c 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -49,21 +49,28 @@
 
     </com.android.launcher2.Workspace>
 
-    <com.android.launcher2.HandleView
-        android:id="@+id/all_apps_button"
+    <LinearLayout
+        android:id="@+id/buttons"
         android:layout_width="fill_parent"
         android:layout_height="56dip"
         android:layout_gravity="bottom"
 
-        android:background="@drawable/handle"
+        android:gravity="right"
+        >
 
-        android:focusable="true"
-        android:clickable="true"
+        <com.android.launcher2.HandleView
+            android:id="@+id/all_apps_button"
+            android:layout_width="@dimen/button_bar_height"
+            android:layout_height="@dimen/button_bar_height"
 
-        android:scaleType="center"
-        android:src="@drawable/handle_icon"
+            android:focusable="true"
+            android:clickable="true"
 
-        launcher:direction="horizontal" />
+            android:scaleType="center"
+            android:src="@drawable/all_apps_button"
+
+            launcher:direction="horizontal" />
+    </LinearLayout>
 
     <com.android.launcher2.DeleteZone
         android:id="@+id/delete_zone"
diff --git a/res/raw/rollo.c b/res/raw/rollo.c
index 216d761..f6097cf 100644
--- a/res/raw/rollo.c
+++ b/res/raw/rollo.c
@@ -12,6 +12,9 @@
 #define PARAM_BUBBLE_HEIGHT             1
 #define PARAM_BUBBLE_BITMAP_WIDTH       2
 #define PARAM_BUBBLE_BITMAP_HEIGHT      3
+#define PARAM_SCROLL_HANDLE_ID          4
+#define PARAM_SCROLL_HANDLE_TEX_WIDTH   5
+#define PARAM_SCROLL_HANDLE_TEX_HEIGHT  6
 
 // State ======
 #define STATE_ICON_COUNT                0
@@ -26,14 +29,14 @@
 #define STATE_FLING_DURATION            6
 #define STATE_FLING_END_POS             7
 
+#define SCROLL_HANDLE_POS               8
+
 // Scratch variables ======
 #define SCRATCH_ADJUSTED_DECELERATION   0
 
 // Drawing constants, should be parameters ======
 #define SCREEN_WIDTH_PX 480
 #define SCREEN_HEIGHT 854
-#define COLUMNS_PER_PAGE 4
-#define ROWS_PER_PAGE 4
 
 #define PAGE_PADDING_TOP_PX 80
 #define CELL_PADDING_TOP_PX 5
@@ -169,15 +172,15 @@
     float iconLabelGutter = ICON_LABEL_GUTTER_PX * densityScale;
 
     float scrollXPx = loadI32(ALLOC_STATE, STATE_SCROLL_X);
-    float maxScrollX = -(pageCount-1) * SCREEN_WIDTH_PX;
+    float maxScrollXPx = -(pageCount-1) * SCREEN_WIDTH_PX;
     int done = 0;
 
     // Clamp -- because java doesn't know how big the icons are
     if (scrollXPx > 0) {
         scrollXPx = 0;
     }
-    if (scrollXPx < maxScrollX) {
-        scrollXPx = maxScrollX;
+    if (scrollXPx < maxScrollXPx) {
+        scrollXPx = maxScrollXPx;
     }
 
     // If we've been given a velocity, start a fling
@@ -217,8 +220,8 @@
             if (endPos > 0) {
                 endPos = 0;
             }
-            if (endPos < maxScrollX) {
-                endPos = maxScrollX;
+            if (endPos < maxScrollXPx) {
+                endPos = maxScrollXPx;
             }
             float scrollOnPage = modf(endPos, SCREEN_WIDTH_PX);
             int endPage = -endPos/SCREEN_WIDTH_PX;
@@ -279,8 +282,8 @@
     if (scrollXPx > 0) {
         scrollXPx = 0;
     }
-    if (scrollXPx < maxScrollX) {
-        scrollXPx = maxScrollX;
+    if (scrollXPx < maxScrollXPx) {
+        scrollXPx = maxScrollXPx;
     }
     
     storeI32(ALLOC_STATE, STATE_CURRENT_SCROLL_X, scrollXPx);
@@ -290,6 +293,8 @@
         storeF(ALLOC_STATE, STATE_ADJUSTED_DECELERATION, 0);
     }
 
+    // Draw the icons ========================================
+    bindProgramVertex(NAMED_PV);
     bindProgramFragment(NAMED_PF);
     bindProgramFragmentStore(NAMED_PFS);
 
@@ -307,6 +312,20 @@
 
     draw_page(icon, lastIcon, -VIEW_ANGLE*currentPagePosition);
     draw_page(icon+iconsPerPage, lastIcon, (-VIEW_ANGLE*currentPagePosition)+VIEW_ANGLE);
+    
+    // Draw the scroll handle ========================================
+    /*
+    bindProgramVertex(NAMED_PVOrtho);
+    bindProgramFragment(NAMED_PFText);
+    bindProgramFragmentStore(NAMED_PFSText);
+
+    bindTexture(NAMED_PFText, 0, loadI32(ALLOC_PARAMS, PARAM_SCROLL_HANDLE_ID));
+    float handleLeft = 40 + (320 * (scrollXPx/(float)(maxScrollXPx)));
+    float handleTop = 680;
+    float handleWidth = loadI32(ALLOC_PARAMS, PARAM_SCROLL_HANDLE_TEX_WIDTH);
+    float handleHeight = loadI32(ALLOC_PARAMS, PARAM_SCROLL_HANDLE_TEX_HEIGHT);
+    drawRect(handleLeft, handleTop, handleLeft+handleWidth, handleTop+handleHeight, 0.0f);
+    */
 
     return !done;
 }
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index b802353..e0ca384 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -18,4 +18,5 @@
     <dimen name="search_widget_inset">19dip</dimen>
     <dimen name="gesture_thumbnail_inset">8dip</dimen>
     <dimen name="gesture_thumbnail_size">64dip</dimen>
+    <dimen name="button_bar_height">56dip</dimen>
 </resources>
diff --git a/src/com/android/launcher2/AllAppsView.java b/src/com/android/launcher2/AllAppsView.java
index cd66b4f..d8ac50b 100644
--- a/src/com/android/launcher2/AllAppsView.java
+++ b/src/com/android/launcher2/AllAppsView.java
@@ -66,11 +66,25 @@
     private ArrayList<ApplicationInfo> mAllAppsList;
 
     private ViewConfiguration mConfig;
+    private int mPageCount;
     private VelocityTracker mVelocity;
     private int mLastScrollX;
     private int mLastMotionX;
     private ApplicationsAdapter mAdapter;
+    private TouchHandler mTouchHandler;
+    private int mScrollHandleTop;
 
+    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;
+
+        public static final int COLUMNS_PER_PAGE = 4;
+        public static final int ROWS_PER_PAGE = 4;
+        
+    }
 
     public AllAppsView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -103,6 +117,10 @@
         if (mAllAppsList != null) {
             mRollo.setApps(mAllAppsList);
         }
+
+        Resources res = getContext().getResources();
+        int barHeight = (int)res.getDimension(R.dimen.button_bar_height);
+        mScrollHandleTop = h - barHeight;
     }
 
     @Override
@@ -115,44 +133,89 @@
     @Override
     public boolean onTouchEvent(MotionEvent ev)
     {
-        int x = (int)ev.getX();
-        int deltaX;
-        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;
-                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;
+        mTouchHandler = mFlingHandler;
+        /*
+        int action = ev.getAction();
+        if (action == MotionEvent.ACTION_DOWN) {
+            if (ev.getY() > mScrollHandleTop) {
+                mTouchHandler = mScrollHandler;
+            } else {
+                mTouchHandler = mFlingHandler;
+            }
         }
-        return true;
+        */
+        return mTouchHandler.onTouchEvent(ev);
     }
 
+    private abstract class TouchHandler {
+        abstract boolean onTouchEvent(MotionEvent ev);
+    };
+
+    private TouchHandler mFlingHandler = new TouchHandler() {
+        @Override
+        public boolean onTouchEvent(MotionEvent ev)
+        {
+            int x = (int)ev.getX();
+            int deltaX;
+            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;
+                    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;
+        }
+    };
+
+    /*
+    private TouchHandler mScrollHandler = new TouchHandler() {
+        @Override
+        public boolean onTouchEvent(MotionEvent ev)
+        {
+            int x = (int)ev.getX();
+            int w = getWidth();
+
+            float percent = x / (float)w;
+
+            mRollo.mState.read();
+
+            mRollo.mState.scrollX = mLastScrollX = -(int)(mPageCount * w * percent);
+            mRollo.mState.flingVelocityX = 0;
+            mRollo.mState.adjustedDeceleration = 0;
+            mRollo.mState.save();
+
+            return true;
+        }
+    };
+    */
+
     @Override
     public boolean onTrackballEvent(MotionEvent ev)
     {
@@ -177,6 +240,16 @@
         if (mRollo != null) {
             mRollo.setApps(list);
         }
+        mPageCount = countPages(list.size());
+    }
+
+    private static int countPages(int iconCount) {
+        int iconsPerPage = Defines.COLUMNS_PER_PAGE * Defines.ROWS_PER_PAGE;
+        int pages = iconCount / iconsPerPage;
+        if (pages*iconsPerPage != iconCount) {
+            pages++;
+        }
+        return pages;
     }
 
     public class RolloRS {
@@ -200,6 +273,8 @@
         private ProgramVertex mPVOrtho;
         private ProgramVertex.MatrixAllocation mPVOrthoAlloc;
 
+        private Allocation mScrollHandle;
+
         private Allocation[] mIcons;
         private int[] mAllocIconIDBuf;
         private Allocation mAllocIconID;
@@ -214,14 +289,6 @@
         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);
@@ -230,6 +297,9 @@
             @AllocationIndex(1) public int bubbleHeight;
             @AllocationIndex(2) public int bubbleBitmapWidth;
             @AllocationIndex(3) public int bubbleBitmapHeight;
+            @AllocationIndex(4) public int scrollHandleId;
+            @AllocationIndex(5) public int scrollHandleTextureWidth;
+            @AllocationIndex(6) public int scrollHandleTextureHeight;
         }
 
         class State extends IntAllocation {
@@ -244,6 +314,7 @@
             @AllocationIndex(5) public int currentScrollX;
             @AllocationIndex(6) public int flingDuration;
             @AllocationIndex(7) public int flingEndPos;
+            @AllocationIndex(8) public int scrollHandlePos;
         }
 
         public RolloRS() {
@@ -338,19 +409,21 @@
             mParams.bubbleBitmapWidth = bubble.getBitmapWidth();
             mParams.bubbleBitmapHeight = bubble.getBitmapHeight();
 
+            mScrollHandle = Allocation.createFromBitmapResource(mRS, mRes,
+                    R.drawable.all_apps_button_pow2, Element.RGBA_8888, true);
+            mScrollHandle.uploadToTexture(0);
+            mParams.scrollHandleId = mScrollHandle.getID();
+            Log.d(TAG, "mParams.scrollHandleId=" + mParams.scrollHandleId);
+            mParams.scrollHandleTextureWidth = 128;
+            mParams.scrollHandleTextureHeight = 128;
+            mState.scrollHandlePos = 0;
+
             mParams.save();
             mState.save();
 
             setApps(null);
         }
 
-        Allocation makeTextBitmap(Utilities.BubbleText bubble, String label) {
-            Bitmap b = bubble.createTextBitmap(label);
-            Allocation a = Allocation.createFromBitmap(mRS, b, Element.RGBA_8888, true);
-            b.recycle();
-            return a;
-        }
-
         private void initRs() {
             ScriptC.Builder sb = new ScriptC.Builder(mRS);
             sb.setScript(mRes, R.raw.rollo);
@@ -415,4 +488,3 @@
 }
 
 
-
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index 40f4b39..e1b213e 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -528,8 +528,10 @@
         mHandleView = (HandleView) findViewById(R.id.all_apps_button);
         mHandleView.setLauncher(this);
         mHandleView.setOnClickListener(this);
+        /* TODO
         TransitionDrawable handleIcon = (TransitionDrawable) mHandleView.getDrawable();
-        handleIcon.setCrossFadeEnabled(true);
+        handleIocon.setCrossFadeEnabled(true);
+        */
 
         workspace.setOnLongClickListener(this);
         workspace.setDragController(dragController);
diff --git a/src/com/android/launcher2/Utilities.java b/src/com/android/launcher2/Utilities.java
index 0c5dbbd..ca12a4d 100644
--- a/src/com/android/launcher2/Utilities.java
+++ b/src/com/android/launcher2/Utilities.java
@@ -274,6 +274,7 @@
             textPaint.setTextSize(13*scale);
             textPaint.setColor(0xffffffff);
             textPaint.setAntiAlias(true);
+            //textPaint.setShadowLayer(8, 0, 0, 0xff000000);
 
             float ascent = -textPaint.ascent();
             float descent = textPaint.descent();