Fade the workspace when the all apps drawer opens and closes.
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index 7e42b8d..c45acb3 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -441,7 +441,7 @@
     @Override
     protected void onPause() {
         super.onPause();
-        closeDrawer(false);
+        closeAllAppsDialog(false);
     }
 
     @Override
@@ -823,7 +823,7 @@
                     mWorkspace.moveToDefaultScreen();
                 }
 
-                closeDrawer();
+                closeAllAppsDialog(true);
 
                 final View v = getWindow().peekDecorView();
                 if (v != null && v.getWindowToken() != null) {
@@ -832,7 +832,7 @@
                     imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
                 }
             } else {
-                closeDrawer(false);
+                closeAllAppsDialog(false);
             }
         }
     }
@@ -922,7 +922,7 @@
     public void startSearch(String initialQuery, boolean selectInitialQuery,
             Bundle appSearchData, boolean globalSearch) {
 
-        closeDrawer(false);
+        closeAllAppsDialog(false);
 
         // Slide the search widget to the top, if it's on the current screen,
         // otherwise show the search dialog immediately.
@@ -1328,7 +1328,7 @@
                     if (!event.isCanceled()) {
                         mWorkspace.dispatchKeyEvent(event);
                         if (mAllAppsDialog.isOpen) {
-                            closeDrawer();
+                            closeAllAppsDialog(true);
                         } else {
                             closeFolder();
                         }
@@ -1344,24 +1344,6 @@
         return super.dispatchKeyEvent(event);
     }
 
-    private void closeDrawer() {
-        closeDrawer(true);
-    }
-
-    private void closeDrawer(boolean animated) {
-        if (mAllAppsDialog.isOpen) {
-            if (animated) {
-                // TODO mDrawer.animateClose();
-                mAllAppsDialog.dismiss();
-            } else {
-                mAllAppsDialog.dismiss();
-            }
-            if (false /* TODO mDrawer.hasFocus() */) {
-                mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus();
-            }
-        }
-    }
-
     private void closeFolder() {
         Folder folder = mWorkspace.getOpenFolder();
         if (folder != null) {
@@ -1785,11 +1767,6 @@
         }
     }
 
-    void showAllAppsDialog() {
-        mAllAppsDialog.isOpen = true;
-        showDialog(DIALOG_ALL_APPS);
-    }
-
     void showRenameDialog(FolderInfo info) {
         mFolderInfo = info;
         mWaitingForResult = true;
@@ -1935,7 +1912,6 @@
         }
         
         private void onDestroy() {
-            this.isOpen = false;
         }
 
         void lock() {
@@ -1945,6 +1921,36 @@
         void unlock() {
             // TODO
         }
+
+        @Override public boolean onKeyDown(int keyCode, KeyEvent event) {
+            switch (keyCode) {
+                case KeyEvent.KEYCODE_BACK:
+                    closeAllAppsDialog(true);
+                    return true;
+                default:
+                    return super.onKeyDown(keyCode, event);
+            }
+        }
+    }
+
+    void showAllAppsDialog() {
+        mAllAppsDialog.isOpen = true;
+        showDialog(DIALOG_ALL_APPS);
+        mWorkspace.hide();
+    }
+
+    private void closeAllAppsDialog(boolean animated) {
+        if (mAllAppsDialog.isOpen) {
+            if (animated) {
+                // TODO mDrawer.animateClose();
+                mAllAppsDialog.dismiss();
+            } else {
+                mAllAppsDialog.dismiss();
+            }
+            mAllAppsDialog.isOpen = false;
+            mWorkspace.getChildAt(mWorkspace.getCurrentScreen()).requestFocus();
+            mWorkspace.show();
+        }
     }
 
     /**
diff --git a/src/com/android/launcher2/SymmetricalLinearTween.java b/src/com/android/launcher2/SymmetricalLinearTween.java
new file mode 100644
index 0000000..2e0ed8f
--- /dev/null
+++ b/src/com/android/launcher2/SymmetricalLinearTween.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2009 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.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.util.Log;
+
+/**
+ * Provides an animation between 0.0f and 1.0f over a given duration.
+ */
+class SymmetricalLinearTween {
+
+    private static final int FPS = 30;
+    private static final int FRAME_TIME = 1000 / FPS;
+
+    Handler mHandler;
+    int mDuration;
+    TweenCallback mCallback;
+
+    boolean mRunning;
+    long mBase;
+    boolean mDirection;
+    float mValue;
+
+    /**
+     * @param duration milliseconds duration
+     * @param callback callbacks
+     */
+    public SymmetricalLinearTween(boolean initial, int duration, TweenCallback callback) {
+        mValue = initial ? 1.0f : 0.0f;
+        mDirection = initial;
+        mDuration = duration;
+        mCallback = callback;
+        mHandler = new Handler();
+    }
+
+    /**
+     * Starts the tweening.
+     *
+     * @param direction If direction is true, the value goes towards 1.0f.  If direction
+     *                  is false, the value goes towards 0.0f.
+     */
+    public void start(boolean direction) {
+        start(direction, SystemClock.uptimeMillis());
+    }
+
+    /**
+     * Starts the tweening.
+     *
+     * @param direction If direction is true, the value goes towards 1.0f.  If direction
+     *                  is false, the value goes towards 0.0f.
+     * @param baseTime  The time to use as zero for this animation, in the
+     *                  {@link SystemClock.uptimeMillis} time base.  This allows you to
+     *                  synchronize multiple animations.
+     */
+    public void start(boolean direction, long baseTime) {
+        if (direction != mDirection) {
+            if (!mRunning) {
+                mBase = baseTime;
+                mRunning = true;
+                mCallback.onTweenStarted();
+                long next = SystemClock.uptimeMillis() + FRAME_TIME;
+                mHandler.postAtTime(mTick, next);
+            } else {
+                // reverse direction
+                long now = SystemClock.uptimeMillis();
+                long diff = now - mBase;
+                mBase = now + diff - mDuration;
+            }
+            mDirection = direction;
+        }
+    }
+
+    Runnable mTick = new Runnable() {
+        public void run() {
+            long base = mBase;
+            long now = SystemClock.uptimeMillis();
+            long diff = now-base;
+            int duration = mDuration;
+            float val = diff/(float)duration;
+            if (!mDirection) {
+                val = 1.0f - val;
+            }
+            if (val > 1.0f) {
+                val = 1.0f;
+            } else if (val < 0.0f) {
+                val = 0.0f;
+            }
+            float old = mValue;
+            mValue = val;
+            mCallback.onTweenValueChanged(val, old);
+            int frame = (int)(diff / FRAME_TIME);
+            long next = base + ((frame+1)*FRAME_TIME);
+            if (diff < duration) {
+                mHandler.postAtTime(this, next);
+            }
+            if (diff >= duration) {
+                mCallback.onTweenFinished();
+                mRunning = false;
+            }
+        }
+    };
+}
+
diff --git a/src/com/android/launcher2/TweenCallback.java b/src/com/android/launcher2/TweenCallback.java
new file mode 100644
index 0000000..380a217
--- /dev/null
+++ b/src/com/android/launcher2/TweenCallback.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2009 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;
+
+interface TweenCallback {
+    void onTweenValueChanged(float value, float oldValue);
+    void onTweenStarted();
+    void onTweenFinished();
+}
+
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index cdb8411..43a6f78 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -47,7 +47,8 @@
  * 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 {
+public class Workspace extends ViewGroup implements DropTarget, DragSource, DragScroller,
+        TweenCallback {
     private static final int INVALID_SCREEN = -1;
     
     /**
@@ -65,6 +66,9 @@
     private Scroller mScroller;
     private VelocityTracker mVelocityTracker;
 
+    private SymmetricalLinearTween mTween;
+    private int mAlpha = 255;
+
     /**
      * CellInfo for the cell that is currently being dragged
      */
@@ -145,6 +149,8 @@
         final ViewConfiguration configuration = ViewConfiguration.get(getContext());
         mTouchSlop = configuration.getScaledTouchSlop();
         mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
+
+        mTween = new SymmetricalLinearTween(true, 250/*ms*/, this);
     }
 
     void setWallpaper(View wallpaper) {
@@ -465,6 +471,16 @@
     @Override
     protected void dispatchDraw(Canvas canvas) {
         boolean restore = false;
+        int restoreCount = 0;
+
+        // For the fade.  If view gets setAlpha(), use that instead.
+        int alpha = mAlpha;
+        if (alpha < 255) {
+            int sx = mScrollX;
+            restoreCount = canvas.saveLayerAlpha(sx, 0, sx+getWidth(), getHeight(), alpha,
+                    Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
+            restore = true;
+        }
 
         // ViewGroup.dispatchDraw() supports many features we don't need:
         // clip to padding, layout animation, animation listener, disappearing
@@ -492,7 +508,7 @@
         }
 
         if (restore) {
-            canvas.restore();
+            canvas.restoreToCount(restoreCount);
         }
     }
 
@@ -1309,4 +1325,29 @@
             }
         };
     }
+
+    void show() {
+        mTween.start(true);
+    }
+
+    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();
+    }
 }