Add the animation for when you enter the all apps view.
diff --git a/src/com/android/launcher2/AllAppsView.java b/src/com/android/launcher2/AllAppsView.java
index dcaaaa1..5128db3 100644
--- a/src/com/android/launcher2/AllAppsView.java
+++ b/src/com/android/launcher2/AllAppsView.java
@@ -66,6 +66,7 @@
 
     private Launcher mLauncher;
     private DragController mDragController;
+    private boolean mLocked = true;
 
     private RenderScript mRS;
     private RolloRS mRollo;
@@ -163,12 +164,16 @@
     @Override
     public boolean onTouchEvent(MotionEvent ev)
     {
-        super.onTouchEvent(ev);
-
         if (mRollo.mState.visible == 0) {
-            return false;
+            return true;
         }
 
+        if (mLocked) {
+            return true;
+        }
+
+        super.onTouchEvent(ev);
+
         mTouchHandler = mFlingHandler;
         /*
         int action = ev.getAction();
@@ -287,15 +292,31 @@
     public void onDropCompleted(View target, boolean success) {
     }
 
+    private static final int SCALE_SCALE = 100000;
+
     public void show() {
         mRollo.mState.read();
         mRollo.mState.visible = 1;
+        mRollo.mState.zoom = SCALE_SCALE;
         mRollo.mState.save();
     }
 
-    public void hide(boolean animate) {
+    public void setScale(float amount) {
+        mRollo.mState.read();
+        if (amount > 0.001f) {
+            mRollo.mState.visible = 1;
+            mRollo.mState.zoom = (int)(SCALE_SCALE*amount);
+        } else {
+            mRollo.mState.visible = 0;
+            mRollo.mState.zoom = 0;
+        }
+        mRollo.mState.save();
+    }
+
+    public void hide() {
         mRollo.mState.read();
         mRollo.mState.visible = 0;
+        mRollo.mState.zoom = 0;
         mRollo.mState.save();
     }
 
@@ -341,6 +362,7 @@
         }
         mPageCount = countPages(list.size());
         Log.d(TAG, "setApps mRollo=" + mRollo + " list=" + list);
+        mLocked = false;
     }
 
     private void invokeIcon(int index) {
@@ -427,6 +449,7 @@
             @AllocationIndex(9) public int selectedIconIndex = -1;
             @AllocationIndex(10) public int selectedIconTexture;
             @AllocationIndex(11) public int visible;
+            @AllocationIndex(12) public int zoom;
         }
 
         public RolloRS() {
diff --git a/src/com/android/launcher2/DragLayer.java b/src/com/android/launcher2/DragLayer.java
index 2839711..f038c2a 100644
--- a/src/com/android/launcher2/DragLayer.java
+++ b/src/com/android/launcher2/DragLayer.java
@@ -40,8 +40,17 @@
  * A ViewGroup that coordinated dragging across its dscendants
  */
 public class DragLayer extends FrameLayout {
+    private static final String TAG = "Launcher.DragLayer";
+
+    private static final int DRAG = 1;
+    private static final int SWIPE = 2;
+    private static final int BOTH = DRAG | SWIPE;
 
     DragController mDragController;
+    SwipeController mSwipeController;
+
+    private int mAllowed = BOTH;
+
 
     /**
      * Used to create a new DragLayer from XML.
@@ -57,6 +66,10 @@
         mDragController = controller;
     }
     
+    public void setSwipeController(SwipeController controller) {
+        mSwipeController = controller;
+    }
+    
     @Override
     public boolean dispatchKeyEvent(KeyEvent event) {
         return mDragController.dispatchKeyEvent(event) || super.dispatchKeyEvent(event);
@@ -64,11 +77,47 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        return mDragController.onInterceptTouchEvent(ev);
+        boolean result = false;
+
+        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            mAllowed = BOTH;
+        }
+
+        if ((mAllowed & DRAG) != 0) {
+            result = mDragController.onInterceptTouchEvent(ev);
+            if (result) {
+                mAllowed = DRAG;
+            }
+        }
+
+        if ((mAllowed & SWIPE) != 0) {
+            result = mSwipeController.onInterceptTouchEvent(ev);
+            if (result) {
+                mAllowed = SWIPE;
+            }
+        }
+
+        return result;
     }
 
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
-        return mDragController.onTouchEvent(ev);
+        boolean result = false;
+
+        if ((mAllowed & DRAG) != 0) {
+            result = mDragController.onTouchEvent(ev);
+            if (result) {
+                mAllowed = DRAG;
+            }
+        }
+
+        if ((mAllowed & SWIPE) != 0) {
+            result = mSwipeController.onTouchEvent(ev);
+            if (result) {
+                mAllowed = SWIPE;
+            }
+        }
+
+        return result;
     }
 }
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index b53fee2..86914b0 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -82,7 +82,8 @@
  * Default launcher application.
  */
 public final class Launcher extends Activity
-        implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks {
+        implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
+        SwipeController.SwipeListener {
     static final String LOG_TAG = "Launcher";
     static final String TAG = LOG_TAG;
     static final boolean LOGD = false;
@@ -108,6 +109,10 @@
     private static final int REQUEST_PICK_LIVE_FOLDER = 8;
     private static final int REQUEST_PICK_APPWIDGET = 9;
 
+    private static final int MODE_WORKSPACE = 0;
+    private static final int MODE_ALL_APPS = 1;
+    private static final int MODE_ALL_APPS_ZOOMED = 2;
+
     static final String EXTRA_SHORTCUT_DUPLICATE = "duplicate";
 
     static final String EXTRA_CUSTOM_WIDGET = "custom_widget";
@@ -158,6 +163,7 @@
     private LayoutInflater mInflater;
 
     private DragController mDragController;
+    private SwipeController mSwipeController;
     private Workspace mWorkspace;
 
     private AppWidgetManager mAppWidgetManager;
@@ -171,7 +177,8 @@
     private DeleteZone mDeleteZone;
     private HandleView mHandleView;
     private AllAppsView mAllAppsGrid;
-    private boolean mAllAppsVisible;
+    private boolean mAllAppsVisible; // if it's visible at all
+    private int mMode = MODE_WORKSPACE;
 
     private Bundle mSavedState;
 
@@ -515,13 +522,18 @@
     private void setupViews() {
         mDragController = new DragController(this);
         DragController dragController = mDragController;
+        mSwipeController = new SwipeController(this, this);
+        SwipeController swipeController = mSwipeController;
+        swipeController.setRange(-1, 0);
 
         DragLayer dragLayer = (DragLayer) findViewById(R.id.drag_layer);
         dragLayer.setDragController(dragController);
+        dragLayer.setSwipeController(swipeController);
 
         mAllAppsGrid = (AllAppsView)dragLayer.findViewById(R.id.all_apps_view);
         mAllAppsGrid.setLauncher(this);
         mAllAppsGrid.setDragController(dragController);
+        mAllAppsGrid.setWillNotDraw(false); // We don't want a hole punched in our window.
 
         mWorkspace = (Workspace) dragLayer.findViewById(R.id.workspace);
         final Workspace workspace = mWorkspace;
@@ -1422,6 +1434,7 @@
         }
 
         if (mWorkspace.allowLongPress()) {
+            mSwipeController.cancelSwipe();
             if (cellInfo.cell == null) {
                 if (cellInfo.valid) {
                     // User long pressed on empty space
@@ -1607,7 +1620,7 @@
     void closeAllAppsDialog(boolean animated) {
         if (mAllAppsVisible) {
             Log.d(LOG_TAG, "closing all apps");
-            mAllAppsGrid.hide(animated);
+            mAllAppsGrid.hide();
             mAllAppsVisible = false;
             mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus();
             mWorkspace.show();
@@ -1744,6 +1757,89 @@
     }
 
     /**
+     * Implementation of the method from SwipeController.SwipeListener.
+     */
+    public void onStartSwipe() {
+        switch (mMode) {
+        case MODE_WORKSPACE:
+            mWorkspace.enableChildrenCache();
+            break;
+        case MODE_ALL_APPS:
+            break;
+        case MODE_ALL_APPS_ZOOMED:
+            break;
+        }
+    }
+
+    /**
+     * Implementation of the method from SwipeController.SwipeListener.
+     *
+     * @param amount The final value of the swipe (-1, 0 or 1)
+     */
+    public void onFinishSwipe(int amount) {
+        switch (mMode) {
+        case MODE_WORKSPACE:
+            if (amount == -1) {
+                mWorkspace.clearChildrenCache();
+                mMode = MODE_ALL_APPS;
+                mSwipeController.setRange(0, 1);
+            }
+            break;
+        case MODE_ALL_APPS:
+            if (amount == 1) {
+                mWorkspace.clearChildrenCache();
+                mMode = MODE_WORKSPACE;
+                mSwipeController.setRange(-1, 0);
+            }
+            break;
+        case MODE_ALL_APPS_ZOOMED:
+            break;
+        }
+    }
+
+    /**
+     * Implementation of the method from SwipeController.SwipeListener.
+     */
+    public void onSwipe(float amount) {
+        switch (mMode) {
+        case MODE_WORKSPACE:
+            // We can open the all apps view.
+            //   0 == workspace is showing
+            //  -1 == all apps is showing
+            setWorkspaceAndAllAppsScale(-amount);
+            break;
+        case MODE_ALL_APPS:
+            // We can close it, or (someday) zoom it further
+            //   0 == all apps showing
+            //   1 == workspace is showing
+            setWorkspaceAndAllAppsScale(1-amount);
+            break;
+        }
+    }
+
+    /**
+     * Set the scale factor for the workspace and the all apps grid.
+     *
+     * @param amount A float between 0 and 1, where:
+     *                  0 == workspace is showing and
+     *                  1 == the all apps grid is showing.
+     */
+    private void setWorkspaceAndAllAppsScale(float amount) {
+        //Log.d("setWorkspaceAndAllAppsScale", "setWorkspaceAndAllAppsScale amount=" + amount);
+        if (amount < 0.001f) {
+            amount = 0.0f;
+        }
+        if (amount > 0.999f) {
+            amount = 1.0f;
+            mWorkspace.setVisibility(View.INVISIBLE);
+        } else {
+            mWorkspace.setVisibility(View.VISIBLE);
+        }
+        mWorkspace.setScale(1-amount);
+        mAllAppsGrid.setScale(amount);
+    }
+
+    /**
      * Implementation of the method from LauncherModel.Callbacks.
      */
     public int getCurrentWorkspaceScreen() {
diff --git a/src/com/android/launcher2/SwipeController.java b/src/com/android/launcher2/SwipeController.java
new file mode 100644
index 0000000..7443b3b
--- /dev/null
+++ b/src/com/android/launcher2/SwipeController.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2008 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 com.android.launcher2;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.DisplayMetrics;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.ViewConfiguration;
+
+import java.util.ArrayList;
+
+public class SwipeController {
+    private static final String TAG = "Launcher.SwipeController";
+
+    private static final int FRAME_DELAY = 1000 / 30;
+    private static final float DECAY_CONSTANT = 0.65f;
+    private static final float SPRING_CONSTANT = 0.0009f;
+
+    // configuration
+    private SwipeListener mListener;
+    private int mSlop;
+    private float mSwipeDistance;
+
+    // state
+    private VelocityTracker mVelocityTracker;
+    private boolean mCanceled;
+    private boolean mTracking;
+    private int mDownX;
+    private int mDownY;
+
+    private float mMinDest;
+    private float mMaxDest;
+    private long mFlingTime;
+    private long mLastTime;
+    private int mDirection;
+    private float mVelocity;
+    private float mDest;
+    private float mAmount;
+
+    public interface SwipeListener {
+        public void onStartSwipe();
+        public void onFinishSwipe(int amount);
+        public void onSwipe(float amount);
+    }
+
+    public SwipeController(Context context, SwipeListener listener) {
+        ViewConfiguration config = ViewConfiguration.get(context);
+        mSlop = config.getScaledTouchSlop();
+        
+        DisplayMetrics display = context.getResources().getDisplayMetrics();
+        mSwipeDistance = display.heightPixels / 2; // one half of the screen
+
+        mListener = listener;
+    }
+
+    public void setRange(float min, float max) {
+        mMinDest = min;
+        mMaxDest = max;
+    }
+
+    public void cancelSwipe() {
+        mCanceled = true;
+    }
+
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        onTouchEvent(ev);
+
+        // After we return true, onIntercept doesn't get called any more, so this is
+        // a good place to do the callback.
+        if (mTracking) {
+            mListener.onStartSwipe();
+        }
+
+        return mTracking;
+    }
+
+    public boolean onTouchEvent(MotionEvent ev) {
+        if (mVelocityTracker == null) {
+            mVelocityTracker = VelocityTracker.obtain();
+        }
+        mVelocityTracker.addMovement(ev);
+
+        final int screenX = (int)ev.getRawX();
+        final int screenY = (int)ev.getRawY();
+
+        final int deltaX = screenX - mDownX;
+        final int deltaY = screenY - mDownY;
+
+        final int action = ev.getAction();
+        switch (action) {
+        case MotionEvent.ACTION_DOWN:
+            // Remember location of down touch
+            mCanceled = false;
+            mTracking = false;
+            mDownX = screenX;
+            mDownY = screenY;
+            break;
+
+        case MotionEvent.ACTION_MOVE:
+            if (!mCanceled && !mTracking) {
+                if (Math.abs(deltaX) > mSlop) {
+                    mCanceled = true;
+                    mTracking = false;
+                }
+                if (Math.abs(deltaY) > mSlop) {
+                    mTracking = true;
+                }
+            }
+            if (mTracking && !mCanceled) {
+                track(screenY);
+            }
+            break;
+
+        case MotionEvent.ACTION_CANCEL:
+        case MotionEvent.ACTION_UP:
+            if (mTracking && !mCanceled) {
+                fling(screenY);
+            }
+            mVelocityTracker.recycle();
+            mVelocityTracker = null;
+            break;
+        }
+
+        return mTracking || mCanceled;
+    }
+
+    private float clamp(float v) {
+        if (v < mMinDest) {
+            return mMinDest;
+        } else if (v > mMaxDest) {
+            return mMaxDest;
+        } else {
+            return v;
+        }
+    }
+
+    /**
+     * Perform the callbacks.
+     */
+    private void track(int screenY) {
+        mAmount = clamp((screenY - mDownY) / mSwipeDistance);
+        mListener.onSwipe(mAmount);
+    }
+
+    private void fling(int screenY) {
+        mVelocityTracker.computeCurrentVelocity(1);
+
+        mVelocity = mVelocityTracker.getYVelocity() / mSwipeDistance;
+        mDirection = mVelocity >= 0.0f ? 1 : -1;
+        mAmount = clamp((screenY-mDownY)/mSwipeDistance);
+        if (mAmount < 0) {
+            mDest = clamp(mVelocity < 0 ? -1.0f : 0.0f);
+        } else {
+            mDest = clamp(mVelocity < 0 ? 0.0f : 1.0f);
+        }
+
+        mFlingTime = SystemClock.uptimeMillis();
+        mLastTime = 0;
+
+        scheduleAnim();
+    }
+
+    private void scheduleAnim() {
+        boolean send = true;
+        if (mDirection > 0) {
+            if (mAmount > (mDest - 0.01f)) {
+                send = false;
+            }
+        } else {
+            if (mAmount < (mDest + 0.01f)) {
+                send = false;
+            }
+        }
+        if (send) {
+            mHandler.sendEmptyMessageDelayed(1, FRAME_DELAY);
+        } else {
+            mListener.onFinishSwipe((int)(mAmount >= 0 ? (mAmount+0.5f) : (mAmount-0.5f)));
+        }
+    }
+
+    Handler mHandler = new Handler() {
+        public void handleMessage(Message msg) {
+            long now = SystemClock.uptimeMillis();
+
+            final long t = now - mFlingTime;
+            final long dt = t - mLastTime;
+            mLastTime = t;
+            final float timeSlices = dt / (float)FRAME_DELAY;
+
+            float distance = mDest - mAmount;
+
+            mVelocity += timeSlices * mDirection * SPRING_CONSTANT * distance * distance / 2;
+            mVelocity *= (timeSlices * DECAY_CONSTANT);
+
+            mAmount += timeSlices * mVelocity;
+            mAmount += distance * timeSlices * 0.2f; // cheat
+
+            mListener.onSwipe(mAmount);
+            scheduleAnim();
+        }
+    };
+}
+
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index 744cf72..14b77f0 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -46,8 +46,9 @@
  * screen contains a number of icons, folders or widgets the user can interact with.
  * A workspace is meant to be used with a fixed width only.
  */
-public class Workspace extends ViewGroup implements DropTarget, DragSource, DragScroller,
-        TweenCallback {
+public class Workspace extends ViewGroup implements DropTarget, DragSource, DragScroller {
+    private static final String TAG = "Launcher.Workspace";
+
     private static final int INVALID_SCREEN = -1;
     
     /**
@@ -66,9 +67,6 @@
     private Scroller mScroller;
     private VelocityTracker mVelocityTracker;
 
-    private SymmetricalLinearTween mTween;
-    private int mAlpha = 255;
-
     /**
      * CellInfo for the cell that is currently being dragged
      */
@@ -150,8 +148,6 @@
         final ViewConfiguration configuration = ViewConfiguration.get(getContext());
         mTouchSlop = configuration.getScaledTouchSlop();
         mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
-
-        mTween = new SymmetricalLinearTween(true, 250/*ms*/, this);
     }
 
     @Override
@@ -486,12 +482,24 @@
         int restoreCount = 0;
 
         // For the fade.  If view gets setAlpha(), use that instead.
-        int alpha = mAlpha;
-        if (alpha < 255) {
+        float scale = mScale;
+        if (scale < 0.999f) {
             int sx = mScrollX;
+
+            int alpha = (scale < 0.5f) ? (int)(255 * 2 * scale) : 255;
+
             restoreCount = canvas.saveLayerAlpha(sx, 0, sx+getWidth(), getHeight(), alpha,
                     Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
             restore = true;
+
+            if (scale < 0.999f) {
+                int w = getWidth();
+                w += 2 * mCurrentScreen * w;
+                int h = getHeight();
+                canvas.translate(w/2, h/2);
+                canvas.scale(scale, scale);
+                canvas.translate(-w/2, -h/2);
+            }
         }
 
         // ViewGroup.dispatchDraw() supports many features we don't need:
@@ -499,7 +507,8 @@
         // children, etc. The following implementation attempts to fast-track
         // the drawing dispatch by drawing only what we know needs to be drawn.
 
-        boolean fastDraw = mTouchState != TOUCH_STATE_SCROLLING && mNextScreen == INVALID_SCREEN;
+        boolean fastDraw = mTouchState != TOUCH_STATE_SCROLLING && mNextScreen == INVALID_SCREEN
+                && scale > 0.999f;
         // If we are not scrolling or flinging, draw only the current screen
         if (fastDraw) {
             drawChild(canvas, getChildAt(mCurrentScreen), getDrawingTime());
@@ -524,6 +533,12 @@
         }
     }
 
+    private float mScale = 1.0f;
+    public void setScale(float scale) {
+        mScale = scale;
+        invalidate();
+    }
+
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         mDragController.setWindowToken(getWindowToken());
@@ -1335,31 +1350,9 @@
     }
 
     void show() {
-        mTween.start(true);
         setVisibility(VISIBLE);
     }
 
     void hide() {
-        mTween.start(false);
-    }
-
-    public void onTweenValueChanged(float value, float oldValue) {
-        mAlpha = (int)(255*value);
-        invalidate();
-    }
-
-    public void onTweenStarted() {
-        // TODO: This conflicts with the cache for drawing.  Ref count instead?
-        // TODO: Don't cache all three.
-        enableChildrenCache();
-    }
-
-    public void onTweenFinished() {
-        // TODO: This conflicts with the cache for drawing.  Ref count instead?
-        // TODO: Don't cache all three.
-        clearChildrenCache();
-        if (mAlpha == 0) {
-            setVisibility(GONE);
-        }
     }
 }