Merge "Make sure crop rect information is visible from java." into jb-dev
diff --git a/api/current.txt b/api/current.txt
index b10c107..4b10577 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3722,10 +3722,6 @@
     field public static final int FLAG_ONGOING_EVENT = 2; // 0x2
     field public static final int FLAG_ONLY_ALERT_ONCE = 8; // 0x8
     field public static final int FLAG_SHOW_LIGHTS = 1; // 0x1
-    field public static final java.lang.String KIND_CALL = "android.call";
-    field public static final java.lang.String KIND_EMAIL = "android.email";
-    field public static final java.lang.String KIND_EVENT = "android.event";
-    field public static final java.lang.String KIND_MESSAGE = "android.message";
     field public static final java.lang.String KIND_PROMO = "android.promo";
     field public static final int PRIORITY_DEFAULT = 0; // 0x0
     field public static final int PRIORITY_HIGH = 1; // 0x1
@@ -3743,7 +3739,6 @@
     field public android.app.PendingIntent fullScreenIntent;
     field public int icon;
     field public int iconLevel;
-    field public java.lang.String[] kind;
     field public android.graphics.Bitmap largeIcon;
     field public int ledARGB;
     field public int ledOffMS;
@@ -3778,7 +3773,6 @@
   public static class Notification.Builder {
     ctor public Notification.Builder(android.content.Context);
     method public android.app.Notification.Builder addAction(int, java.lang.CharSequence, android.app.PendingIntent);
-    method public android.app.Notification.Builder addKind(java.lang.String);
     method public android.app.Notification build();
     method public deprecated android.app.Notification getNotification();
     method public android.app.Notification.Builder setAutoCancel(boolean);
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 3d0b7d8..523a78d 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -98,6 +98,8 @@
     public static final int ANIM_SCALE_UP = 2;
     /** @hide */
     public static final int ANIM_THUMBNAIL = 3;
+    /** @hide */
+    public static final int ANIM_THUMBNAIL_DELAYED = 4;
 
     private String mPackageName;
     private int mAnimationType = ANIM_NONE;
@@ -219,9 +221,38 @@
      */
     public static ActivityOptions makeThumbnailScaleUpAnimation(View source,
             Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) {
+        return makeThumbnailScaleUpAnimation(source, thumbnail, startX, startY, listener, false);
+    }
+
+    /**
+     * Create an ActivityOptions specifying an animation where a thumbnail
+     * is scaled from a given position to the new activity window that is
+     * being started. Before the animation, there is a short delay.
+     *
+     * @param source The View that this thumbnail is animating from.  This
+     * defines the coordinate space for <var>startX</var> and <var>startY</var>.
+     * @param thumbnail The bitmap that will be shown as the initial thumbnail
+     * of the animation.
+     * @param startX The x starting location of the bitmap, relative to <var>source</var>.
+     * @param startY The y starting location of the bitmap, relative to <var>source</var>.
+     * @param listener Optional OnAnimationStartedListener to find out when the
+     * requested animation has started running.  If for some reason the animation
+     * is not executed, the callback will happen immediately.
+     * @return Returns a new ActivityOptions object that you can use to
+     * supply these options as the options Bundle when starting an activity.
+     * @hide
+     */
+    public static ActivityOptions makeDelayedThumbnailScaleUpAnimation(View source,
+            Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener) {
+        return makeThumbnailScaleUpAnimation(source, thumbnail, startX, startY, listener, true);
+    }
+
+    private static ActivityOptions makeThumbnailScaleUpAnimation(View source,
+            Bitmap thumbnail, int startX, int startY, OnAnimationStartedListener listener,
+            boolean delayed) {
         ActivityOptions opts = new ActivityOptions();
         opts.mPackageName = source.getContext().getPackageName();
-        opts.mAnimationType = ANIM_THUMBNAIL;
+        opts.mAnimationType = delayed ? ANIM_THUMBNAIL_DELAYED : ANIM_THUMBNAIL;
         opts.mThumbnail = thumbnail;
         int[] pts = new int[2];
         source.getLocationOnScreen(pts);
@@ -258,7 +289,8 @@
             mStartY = opts.getInt(KEY_ANIM_START_Y, 0);
             mStartWidth = opts.getInt(KEY_ANIM_START_WIDTH, 0);
             mStartHeight = opts.getInt(KEY_ANIM_START_HEIGHT, 0);
-        } else if (mAnimationType == ANIM_THUMBNAIL) {
+        } else if (mAnimationType == ANIM_THUMBNAIL ||
+                mAnimationType == ANIM_THUMBNAIL_DELAYED) {
             mThumbnail = (Bitmap)opts.getParcelable(KEY_ANIM_THUMBNAIL);
             mStartX = opts.getInt(KEY_ANIM_START_X, 0);
             mStartY = opts.getInt(KEY_ANIM_START_Y, 0);
@@ -359,6 +391,7 @@
                 mStartHeight = otherOptions.mStartHeight;
                 break;
             case ANIM_THUMBNAIL:
+            case ANIM_THUMBNAIL_DELAYED:
                 mAnimationType = otherOptions.mAnimationType;
                 mThumbnail = otherOptions.mThumbnail;
                 mStartX = otherOptions.mStartX;
@@ -401,6 +434,7 @@
                 b.putInt(KEY_ANIM_START_HEIGHT, mStartHeight);
                 break;
             case ANIM_THUMBNAIL:
+            case ANIM_THUMBNAIL_DELAYED:
                 b.putInt(KEY_ANIM_TYPE, mAnimationType);
                 b.putParcelable(KEY_ANIM_THUMBNAIL, mThumbnail);
                 b.putInt(KEY_ANIM_START_X, mStartX);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index edeeee2..ed9babf 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -390,21 +390,25 @@
     public int priority;
     
     /**
+     * @hide
      * Notification type: incoming call (voice or video) or similar synchronous communication request.
      */
     public static final String KIND_CALL = "android.call";
 
     /**
+     * @hide
      * Notification type: incoming direct message (SMS, instant message, etc.).
      */
     public static final String KIND_MESSAGE = "android.message";
 
     /**
+     * @hide
      * Notification type: asynchronous bulk message (email).
      */
     public static final String KIND_EMAIL = "android.email";
 
     /**
+     * @hide
      * Notification type: calendar event.
      */
     public static final String KIND_EVENT = "android.event";
@@ -415,6 +419,7 @@
     public static final String KIND_PROMO = "android.promo";
 
     /**
+     * @hide
      * If this notification matches of one or more special types (see the <code>KIND_*</code>
      * constants), add them here, best match first.
      */
@@ -977,8 +982,14 @@
         }
 
         /**
-         * Show the {@link Notification#when} field as a countdown (or count-up) timer instead of a timestamp.  
+         * Show the {@link Notification#when} field as a stopwatch.
+         * 
+         * Instead of presenting <code>when</code> as a timestamp, the notification will show an 
+         * automatically updating display of the minutes and seconds since <code>when</code>.
          *
+         * Useful when showing an elapsed time (like an ongoing phone call).
+         *
+         * @see android.widget.Chronometer
          * @see Notification#when
          */
         public Builder setUsesChronometer(boolean b) {
@@ -1303,6 +1314,8 @@
         }
         
         /**
+         * @hide
+         * 
          * Add a kind (category) to this notification. Optional.
          * 
          * @see Notification#kind
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index e1f01db..c5a687a 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -84,7 +84,7 @@
     void overridePendingAppTransitionScaleUp(int startX, int startY, int startWidth,
             int startHeight);
     void overridePendingAppTransitionThumb(in Bitmap srcThumb, int startX, int startY,
-            IRemoteCallback startedCallback);
+            IRemoteCallback startedCallback, boolean delayed);
     void executeAppTransition();
     void setAppStartingWindow(IBinder token, String pkg, int theme,
             in CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java
index 37567fd..89f2187 100644
--- a/core/java/com/android/internal/app/PlatLogoActivity.java
+++ b/core/java/com/android/internal/app/PlatLogoActivity.java
@@ -29,64 +29,43 @@
 import android.widget.Toast;
 
 public class PlatLogoActivity extends Activity {
-    Vibrator mZzz;
     Toast mToast;
     ImageView mContent;
     int mCount;
     final Handler mHandler = new Handler();
 
-    Runnable mSuperLongPress = new Runnable() {
-        public void run() {
-            mCount++;
-            mZzz.vibrate(50 * mCount);
-            final float scale = 1f + 0.25f * mCount * mCount;
-            mContent.setScaleX(scale);
-            mContent.setScaleY(scale);
-
-            if (mCount <= 3) {
-                mHandler.postDelayed(mSuperLongPress, ViewConfiguration.getLongPressTimeout());
-            } else {
-                try {
-                    startActivity(new Intent(Intent.ACTION_MAIN)
-                        .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                            | Intent.FLAG_ACTIVITY_CLEAR_TASK
-                            | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
-                        .setClassName("com.android.systemui","com.android.systemui.Nyandroid"));
-                } catch (ActivityNotFoundException ex) {
-                    android.util.Log.e("PlatLogoActivity", "Couldn't find platlogo screensaver.");
-                }
-                finish();
-            }
-        }
-    };
-
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        mZzz = (Vibrator)getSystemService(VIBRATOR_SERVICE);
-        mToast = Toast.makeText(this, "Android 4.0: Ice Cream Sandwich", Toast.LENGTH_SHORT);
+        mToast = Toast.makeText(this, "Android X.X: Jelly Bean", Toast.LENGTH_SHORT);
 
         mContent = new ImageView(this);
         mContent.setImageResource(com.android.internal.R.drawable.platlogo);
         mContent.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
 
-        mContent.setOnTouchListener(new View.OnTouchListener() {
+        mContent.setOnClickListener(new View.OnClickListener() {
             @Override
-            public boolean onTouch(View v, MotionEvent event) {
-                final int action = event.getAction();
-                if (action == MotionEvent.ACTION_DOWN) {
-                    mContent.setPressed(true);
-                    mHandler.removeCallbacks(mSuperLongPress);
-                    mCount = 0;
-                    mHandler.postDelayed(mSuperLongPress, 2*ViewConfiguration.getLongPressTimeout());
-                } else if (action == MotionEvent.ACTION_UP) {
-                    if (mContent.isPressed()) {
-                        mContent.setPressed(false);
-                        mHandler.removeCallbacks(mSuperLongPress);
-                        mToast.show();
-                    }
+            public void onClick(View v) {
+                mToast.show();
+                mContent.setImageResource(com.android.internal.R.drawable.platlogo_alt);
+            }
+        });
+
+        mContent.setOnLongClickListener(new View.OnLongClickListener() {
+            @Override
+            public boolean onLongClick(View v) {
+                try {
+                    startActivity(new Intent(Intent.ACTION_MAIN)
+                        .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                            | Intent.FLAG_ACTIVITY_CLEAR_TASK
+                            | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+                        .addCategory("com.android.internal.category.PLATLOGO"));
+                        //.setClassName("com.android.systemui","com.android.systemui.BeanBag"));
+                } catch (ActivityNotFoundException ex) {
+                    android.util.Log.e("PlatLogoActivity", "Couldn't find a bag of beans.");
                 }
+                finish();
                 return true;
             }
         });
diff --git a/core/java/com/google/android/mms/pdu/PduPersister.java b/core/java/com/google/android/mms/pdu/PduPersister.java
index 1a8e80f..ee285aa 100644
--- a/core/java/com/google/android/mms/pdu/PduPersister.java
+++ b/core/java/com/google/android/mms/pdu/PduPersister.java
@@ -1058,9 +1058,10 @@
                 }
             }
         }
-
-        long threadId = Threads.getOrCreateThreadId(mContext, recipients);
-        values.put(Mms.THREAD_ID, threadId);
+        if (!recipients.isEmpty()) {
+            long threadId = Threads.getOrCreateThreadId(mContext, recipients);
+            values.put(Mms.THREAD_ID, threadId);
+        }
 
         SqliteWrapper.update(mContext, mContentResolver, uri, values, null, null);
     }
@@ -1299,7 +1300,6 @@
         }
 
         HashSet<String> recipients = new HashSet<String>();
-        long threadId = DUMMY_THREAD_ID;
         int msgType = pdu.getMessageType();
         // Here we only allocate thread ID for M-Notification.ind,
         // M-Retrieve.conf and M-Send.req.
@@ -1326,9 +1326,11 @@
                     }
                 }
             }
-            threadId = Threads.getOrCreateThreadId(mContext, recipients);
+            if (!recipients.isEmpty()) {
+                long threadId = Threads.getOrCreateThreadId(mContext, recipients);
+                values.put(Mms.THREAD_ID, threadId);
+            }
         }
-        values.put(Mms.THREAD_ID, threadId);
 
         // Save parts first to avoid inconsistent message is loaded
         // while saving the parts.
diff --git a/core/res/res/drawable-hdpi/stat_sys_adb.png b/core/res/res/drawable-hdpi/stat_sys_adb.png
index e7e1d8d..cfbbd8d 100644
--- a/core/res/res/drawable-hdpi/stat_sys_adb.png
+++ b/core/res/res/drawable-hdpi/stat_sys_adb.png
Binary files differ
diff --git a/core/res/res/drawable-ldpi/stat_sys_adb.png b/core/res/res/drawable-ldpi/stat_sys_adb.png
index 86b945b..0171adb 100644
--- a/core/res/res/drawable-ldpi/stat_sys_adb.png
+++ b/core/res/res/drawable-ldpi/stat_sys_adb.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_adb.png b/core/res/res/drawable-mdpi/stat_sys_adb.png
index 86d113f..4862919 100644
--- a/core/res/res/drawable-mdpi/stat_sys_adb.png
+++ b/core/res/res/drawable-mdpi/stat_sys_adb.png
Binary files differ
diff --git a/core/res/res/drawable-nodpi/platlogo.png b/core/res/res/drawable-nodpi/platlogo.png
index 8aa3b9e..f46c6c6 100644
--- a/core/res/res/drawable-nodpi/platlogo.png
+++ b/core/res/res/drawable-nodpi/platlogo.png
Binary files differ
diff --git a/core/res/res/drawable-nodpi/platlogo_alt.png b/core/res/res/drawable-nodpi/platlogo_alt.png
new file mode 100644
index 0000000..63b53b8
--- /dev/null
+++ b/core/res/res/drawable-nodpi/platlogo_alt.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/stat_sys_adb.png b/core/res/res/drawable-xhdpi/stat_sys_adb.png
index 684d57a..576ae24 100644
--- a/core/res/res/drawable-xhdpi/stat_sys_adb.png
+++ b/core/res/res/drawable-xhdpi/stat_sys_adb.png
Binary files differ
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index d899a1a..1d17cd8 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -980,6 +980,7 @@
   <java-symbol type="drawable" name="jog_tab_target_gray" />
   <java-symbol type="drawable" name="picture_emergency" />
   <java-symbol type="drawable" name="platlogo" />
+  <java-symbol type="drawable" name="platlogo_alt" />
   <java-symbol type="drawable" name="stat_notify_sync_error" />
   <java-symbol type="drawable" name="stat_notify_wifi_in_range" />
   <java-symbol type="drawable" name="stat_sys_gps_on" />
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/BitmapOverlayFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/BitmapOverlayFilter.java
index d4c901f..e4bb6cf 100644
--- a/media/mca/filterpacks/java/android/filterpacks/imageproc/BitmapOverlayFilter.java
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/BitmapOverlayFilter.java
@@ -46,8 +46,6 @@
     private Program mProgram;
     private Frame mFrame;
 
-    private int mWidth = 0;
-    private int mHeight = 0;
     private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
 
     private final String mOverlayShader =
@@ -113,18 +111,17 @@
             initProgram(context, inputFormat.getTarget());
         }
 
-        // Check if the frame size has changed
-        if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) {
-            mWidth = inputFormat.getWidth();
-            mHeight = inputFormat.getHeight();
+        if (mBitmap != null) {
+            Frame frame = createBitmapFrame(context);
+            // Process
+            Frame[] inputs = {input, frame};
+            mProgram.process(inputs, output);
 
-            createBitmapFrame(context);
+            frame.release();
+        } else {
+            output.setDataFromFrame(input);
         }
 
-        // Process
-        Frame[] inputs = {input, mFrame};
-        mProgram.process(inputs, output);
-
         // Push output
         pushOutput("image", output);
 
@@ -132,22 +129,18 @@
         output.release();
     }
 
-    private void createBitmapFrame(FilterContext context) {
-        if (mBitmap != null) {
-            FrameFormat format = ImageFormat.create(mBitmap.getWidth(),
-                                                    mBitmap.getHeight(),
-                                                    ImageFormat.COLORSPACE_RGBA,
-                                                    FrameFormat.TARGET_GPU);
+    private Frame createBitmapFrame(FilterContext context) {
+        FrameFormat format = ImageFormat.create(mBitmap.getWidth(),
+                                                mBitmap.getHeight(),
+                                                ImageFormat.COLORSPACE_RGBA,
+                                                FrameFormat.TARGET_GPU);
 
-            if (mFrame != null) {
-                mFrame.release();
-            }
+        Frame frame = context.getFrameManager().newFrame(format);
+        frame.setBitmap(mBitmap);
 
-            mFrame = context.getFrameManager().newFrame(format);
-            mFrame.setBitmap(mBitmap);
+        mBitmap.recycle();
+        mBitmap = null;
 
-            mBitmap.recycle();
-            mBitmap = null;
-        }
+        return frame;
     }
 }
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/BlackWhiteFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/BlackWhiteFilter.java
index 9e40d37..dd7f5e0 100644
--- a/media/mca/filterpacks/java/android/filterpacks/imageproc/BlackWhiteFilter.java
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/BlackWhiteFilter.java
@@ -28,6 +28,7 @@
 import android.filterfw.core.ShaderProgram;
 import android.filterfw.format.ImageFormat;
 
+import java.util.Date;
 import java.util.Random;
 
 public class BlackWhiteFilter extends Filter {
@@ -42,24 +43,30 @@
     private int mTileSize = 640;
 
     private Program mProgram;
+    private Random mRandom;
 
-    private int mWidth = 0;
-    private int mHeight = 0;
     private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
 
     private final String mBlackWhiteShader =
             "precision mediump float;\n" +
             "uniform sampler2D tex_sampler_0;\n" +
+            "uniform vec2 seed;\n" +
             "uniform float black;\n" +
             "uniform float scale;\n" +
             "uniform float stepsize;\n" +
             "varying vec2 v_texcoord;\n" +
             "float rand(vec2 loc) {\n" +
-            "  return fract(sin(dot(loc, vec2(12.9898, 78.233))) * 43758.5453);\n" +
+            "  const float divide = 0.00048828125;\n" +
+            "  const float factor = 2048.0;\n" +
+            "  float value = sin(dot(loc, vec2(12.9898, 78.233)));\n" +
+            "  float residual = mod(dot(mod(loc, divide), vec2(0.9898, 0.233)), divide);\n" +
+            "  float part2 = mod(value, divide);\n" +
+            "  float part1 = value - part2;\n" +
+            "  return fract(0.5453 * part1 + factor * (part2 + residual));\n" +
             "}\n" +
             "void main() {\n" +
             "  vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
-            "  float dither = rand(v_texcoord);\n" +
+            "  float dither = rand(v_texcoord + seed);\n" +
             "  vec3 xform = clamp((color.rgb - black) * scale, 0.0, 1.0);\n" +
             "  vec3 temp = clamp((color.rgb + stepsize - black) * scale, 0.0, 1.0);\n" +
             "  vec3 new_color = clamp(xform + (temp - xform) * (dither - 0.5), 0.0, 1.0);\n" +
@@ -68,6 +75,7 @@
 
     public BlackWhiteFilter(String name) {
         super(name);
+        mRandom = new Random(new Date().getTime());
     }
 
     @Override
@@ -100,10 +108,12 @@
     private void updateParameters() {
         float scale = (mBlack != mWhite) ? 1.0f / (mWhite - mBlack) : 2000f;
         float stepsize = 1.0f / 255.0f;
-
         mProgram.setHostValue("black", mBlack);
         mProgram.setHostValue("scale", scale);
         mProgram.setHostValue("stepsize", stepsize);
+
+        float seed[] = { mRandom.nextFloat(), mRandom.nextFloat() };
+        mProgram.setHostValue("seed", seed);
     }
 
     @Override
@@ -124,12 +134,6 @@
             initProgram(context, inputFormat.getTarget());
         }
 
-        // Check if the frame size has changed
-        if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) {
-            mWidth = inputFormat.getWidth();
-            mHeight = inputFormat.getHeight();
-        }
-
         // Create output frame
         Frame output = context.getFrameManager().newFrame(inputFormat);
 
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/DocumentaryFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/DocumentaryFilter.java
index 0144d4e..377e49d5 100644
--- a/media/mca/filterpacks/java/android/filterpacks/imageproc/DocumentaryFilter.java
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/DocumentaryFilter.java
@@ -28,6 +28,7 @@
 import android.filterfw.core.ShaderProgram;
 import android.filterfw.format.ImageFormat;
 
+import java.util.Date;
 import java.util.Random;
 
 public class DocumentaryFilter extends Filter {
@@ -36,6 +37,7 @@
     private int mTileSize = 640;
 
     private Program mProgram;
+    private Random mRandom;
 
     private int mWidth = 0;
     private int mHeight = 0;
@@ -44,17 +46,24 @@
     private final String mDocumentaryShader =
             "precision mediump float;\n" +
             "uniform sampler2D tex_sampler_0;\n" +
+            "uniform vec2 seed;\n" +
             "uniform float stepsize;\n" +
             "uniform float inv_max_dist;\n" +
             "uniform vec2 center;\n" +
             "varying vec2 v_texcoord;\n" +
             "float rand(vec2 loc) {\n" +
-            "  return fract(sin(dot(loc, vec2(12.9898, 78.233))) * 43758.5453);\n" +
+            "  const float divide = 0.00048828125;\n" +
+            "  const float factor = 2048.0;\n" +
+            "  float value = sin(dot(loc, vec2(12.9898, 78.233)));\n" +
+            "  float residual = mod(dot(mod(loc, divide), vec2(0.9898, 0.233)), divide);\n" +
+            "  float part2 = mod(value, divide);\n" +
+            "  float part1 = value - part2;\n" +
+            "  return fract(0.5453 * part1 + factor * (part2 + residual));\n" +
             "}\n" +
             "void main() {\n" +
             // black white
             "  vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
-            "  float dither = rand(v_texcoord);\n" +
+            "  float dither = rand(v_texcoord + seed);\n" +
             "  vec3 xform = clamp(2.0 * color.rgb, 0.0, 1.0);\n" +
             "  vec3 temp = clamp(2.0 * (color.rgb + stepsize), 0.0, 1.0);\n" +
             "  vec3 new_color = clamp(xform + (temp - xform) * (dither - 0.5), 0.0, 1.0);\n" +
@@ -69,6 +78,8 @@
 
     public DocumentaryFilter(String name) {
         super(name);
+        Date date = new Date();
+        mRandom = new Random(new Date().getTime());
     }
 
     @Override
@@ -138,7 +149,9 @@
             mProgram.setHostValue("center", center);
             mProgram.setHostValue("inv_max_dist", 1.0f / max_dist);
             mProgram.setHostValue("stepsize", 1.0f / 255.0f);
+
+            float seed[] = { mRandom.nextFloat(), mRandom.nextFloat() };
+            mProgram.setHostValue("seed", seed);
         }
     }
-
 }
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/GrainFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/GrainFilter.java
index 31855460..f236856 100644
--- a/media/mca/filterpacks/java/android/filterpacks/imageproc/GrainFilter.java
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/GrainFilter.java
@@ -30,6 +30,7 @@
 import android.filterfw.geometry.Quad;
 import android.filterfw.geometry.Point;
 
+import java.util.Date;
 import java.util.Random;
 
 public class GrainFilter extends Filter {
@@ -49,14 +50,20 @@
     private int mHeight = 0;
     private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
 
-    private Random mRandom = new Random();
+    private Random mRandom;
 
     private final String mNoiseShader =
             "precision mediump float;\n" +
             "uniform vec2 seed;\n" +
             "varying vec2 v_texcoord;\n" +
             "float rand(vec2 loc) {\n" +
-            "  return fract(sin(dot(loc, vec2(12.9898, 78.233))) * 43758.5453);\n" +
+            "  const float divide = 0.00048828125;\n" +
+            "  const float factor = 2048.0;\n" +
+            "  float value = sin(dot(loc, vec2(12.9898, 78.233)));\n" +
+            "  float residual = mod(dot(mod(loc, divide), vec2(0.9898, 0.233)), divide);\n" +
+            "  float part2 = mod(value, divide);\n" +
+            "  float part1 = value - part2;\n" +
+            "  return fract(0.5453 * part1 + factor * (part2 + residual));\n" +
             "}\n" +
             "void main() {\n" +
             "  gl_FragColor = vec4(rand(v_texcoord + seed), 0.0, 0.0, 1.0);\n" +
@@ -86,6 +93,7 @@
 
     public GrainFilter(String name) {
         super(name);
+        mRandom = new Random(new Date().getTime());
     }
 
     @Override
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/LomoishFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/LomoishFilter.java
index 0814ba5..22a2ec8 100644
--- a/media/mca/filterpacks/java/android/filterpacks/imageproc/LomoishFilter.java
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/LomoishFilter.java
@@ -28,12 +28,16 @@
 import android.filterfw.core.ShaderProgram;
 import android.filterfw.format.ImageFormat;
 
+import java.util.Date;
+import java.util.Random;
+
 public class LomoishFilter extends Filter {
 
     @GenerateFieldPort(name = "tile_size", hasDefault = true)
     private int mTileSize = 640;
 
     private Program mProgram;
+    private Random mRandom;
 
     private int mWidth = 0;
     private int mHeight = 0;
@@ -42,6 +46,7 @@
     private final String mLomoishShader =
             "precision mediump float;\n" +
             "uniform sampler2D tex_sampler_0;\n" +
+            "uniform vec2 seed;\n" +
             "uniform float stepsizeX;\n" +
             "uniform float stepsizeY;\n" +
             "uniform float stepsize;\n" +
@@ -49,7 +54,13 @@
             "uniform float inv_max_dist;\n" +
             "varying vec2 v_texcoord;\n" +
             "float rand(vec2 loc) {\n" +
-            "  return fract(sin(dot(loc, vec2(12.9898, 78.233))) * 43758.5453);\n" +
+            "  const float divide = 0.00048828125;\n" +
+            "  const float factor = 2048.0;\n" +
+            "  float value = sin(dot(loc, vec2(12.9898, 78.233)));\n" +
+            "  float residual = mod(dot(mod(loc, divide), vec2(0.9898, 0.233)), divide);\n" +
+            "  float part2 = mod(value, divide);\n" +
+            "  float part1 = value - part2;\n" +
+            "  return fract(0.5453 * part1 + factor * (part2 + residual));\n" +
             "}\n" +
             "void main() {\n" +
             // sharpen
@@ -96,7 +107,7 @@
             "  }\n" +
             "  c_color.b = s_color.b * 0.5 + 0.25;\n" +
             // blackwhite
-            "  float dither = rand(v_texcoord);\n" +
+            "  float dither = rand(v_texcoord + seed);\n" +
             "  vec3 xform = clamp((c_color.rgb - 0.15) * 1.53846, 0.0, 1.0);\n" +
             "  vec3 temp = clamp((color.rgb + stepsize - 0.15) * 1.53846, 0.0, 1.0);\n" +
             "  vec3 bw_color = clamp(xform + (temp - xform) * (dither - 0.5), 0.0, 1.0);\n" +
@@ -108,6 +119,7 @@
 
     public LomoishFilter(String name) {
         super(name);
+        mRandom = new Random(new Date().getTime());
     }
 
     @Override
@@ -149,6 +161,9 @@
             mProgram.setHostValue("stepsize", 1.0f / 255.0f);
             mProgram.setHostValue("stepsizeX", 1.0f / mWidth);
             mProgram.setHostValue("stepsizeY", 1.0f / mHeight);
+
+            float seed[] = { mRandom.nextFloat(), mRandom.nextFloat() };
+            mProgram.setHostValue("seed", seed);
         }
     }
 
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/RedEyeFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/RedEyeFilter.java
index 5632a5e..3450ef1 100644
--- a/media/mca/filterpacks/java/android/filterpacks/imageproc/RedEyeFilter.java
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/RedEyeFilter.java
@@ -115,14 +115,6 @@
     }
 
     @Override
-    public void tearDown(FilterContext context) {
-        if (mRedEyeBitmap != null) {
-            mRedEyeBitmap.recycle();
-            mRedEyeBitmap = null;
-        }
-    }
-
-    @Override
     public void process(FilterContext context) {
         // Get input frame
         Frame input = pullInput("image");
@@ -140,10 +132,7 @@
         if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) {
             mWidth = inputFormat.getWidth();
             mHeight = inputFormat.getHeight();
-
-            createRedEyeBitmap();
         }
-
         createRedEyeFrame(context);
 
         // Process
@@ -168,29 +157,26 @@
         }
     }
 
-    private void createRedEyeBitmap() {
-        if (mRedEyeBitmap != null) {
-            mRedEyeBitmap.recycle();
-        }
-
+    private void createRedEyeFrame(FilterContext context) {
         int bitmapWidth = mWidth / 2;
         int bitmapHeight = mHeight / 2;
 
-        mRedEyeBitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
-        mCanvas.setBitmap(mRedEyeBitmap);
+        Bitmap redEyeBitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
+        mCanvas.setBitmap(redEyeBitmap);
         mPaint.setColor(Color.WHITE);
         mRadius = Math.max(MIN_RADIUS, RADIUS_RATIO * Math.min(bitmapWidth, bitmapHeight));
 
-        updateProgramParams();
-    }
+        for (int i = 0; i < mCenters.length; i += 2) {
+            mCanvas.drawCircle(mCenters[i] * bitmapWidth, mCenters[i + 1] * bitmapHeight,
+                               mRadius, mPaint);
+        }
 
-    private void createRedEyeFrame(FilterContext context) {
-        FrameFormat format = ImageFormat.create(mRedEyeBitmap.getWidth() ,
-                                                mRedEyeBitmap.getHeight(),
+        FrameFormat format = ImageFormat.create(bitmapWidth, bitmapHeight,
                                                 ImageFormat.COLORSPACE_RGBA,
                                                 FrameFormat.TARGET_GPU);
         mRedEyeFrame = context.getFrameManager().newFrame(format);
-        mRedEyeFrame.setBitmap(mRedEyeBitmap);
+        mRedEyeFrame.setBitmap(redEyeBitmap);
+        redEyeBitmap.recycle();
     }
 
     private void updateProgramParams() {
@@ -199,13 +185,5 @@
         if ( mCenters.length % 2 == 1) {
             throw new RuntimeException("The size of center array must be even.");
         }
-
-        if (mRedEyeBitmap != null) {
-            for (int i = 0; i < mCenters.length; i += 2) {
-                mCanvas.drawCircle(mCenters[i] * mRedEyeBitmap.getWidth(),
-                                   mCenters[i + 1] * mRedEyeBitmap.getHeight(),
-                                   mRadius, mPaint);
-            }
-        }
     }
 }
diff --git a/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureTarget.java b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureTarget.java
index 20e4b32..b023e42 100644
--- a/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureTarget.java
+++ b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureTarget.java
@@ -110,7 +110,7 @@
     }
 
     @Override
-    public void setupPorts() {
+    public synchronized void setupPorts() {
         // Make sure we have a SurfaceView
         if (mSurfaceTexture == null) {
             throw new RuntimeException("Null SurfaceTexture passed to SurfaceTextureTarget");
@@ -158,7 +158,7 @@
     }
 
     @Override
-    public void open(FilterContext context) {
+    public synchronized void open(FilterContext context) {
         // Set up SurfaceTexture internals
         mSurfaceId = context.getGLEnvironment().registerSurfaceTexture(
             mSurfaceTexture, mScreenWidth, mScreenHeight);
@@ -169,17 +169,42 @@
 
 
     @Override
-    public void close(FilterContext context) {
+    public synchronized void close(FilterContext context) {
         if (mSurfaceId > 0) {
             context.getGLEnvironment().unregisterSurfaceId(mSurfaceId);
+            mSurfaceId = -1;
+            // Once the surface is unregistered, remove the surfacetexture reference.
+            // The surfaceId could not have been valid without a valid surfacetexture.
+            mSurfaceTexture = null;
         }
     }
 
+    // This should be called from the client side when the surfacetexture is no longer
+    // valid. e.g. from onPause() in the application using the filter graph.
+    public synchronized void disconnect(FilterContext context) {
+        if (mLogVerbose) Log.v(TAG, "disconnect");
+        if (mSurfaceTexture == null) {
+            Log.d(TAG, "SurfaceTexture is already null. Nothing to disconnect.");
+            return;
+        }
+        mSurfaceTexture = null;
+        // Make sure we unregister the surface as well if a surface was registered.
+        // There can be a situation where the surface was not registered but the
+        // surfacetexture was valid. For example, the disconnect can be called before
+        // the filter was opened. Hence, the surfaceId may not be a valid one here,
+        // and need to check for its validity.
+        if (mSurfaceId > 0) {
+            context.getGLEnvironment().unregisterSurfaceId(mSurfaceId);
+            mSurfaceId = -1;
+        }
+    }
 
     @Override
-    public void process(FilterContext context) {
-        if (mLogVerbose) Log.v(TAG, "Starting frame processing");
-
+    public synchronized void process(FilterContext context) {
+        // Surface is not registered. Nothing to render into.
+        if (mSurfaceId <= 0) {
+            return;
+        }
         GLEnvironment glEnv = context.getGLEnvironment();
 
         // Get input frame
@@ -197,8 +222,6 @@
 
         // See if we need to copy to GPU
         Frame gpuFrame = null;
-        if (mLogVerbose) Log.v("SurfaceTextureTarget", "Got input format: " + input.getFormat());
-
         int target = input.getFormat().getTarget();
         if (target != FrameFormat.TARGET_GPU) {
             gpuFrame = context.getFrameManager().duplicateFrameToTarget(input,
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 9d3a942..0038d13 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -161,18 +161,19 @@
 
         <!-- started from ... somewhere -->
         <activity
-            android:name=".Nyandroid"
+            android:name=".BeanBag"
             android:exported="true"
-            android:label="Nyandroid"
-            android:icon="@drawable/nyandroid04"
-            android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
+            android:label="BeanBag"
+            android:icon="@drawable/redbeandroid"
+            android:theme="@android:style/Theme.Wallpaper.NoTitleBar.Fullscreen"
             android:hardwareAccelerated="true"
             android:launchMode="singleInstance"
             android:excludeFromRecents="true">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.DEFAULT" />
-<!--                <category android:name="android.intent.category.DREAM" />-->
+                <category android:name="com.android.internal.category.PLATLOGO" />
+<!--            <category android:name="android.intent.category.LAUNCHER" />-->
             </intent-filter>
         </activity>
     </application>
diff --git a/packages/SystemUI/res/drawable-nodpi/jandycane.png b/packages/SystemUI/res/drawable-nodpi/jandycane.png
new file mode 100644
index 0000000..278cfec
--- /dev/null
+++ b/packages/SystemUI/res/drawable-nodpi/jandycane.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/nyandroid00.png b/packages/SystemUI/res/drawable-nodpi/nyandroid00.png
deleted file mode 100644
index 6cea873..0000000
--- a/packages/SystemUI/res/drawable-nodpi/nyandroid00.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/nyandroid01.png b/packages/SystemUI/res/drawable-nodpi/nyandroid01.png
deleted file mode 100644
index 82b8a21..0000000
--- a/packages/SystemUI/res/drawable-nodpi/nyandroid01.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/nyandroid02.png b/packages/SystemUI/res/drawable-nodpi/nyandroid02.png
deleted file mode 100644
index fde0033..0000000
--- a/packages/SystemUI/res/drawable-nodpi/nyandroid02.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/nyandroid03.png b/packages/SystemUI/res/drawable-nodpi/nyandroid03.png
deleted file mode 100644
index 54c5f46..0000000
--- a/packages/SystemUI/res/drawable-nodpi/nyandroid03.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/nyandroid04.png b/packages/SystemUI/res/drawable-nodpi/nyandroid04.png
deleted file mode 100644
index 35e5ab5..0000000
--- a/packages/SystemUI/res/drawable-nodpi/nyandroid04.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/nyandroid05.png b/packages/SystemUI/res/drawable-nodpi/nyandroid05.png
deleted file mode 100644
index d3eaace..0000000
--- a/packages/SystemUI/res/drawable-nodpi/nyandroid05.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/nyandroid06.png b/packages/SystemUI/res/drawable-nodpi/nyandroid06.png
deleted file mode 100644
index 0e0d3b1..0000000
--- a/packages/SystemUI/res/drawable-nodpi/nyandroid06.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/nyandroid07.png b/packages/SystemUI/res/drawable-nodpi/nyandroid07.png
deleted file mode 100644
index edb0b17..0000000
--- a/packages/SystemUI/res/drawable-nodpi/nyandroid07.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/nyandroid08.png b/packages/SystemUI/res/drawable-nodpi/nyandroid08.png
deleted file mode 100644
index 10fc4f6..0000000
--- a/packages/SystemUI/res/drawable-nodpi/nyandroid08.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/nyandroid09.png b/packages/SystemUI/res/drawable-nodpi/nyandroid09.png
deleted file mode 100644
index 57ade54..0000000
--- a/packages/SystemUI/res/drawable-nodpi/nyandroid09.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/nyandroid10.png b/packages/SystemUI/res/drawable-nodpi/nyandroid10.png
deleted file mode 100644
index 36feb2f..0000000
--- a/packages/SystemUI/res/drawable-nodpi/nyandroid10.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/nyandroid11.png b/packages/SystemUI/res/drawable-nodpi/nyandroid11.png
deleted file mode 100644
index 125935b..0000000
--- a/packages/SystemUI/res/drawable-nodpi/nyandroid11.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/redbean0.png b/packages/SystemUI/res/drawable-nodpi/redbean0.png
new file mode 100644
index 0000000..b088939
--- /dev/null
+++ b/packages/SystemUI/res/drawable-nodpi/redbean0.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/redbean1.png b/packages/SystemUI/res/drawable-nodpi/redbean1.png
new file mode 100644
index 0000000..8fc8d9d
--- /dev/null
+++ b/packages/SystemUI/res/drawable-nodpi/redbean1.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/redbean2.png b/packages/SystemUI/res/drawable-nodpi/redbean2.png
new file mode 100644
index 0000000..ef11ca8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-nodpi/redbean2.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/redbeandroid.png b/packages/SystemUI/res/drawable-nodpi/redbeandroid.png
new file mode 100644
index 0000000..9aa3f82
--- /dev/null
+++ b/packages/SystemUI/res/drawable-nodpi/redbeandroid.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/star0.png b/packages/SystemUI/res/drawable-nodpi/star0.png
deleted file mode 100644
index f2ca960..0000000
--- a/packages/SystemUI/res/drawable-nodpi/star0.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/star1.png b/packages/SystemUI/res/drawable-nodpi/star1.png
deleted file mode 100644
index 69ef4da..0000000
--- a/packages/SystemUI/res/drawable-nodpi/star1.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/star2.png b/packages/SystemUI/res/drawable-nodpi/star2.png
deleted file mode 100644
index b95968a..0000000
--- a/packages/SystemUI/res/drawable-nodpi/star2.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/star3.png b/packages/SystemUI/res/drawable-nodpi/star3.png
deleted file mode 100644
index ad0f589..0000000
--- a/packages/SystemUI/res/drawable-nodpi/star3.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/star4.png b/packages/SystemUI/res/drawable-nodpi/star4.png
deleted file mode 100644
index 934c45b..0000000
--- a/packages/SystemUI/res/drawable-nodpi/star4.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-nodpi/star5.png b/packages/SystemUI/res/drawable-nodpi/star5.png
deleted file mode 100644
index 46a4435..0000000
--- a/packages/SystemUI/res/drawable-nodpi/star5.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable/nyandroid_anim.xml b/packages/SystemUI/res/drawable/nyandroid_anim.xml
deleted file mode 100644
index 855a0c2..0000000
--- a/packages/SystemUI/res/drawable/nyandroid_anim.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 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.
--->
-<animation-list
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:oneshot="false">
-    <item android:drawable="@drawable/nyandroid00" android:duration="80" />
-    <item android:drawable="@drawable/nyandroid01" android:duration="80" />
-    <item android:drawable="@drawable/nyandroid02" android:duration="80" />
-    <item android:drawable="@drawable/nyandroid03" android:duration="80" />
-    <item android:drawable="@drawable/nyandroid04" android:duration="80" />
-    <item android:drawable="@drawable/nyandroid05" android:duration="80" />
-    <item android:drawable="@drawable/nyandroid06" android:duration="80" />
-    <item android:drawable="@drawable/nyandroid07" android:duration="80" />
-    <item android:drawable="@drawable/nyandroid08" android:duration="80" />
-    <item android:drawable="@drawable/nyandroid09" android:duration="80" />
-    <item android:drawable="@drawable/nyandroid10" android:duration="80" />
-    <item android:drawable="@drawable/nyandroid11" android:duration="80" />
-</animation-list>
-
diff --git a/packages/SystemUI/res/drawable/star_anim.xml b/packages/SystemUI/res/drawable/star_anim.xml
deleted file mode 100644
index d7f2d8f..0000000
--- a/packages/SystemUI/res/drawable/star_anim.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 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.
--->
-<animation-list
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:oneshot="false">
-    <item android:drawable="@drawable/star0" android:duration="200" />
-    <item android:drawable="@drawable/star1" android:duration="200" />
-    <item android:drawable="@drawable/star2" android:duration="200" />
-    <item android:drawable="@drawable/star3" android:duration="200" />
-    <item android:drawable="@drawable/star4" android:duration="200" />
-    <item android:drawable="@drawable/star5" android:duration="200" />
-</animation-list>
-
diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
index ec2abe0..869b164 100644
--- a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
@@ -27,6 +27,12 @@
     systemui:recentItemLayout="@layout/status_bar_recent_item"
     >
 
+    <ImageView
+        android:id="@+id/recents_transition_placeholder_icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:visibility="invisible" />
+
     <FrameLayout
         android:id="@+id/recents_bg_protect"
         android:background="@drawable/status_bar_recents_background"
diff --git a/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml
index 216dcb0..fc9fcf4 100644
--- a/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml
@@ -27,6 +27,12 @@
     systemui:recentItemLayout="@layout/status_bar_recent_item"
     >
 
+    <ImageView
+        android:id="@+id/recents_transition_placeholder_icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:visibility="invisible" />
+
     <FrameLayout
         android:id="@+id/recents_bg_protect"
         android:background="@drawable/status_bar_recents_background"
diff --git a/packages/SystemUI/res/layout/system_bar_recent_panel.xml b/packages/SystemUI/res/layout/system_bar_recent_panel.xml
index c2b9e51..d5745c8 100644
--- a/packages/SystemUI/res/layout/system_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout/system_bar_recent_panel.xml
@@ -39,6 +39,12 @@
         android:clipToPadding="false"
         android:clipChildren="false">
 
+        <ImageView
+            android:id="@+id/recents_transition_placeholder_icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:visibility="invisible" />
+
         <com.android.systemui.recent.RecentsVerticalScrollView android:id="@+id/recents_container"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 2a4c5fd..8ebbc52 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -17,4 +17,5 @@
 
 <resources>
     <item type="id" name="expandable_tag" />
+    <item type="id" name="user_expanded_tag" />
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/BeanBag.java b/packages/SystemUI/src/com/android/systemui/BeanBag.java
new file mode 100644
index 0000000..e4f00d6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/BeanBag.java
@@ -0,0 +1,439 @@
+/*);
+ * Copyright (C) 2012 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.systemui;
+
+import android.animation.AnimatorSet;
+import android.animation.PropertyValuesHolder;
+import android.animation.ObjectAnimator;
+import android.animation.TimeAnimator;
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.AnimationDrawable;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Pair;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.animation.AnimationUtils;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import java.util.HashMap;
+import java.util.Random;
+
+public class BeanBag extends Activity {
+    final static boolean DEBUG = false;
+
+    public static class Board extends FrameLayout
+    {
+        static Random sRNG = new Random();
+
+        static float lerp(float a, float b, float f) {
+            return (b-a)*f + a;
+        }
+
+        static float randfrange(float a, float b) {
+            return lerp(a, b, sRNG.nextFloat());
+        }
+
+        static int randsign() {
+            return sRNG.nextBoolean() ? 1 : -1;
+        }
+
+        static boolean flip() {
+            return sRNG.nextBoolean();
+        }
+
+        static float mag(float x, float y) {
+            return (float) Math.sqrt(x*x+y*y);
+        }
+
+        static float dot(float x1, float y1, float x2, float y2) {
+            return x1*x2+y1+y2;
+        }
+
+        static <E> E pick(E[] array) {
+            if (array.length == 0) return null;
+            return array[sRNG.nextInt(array.length)];
+        }
+
+        static int pickInt(int[] array) {
+            if (array.length == 0) return 0;
+            return array[sRNG.nextInt(array.length)];
+        }
+
+        static int NUM_BEANS = 40;
+        static float MIN_SCALE = 0.2f;
+        static float MAX_SCALE = 1f;
+
+        static float LUCKY = 0.001f;
+
+        static int MAX_RADIUS = (int)(576 * MAX_SCALE);
+
+        static int BEANS[] = {
+          R.drawable.redbean0,
+          R.drawable.redbean0,
+          R.drawable.redbean0,
+          R.drawable.redbean0,
+          R.drawable.redbean1,
+          R.drawable.redbean1,
+          R.drawable.redbean2,
+          R.drawable.redbean2,
+          R.drawable.redbeandroid,
+        };
+
+        static int COLORS[] = {
+            0xFF00CC00,
+            0xFFCC0000,
+            0xFF0000CC,
+            0xFFFFFF00,
+            0xFFFF8000,
+            0xFF00CCFF,
+            0xFFFF0080,
+            0xFF8000FF,
+            0xFFFF8080,
+            0xFF8080FF,
+            0xFFB0C0D0,
+            0xFFDDDDDD,
+            0xFF333333,
+        };
+
+        public class Bean extends ImageView {
+            public static final float VMAX = 1000.0f;
+            public static final float VMIN = 100.0f;
+
+            public float x, y, a;
+
+            public float va;
+            public float vx, vy;
+
+            public float r;
+
+            public float z;
+
+            public int h,w;
+
+            public boolean grabbed;
+            public float grabx, graby;
+            public long grabtime;
+
+            public Bean(Context context, AttributeSet as) {
+                super(context, as);
+            }
+
+            public String toString() {
+                return String.format("<bean (%.1f, %.1f) (%d x %d)>",
+                    getX(), getY(), getWidth(), getHeight());
+            }
+
+            private void pickBean() {
+                int beanId = pickInt(BEANS);
+                if (randfrange(0,1) <= LUCKY) {
+                    beanId = R.drawable.jandycane;
+                }
+                BitmapDrawable bean = (BitmapDrawable) getContext().getResources().getDrawable(beanId);
+                Bitmap beanBits = bean.getBitmap();
+                h=beanBits.getHeight();
+                w=beanBits.getWidth();
+
+                if (DEBUG) {
+                    bean.setAlpha(0x80);
+                }
+                this.setImageDrawable(bean);
+
+                Paint pt = new Paint();
+                final int color = pickInt(COLORS);
+                ColorMatrix CM = new ColorMatrix();
+                float[] M = CM.getArray();
+                // we assume the color information is in the red channel
+                /* R */ M[0]  = (float)((color & 0x00FF0000) >> 16) / 0xFF;
+                /* G */ M[5]  = (float)((color & 0x0000FF00) >> 8)  / 0xFF;
+                /* B */ M[10] = (float)((color & 0x000000FF))       / 0xFF;
+                pt.setColorFilter(new ColorMatrixColorFilter(M));
+                setLayerType(View.LAYER_TYPE_HARDWARE, (beanId == R.drawable.jandycane) ? null : pt);
+            }
+
+            public void reset() {
+                pickBean();
+
+                final float scale = lerp(MIN_SCALE,MAX_SCALE,z);
+                setScaleX(scale); setScaleY(scale);
+
+                r = 0.3f*Math.max(h,w)*scale;
+
+                a=(randfrange(0,360));
+                va = randfrange(-30,30);
+
+                vx = randfrange(-40,40) * z;
+                vy = randfrange(-40,40) * z;
+                final float boardh = boardHeight;
+                final float boardw = boardWidth;
+                //android.util.Log.d("BeanBag", "reset: w="+w+" h="+h);
+                if (flip()) {
+                    x=(vx < 0 ? boardw+2*r : -r*4f);
+                    y=(randfrange(0, boardh-3*r)*0.5f + ((vy < 0)?boardh*0.5f:0));
+                } else {
+                    y=(vy < 0 ? boardh+2*r : -r*4f);
+                    x=(randfrange(0, boardw-3*r)*0.5f + ((vx < 0)?boardw*0.5f:0));
+                }
+            }
+
+            public void update(float dt) {
+                if (grabbed) {
+//                    final float interval = (SystemClock.uptimeMillis() - grabtime) / 1000f;
+                    vx = (vx * 0.75f) + ((grabx - x) / dt) * 0.25f;
+                    x = grabx;
+                    vy = (vy * 0.75f) + ((graby - y) / dt) * 0.25f;;
+                    y = graby;
+                } else {
+                    x = (x + vx * dt);
+                    y = (y + vy * dt);
+                    a = (a + va * dt);
+                }
+            }
+
+            public float overlap(Bean other) {
+                final float dx = (x - other.x);
+                final float dy = (y - other.y);
+                return mag(dx, dy) - r - other.r;
+            }
+
+            @Override
+            public boolean onTouchEvent(MotionEvent e) {
+                switch (e.getAction()) {
+                    case MotionEvent.ACTION_DOWN:
+                        grabbed = true;
+                        va = 0;
+                        // fall
+                    case MotionEvent.ACTION_MOVE:
+                        grabx = e.getRawX();
+                        graby = e.getRawY();
+                        grabtime = e.getEventTime();
+                        break;
+                    case MotionEvent.ACTION_CANCEL:
+                    case MotionEvent.ACTION_UP:
+                        grabbed = false;
+                        va = randfrange(-5,5);
+                        break;
+                }
+                return true;
+            }
+        }
+
+        TimeAnimator mAnim;
+        private int boardWidth;
+        private int boardHeight;
+
+        public Board(Context context, AttributeSet as) {
+            super(context, as);
+
+            setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
+
+            setWillNotDraw(!DEBUG);
+        }
+
+        private void reset() {
+//            android.util.Log.d("Nyandroid", "board reset");
+            removeAllViews();
+
+            final ViewGroup.LayoutParams wrap = new ViewGroup.LayoutParams(
+                        ViewGroup.LayoutParams.WRAP_CONTENT,
+                        ViewGroup.LayoutParams.WRAP_CONTENT);
+
+            for(int i=0; i<NUM_BEANS; i++) {
+                Bean nv = new Bean(getContext(), null);
+                addView(nv, wrap);
+                nv.z = ((float)i/NUM_BEANS);
+                nv.z *= nv.z;
+                nv.reset();
+                nv.x = (randfrange(0, boardWidth));
+                nv.y = (randfrange(0, boardHeight));
+            }
+
+            if (mAnim != null) {
+                mAnim.cancel();
+            }
+            mAnim = new TimeAnimator();
+            mAnim.setTimeListener(new TimeAnimator.TimeListener() {
+                private long lastPrint = 0;
+                public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
+                    if (DEBUG && totalTime - lastPrint > 5000) {
+                        lastPrint = totalTime;
+                        for (int i=0; i<getChildCount(); i++) {
+                            android.util.Log.d("BeanBag", "bean " + i + ": " + getChildAt(i));
+                        }
+                    }
+
+                    for (int i=0; i<getChildCount(); i++) {
+                        View v = getChildAt(i);
+                        if (!(v instanceof Bean)) continue;
+                        Bean nv = (Bean) v;
+                        nv.update(deltaTime / 1000f);
+
+                        for (int j=i+1; j<getChildCount(); j++) {
+                            View v2 = getChildAt(j);
+                            if (!(v2 instanceof Bean)) continue;
+                            Bean nv2 = (Bean) v2;
+                            final float overlap = nv.overlap(nv2);
+                            if (false && overlap < 0) {
+                                // angle pointing from nv2 to nv
+                                final float dx = nv.x - nv2.x;
+                                final float dy = nv.y - nv2.y;
+                                final float ang = (float) Math.atan2(dx, dy);
+
+                                if (false) {
+                                nv.vx -= Math.cos(ang) * overlap * 0.5f;
+                                nv.vy -= Math.sin(ang) * overlap * 0.5f;
+                                nv2.vx += Math.cos(ang) * overlap * 0.5f;
+                                nv2.vy += Math.sin(ang) * overlap * 0.5f;
+                                }
+
+
+                                // first, move them apart
+                                nv.x -= Math.cos(ang) * overlap/2;
+                                nv.y -= Math.sin(ang) * overlap/2;
+                                nv2.x += Math.cos(ang) * overlap/2;
+                                nv2.y += Math.sin(ang) * overlap/2;
+
+                                // next, figure out velocities
+                                final float sap = 0f; // randfrange(0,0.25f);
+
+                                final float mag1 = mag(nv.vx, nv.vy) * (1f-sap);
+                                final float mag2 = mag(nv2.vx, nv2.vy) * (1f-sap);
+
+
+                                // hacky way to transfer "momentum"
+                                nv.vx = mag2 * (float)Math.cos(ang);
+                                nv.vy = mag2 * (float)Math.sin(ang);
+                                nv2.vx = -mag1 * (float)Math.cos(ang);
+                                nv2.vy = -mag1 * (float)Math.sin(ang);
+
+                                final float totalva = nv.va + nv2.va;
+                                final float frac = randfrange(0.25f,0.75f);
+                                nv.va = totalva * frac;
+                                nv2.va = totalva * (1f-frac);
+//                                nv.va += randfrange(-20,20);
+//                                nv2.va += randfrange(-20,20);
+
+                            }
+                        }
+
+                        nv.setRotation(nv.a);
+                        nv.setX(nv.x-nv.getPivotX());
+                        nv.setY(nv.y-nv.getPivotY());
+
+                        if (   nv.x < - MAX_RADIUS
+                            || nv.x > boardWidth + MAX_RADIUS
+                            || nv.y < -MAX_RADIUS
+                            || nv.y > boardHeight + MAX_RADIUS)
+                        {
+                            nv.reset();
+                        }
+                    }
+
+                    if (DEBUG) invalidate();
+                }
+            });
+        }
+
+        @Override
+        protected void onSizeChanged (int w, int h, int oldw, int oldh) {
+            super.onSizeChanged(w,h,oldw,oldh);
+            boardWidth = w;
+            boardHeight = h;
+//            android.util.Log.d("Nyandroid", "resized: " + w + "x" + h);
+            post(new Runnable() { public void run() {
+                reset();
+                mAnim.start();
+            } });
+        }
+
+
+        @Override
+        protected void onDetachedFromWindow() {
+            super.onDetachedFromWindow();
+            mAnim.cancel();
+        }
+
+        @Override
+        public boolean isOpaque() {
+            return false;
+        }
+
+        @Override
+        public void onDraw(Canvas c) {
+            if (DEBUG) {
+                //android.util.Log.d("BeanBag", "onDraw");
+                Paint pt = new Paint();
+                pt.setAntiAlias(true);
+                pt.setStyle(Paint.Style.STROKE);
+                pt.setColor(0xFFFF0000);
+                pt.setStrokeWidth(4.0f);
+                c.drawRect(0, 0, getWidth(), getHeight(), pt);
+                pt.setColor(0xFFFFCC00);
+                pt.setStrokeWidth(1.0f);
+                for (int i=0; i<getChildCount(); i++) {
+                    Bean b = (Bean) getChildAt(i);
+                    final float a = (360-b.a)/180f*3.14159f;
+                    final float tx = b.getTranslationX();
+                    final float ty = b.getTranslationY();
+                    c.drawCircle(b.x, b.y, b.r, pt);
+                    c.drawCircle(tx, ty, 4, pt);
+                    c.drawLine(b.x, b.y, (float)(b.x+b.r*Math.sin(a)), (float)(b.y+b.r*Math.cos(a)), pt);
+                }
+            }
+        }
+    }
+
+    private Board mBoard;
+
+    @Override
+    public void onStart() {
+        super.onStart();
+
+        getWindow().addFlags(
+                  WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
+                | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+                );
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mBoard = new Board(this, null);
+        setContentView(mBoard);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index c5928f1..7a7afa7 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -34,6 +34,7 @@
         View getChildAtPosition(MotionEvent ev);
         View getChildAtPosition(float x, float y);
         boolean canChildBeExpanded(View v);
+        boolean setUserExpandedChild(View v, boolean userxpanded);
     }
 
     private static final String TAG = "ExpandHelper";
@@ -272,6 +273,7 @@
         mScaleAnimation.start();
         mStretching = false;
         setGlow(0f);
+        mCallback.setUserExpandedChild(mCurrView, h == mNaturalHeight);
         if (DEBUG) Log.d(TAG, "scale was finished on view: " + mCurrView);
         clearView();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/Nyandroid.java b/packages/SystemUI/src/com/android/systemui/Nyandroid.java
deleted file mode 100644
index 6f168ba..0000000
--- a/packages/SystemUI/src/com/android/systemui/Nyandroid.java
+++ /dev/null
@@ -1,253 +0,0 @@
-/*);
- * Copyright (C) 2011 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.systemui;
-
-import android.animation.AnimatorSet;
-import android.animation.PropertyValuesHolder;
-import android.animation.ObjectAnimator;
-import android.animation.TimeAnimator;
-import android.app.Activity;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.drawable.AnimationDrawable;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.os.Handler;
-import android.util.AttributeSet;
-import android.util.DisplayMetrics;
-import android.util.Pair;
-import android.view.Gravity;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import java.util.HashMap;
-import java.util.Random;
-
-public class Nyandroid extends Activity {
-    final static boolean DEBUG = false;
-
-    public static class Board extends FrameLayout
-    {
-        public static final boolean FIXED_STARS = true;
-        public static final int NUM_CATS = 20;
-
-        static Random sRNG = new Random();
-
-        static float lerp(float a, float b, float f) {
-            return (b-a)*f + a;
-        }
-
-        static float randfrange(float a, float b) {
-            return lerp(a, b, sRNG.nextFloat());
-        }
-
-        static int randsign() {
-            return sRNG.nextBoolean() ? 1 : -1;
-        }
-
-        static <E> E pick(E[] array) {
-            if (array.length == 0) return null;
-            return array[sRNG.nextInt(array.length)];
-        }
-
-        public class FlyingCat extends ImageView {
-            public static final float VMAX = 1000.0f;
-            public static final float VMIN = 100.0f;
-
-            public float v, vr;
-
-            public float dist;
-            public float z;
-
-            public ComponentName component;
-
-            public FlyingCat(Context context, AttributeSet as) {
-                super(context, as);
-                setImageResource(R.drawable.nyandroid_anim); // @@@
-
-                if (DEBUG) setBackgroundColor(0x80FF0000);
-            }
-
-            public String toString() {
-                return String.format("<cat (%.1f, %.1f) (%d x %d)>",
-                    getX(), getY(), getWidth(), getHeight());
-            }
-
-            public void reset() {
-                final float scale = lerp(0.1f,2f,z);
-                setScaleX(scale); setScaleY(scale);
-
-                setX(-scale*getWidth()+1);
-                setY(randfrange(0, Board.this.getHeight()-scale*getHeight()));
-                v = lerp(VMIN, VMAX, z);
-
-                dist = 0;
-
-//                android.util.Log.d("Nyandroid", "reset cat: " + this);
-            }
-
-            public void update(float dt) {
-                dist += v * dt;
-                setX(getX() + v * dt);
-            }
-        }
-
-        TimeAnimator mAnim;
-
-        public Board(Context context, AttributeSet as) {
-            super(context, as);
-
-            setLayerType(View.LAYER_TYPE_HARDWARE, null);
-            setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
-            setBackgroundColor(0xFF003366);
-        }
-
-        private void reset() {
-//            android.util.Log.d("Nyandroid", "board reset");
-            removeAllViews();
-
-            final ViewGroup.LayoutParams wrap = new ViewGroup.LayoutParams(
-                        ViewGroup.LayoutParams.WRAP_CONTENT,
-                        ViewGroup.LayoutParams.WRAP_CONTENT);
-
-            if (FIXED_STARS) {
-                for(int i=0; i<20; i++) {
-                    ImageView fixedStar = new ImageView(getContext(), null);
-                    if (DEBUG) fixedStar.setBackgroundColor(0x8000FF80);
-                    fixedStar.setImageResource(R.drawable.star_anim); // @@@
-                    addView(fixedStar, wrap);
-                    final float scale = randfrange(0.1f, 1f);
-                    fixedStar.setScaleX(scale); fixedStar.setScaleY(scale);
-                    fixedStar.setX(randfrange(0, getWidth()));
-                    fixedStar.setY(randfrange(0, getHeight()));
-                    final AnimationDrawable anim = (AnimationDrawable) fixedStar.getDrawable();
-                    postDelayed(new Runnable() { 
-                        public void run() {
-                            anim.start();
-                        }}, (int) randfrange(0, 1000));
-                }
-            }
-
-            for(int i=0; i<NUM_CATS; i++) {
-                FlyingCat nv = new FlyingCat(getContext(), null);
-                addView(nv, wrap);
-                nv.z = ((float)i/NUM_CATS);
-                nv.z *= nv.z;
-                nv.reset();
-                nv.setX(randfrange(0,Board.this.getWidth()));
-                final AnimationDrawable anim = (AnimationDrawable) nv.getDrawable();
-                postDelayed(new Runnable() { 
-                    public void run() {
-                        anim.start();
-                    }}, (int) randfrange(0, 1000));
-            }
-
-            if (mAnim != null) {
-                mAnim.cancel();
-            }
-            mAnim = new TimeAnimator();
-            mAnim.setTimeListener(new TimeAnimator.TimeListener() {
-                public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
-                    // setRotation(totalTime * 0.01f); // not as cool as you would think
-//                    android.util.Log.d("Nyandroid", "t=" + totalTime);
-
-                    for (int i=0; i<getChildCount(); i++) {
-                        View v = getChildAt(i);
-                        if (!(v instanceof FlyingCat)) continue;
-                        FlyingCat nv = (FlyingCat) v;
-                        nv.update(deltaTime / 1000f);
-                        final float catWidth = nv.getWidth() * nv.getScaleX();
-                        final float catHeight = nv.getHeight() * nv.getScaleY();
-                        if (   nv.getX() + catWidth < -2
-                            || nv.getX() > getWidth() + 2
-                            || nv.getY() + catHeight < -2
-                            || nv.getY() > getHeight() + 2)
-                        {
-                            nv.reset();
-                        }
-                    }
-                }
-            });
-        }
-
-        @Override
-        protected void onSizeChanged (int w, int h, int oldw, int oldh) {
-            super.onSizeChanged(w,h,oldw,oldh);
-//            android.util.Log.d("Nyandroid", "resized: " + w + "x" + h);
-            post(new Runnable() { public void run() { 
-                reset();
-                mAnim.start(); 
-            } });
-        }
-
-
-        @Override
-        protected void onDetachedFromWindow() {
-            super.onDetachedFromWindow();
-            mAnim.cancel();
-        }
-
-        @Override
-        public boolean isOpaque() {
-            return true;
-        }
-    }
-
-    private Board mBoard;
-
-    @Override
-    public void onStart() {
-        super.onStart();
-
-        getWindow().addFlags(
-                  WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
-                | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
-                );
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        mBoard = new Board(this, null);
-        setContentView(mBoard);
-
-        mBoard.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() {
-            @Override
-            public void onSystemUiVisibilityChange(int vis) {
-                if (0 == (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)) {
-                    Nyandroid.this.finish();
-                }
-            }
-        });
-    }
-
-    @Override
-    public void onUserInteraction() {
-//        android.util.Log.d("Nyandroid", "finishing on user interaction");
-        finish();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
index dc2f0be..a8c2020 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
@@ -21,21 +21,21 @@
 import android.animation.AnimatorSet.Builder;
 import android.animation.ObjectAnimator;
 import android.graphics.drawable.Drawable;
-import android.util.Log;
 import android.util.Slog;
 import android.view.View;
+import android.view.ViewRootImpl;
 
 /* package */ class Choreographer implements Animator.AnimatorListener {
     // should group this into a multi-property animation
     private static final int OPEN_DURATION = 136;
-    private static final int CLOSE_DURATION = 250;
+    private static final int CLOSE_DURATION = 130;
     private static final int SCRIM_DURATION = 400;
     private static final String TAG = RecentsPanelView.TAG;
     private static final boolean DEBUG = RecentsPanelView.DEBUG;
 
     boolean mVisible;
     int mPanelHeight;
-    View mRootView;
+    RecentsPanelView mRootView;
     View mScrimView;
     View mContentView;
     View mNoRecentAppsView;
@@ -45,7 +45,7 @@
     // the panel will start to appear this many px from the end
     final int HYPERSPACE_OFFRAMP = 200;
 
-    public Choreographer(View root, View scrim, View content,
+    public Choreographer(RecentsPanelView root, View scrim, View content,
             View noRecentApps, Animator.AnimatorListener listener) {
         mRootView = root;
         mScrimView = scrim;
@@ -67,7 +67,7 @@
             end = 0;
         } else {
             start = y;
-            end = y + HYPERSPACE_OFFRAMP;
+            end = y;
         }
 
         Animator posAnim = ObjectAnimator.ofFloat(mContentView, "translationY",
@@ -77,12 +77,12 @@
                 : new android.view.animation.AccelerateInterpolator(2.5f));
         posAnim.setDuration(appearing ? OPEN_DURATION : CLOSE_DURATION);
 
-        Animator glowAnim = ObjectAnimator.ofFloat(mContentView, "alpha",
+        Animator fadeAnim = ObjectAnimator.ofFloat(mContentView, "alpha",
                 mContentView.getAlpha(), appearing ? 1.0f : 0.0f);
-        glowAnim.setInterpolator(appearing
+        fadeAnim.setInterpolator(appearing
                 ? new android.view.animation.AccelerateInterpolator(1.0f)
                 : new android.view.animation.DecelerateInterpolator(1.0f));
-        glowAnim.setDuration(appearing ? OPEN_DURATION : CLOSE_DURATION);
+        fadeAnim.setDuration(appearing ? OPEN_DURATION : CLOSE_DURATION);
 
         Animator noRecentAppsFadeAnim = null;
         if (mNoRecentAppsView != null &&  // doesn't exist on large devices
@@ -96,7 +96,7 @@
         }
 
         mContentAnim = new AnimatorSet();
-        final Builder builder = mContentAnim.play(glowAnim).with(posAnim);
+        final Builder builder = mContentAnim.play(fadeAnim).with(posAnim);
 
         if (noRecentAppsFadeAnim != null) {
             builder.with(noRecentAppsFadeAnim);
@@ -153,9 +153,10 @@
     public void onAnimationEnd(Animator animation) {
         if (DEBUG) Slog.d(TAG, "onAnimationEnd");
         if (!mVisible) {
-            mRootView.setVisibility(View.GONE);
+            mRootView.hideWindow();
         }
         mContentView.setLayerType(View.LAYER_TYPE_NONE, null);
+        mContentView.setAlpha(1f);
         mContentAnim = null;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index 995ee43..feb1ac8 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -26,7 +26,9 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
+import android.graphics.Canvas;
 import android.graphics.Matrix;
+import android.graphics.Rect;
 import android.graphics.Shader.TileMode;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
@@ -48,11 +50,9 @@
 import android.widget.AdapterView.OnItemClickListener;
 import android.widget.BaseAdapter;
 import android.widget.FrameLayout;
-import android.widget.HorizontalScrollView;
 import android.widget.ImageView;
 import android.widget.ImageView.ScaleType;
 import android.widget.PopupMenu;
-import android.widget.ScrollView;
 import android.widget.TextView;
 
 import com.android.systemui.R;
@@ -83,6 +83,9 @@
     private Choreographer mChoreo;
     OnRecentsPanelVisibilityChangedListener mVisibilityChangedListener;
 
+    ImageView mPlaceholderThumbnail;
+    boolean mHideWindowAfterPlaceholderThumbnailIsHidden;
+
     private RecentTasksLoader mRecentTasksLoader;
     private ArrayList<TaskDescription> mRecentTaskDescriptions;
     private Runnable mPreloadTasksRunnable;
@@ -283,7 +286,9 @@
     public void show(boolean show, boolean animate,
             ArrayList<TaskDescription> recentTaskDescriptions, boolean firstScreenful) {
         // For now, disable animations. We may want to re-enable in the future
-        animate = false;
+        if (show) {
+            animate = false;
+        }
         if (show) {
             // Need to update list of recent apps before we set visibility so this view's
             // content description is updated before it gets focus for TalkBack mode
@@ -687,11 +692,31 @@
                 context.getSystemService(Context.ACTIVITY_SERVICE);
         holder.thumbnailViewImage.setDrawingCacheEnabled(true);
         Bitmap bm = holder.thumbnailViewImage.getDrawingCache();
-        ActivityOptions opts = ActivityOptions.makeThumbnailScaleUpAnimation(
+        mPlaceholderThumbnail = (ImageView) findViewById(R.id.recents_transition_placeholder_icon);
+
+        final ImageView placeholderThumbnail = mPlaceholderThumbnail;
+        mHideWindowAfterPlaceholderThumbnailIsHidden = false;
+        placeholderThumbnail.setVisibility(VISIBLE);
+        Bitmap b2 = bm.copy(bm.getConfig(), true);
+        placeholderThumbnail.setImageBitmap(b2);
+
+        Rect r = new Rect();
+        holder.thumbnailViewImage.getGlobalVisibleRect(r);
+
+        placeholderThumbnail.setTranslationX(r.left);
+        placeholderThumbnail.setTranslationY(r.top);
+
+        show(false, true);
+
+        ActivityOptions opts = ActivityOptions.makeDelayedThumbnailScaleUpAnimation(
                 holder.thumbnailViewImage, bm, 0, 0,
                 new ActivityOptions.OnAnimationStartedListener() {
                     @Override public void onAnimationStarted() {
-                        hide(true);
+                        mPlaceholderThumbnail = null;
+                        placeholderThumbnail.setVisibility(INVISIBLE);
+                        if (mHideWindowAfterPlaceholderThumbnailIsHidden) {
+                            hideWindow();
+                        }
                     }
                 });
         if (ad.taskId >= 0) {
@@ -709,6 +734,15 @@
         holder.thumbnailViewImage.setDrawingCacheEnabled(false);
     }
 
+    public void hideWindow() {
+        if (mPlaceholderThumbnail != null) {
+            mHideWindowAfterPlaceholderThumbnailIsHidden = true;
+        } else {
+            setVisibility(GONE);
+            mHideWindowAfterPlaceholderThumbnailIsHidden = false;
+        }
+    }
+
     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
         handleOnClick(view);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 37fb53d..a310b1d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -35,6 +35,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.provider.Settings;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.Slog;
 import android.view.Display;
@@ -47,6 +48,7 @@
 import android.view.ViewGroup.LayoutParams;
 import android.view.WindowManager;
 import android.view.WindowManagerImpl;
+import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.RemoteViews;
 import android.widget.PopupMenu;
@@ -62,6 +64,7 @@
 import com.android.systemui.recent.RecentTasksLoader;
 import com.android.systemui.recent.TaskDescription;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.policy.NotificationRowLayout;
 import com.android.systemui.statusbar.tablet.StatusBarPanel;
 
 import com.android.systemui.R;
@@ -77,11 +80,24 @@
     protected static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 1023;
     protected static final int MSG_OPEN_SEARCH_PANEL = 1024;
     protected static final int MSG_CLOSE_SEARCH_PANEL = 1025;
+    protected static final int MSG_SHOW_INTRUDER = 1026;
+    protected static final int MSG_HIDE_INTRUDER = 1027;
+
+    protected static final boolean ENABLE_INTRUDERS = false;
+
+    public static final int EXPANDED_LEAVE_ALONE = -10000;
+    public static final int EXPANDED_FULL_OPEN = -10001;
 
     protected CommandQueue mCommandQueue;
     protected IStatusBarService mBarService;
     protected H mHandler = createHandler();
 
+    // all notifications
+    protected NotificationData mNotificationData = new NotificationData();
+    protected NotificationRowLayout mPile;
+
+    protected StatusBarNotification mCurrentlyIntrudingNotification;
+
     // used to notify status bar for suppressing notification LED
     protected boolean mPanelSlightlyVisible;
 
@@ -470,17 +486,7 @@
         // for blaming (see SwipeHelper.setLongPressListener)
         row.setTag(sbn.pkg);
 
-        // XXX: temporary: while testing big notifications, auto-expand all of them
         ViewGroup.LayoutParams lp = row.getLayoutParams();
-        Boolean expandable = Boolean.FALSE;
-        if (large != null) {
-            lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
-            expandable = Boolean.TRUE;
-        } else {
-            lp.height = rowHeight;
-        }
-        row.setLayoutParams(lp);
-        row.setTag(R.id.expandable_tag, expandable);
         workAroundBadLayerDrawableOpacity(row);
         View vetoButton = updateNotificationVetoButton(row, sbn);
         vetoButton.setContentDescription(mContext.getString(
@@ -546,10 +552,11 @@
 
         applyLegacyRowBackground(sbn, content);
 
+        row.setTag(R.id.expandable_tag, Boolean.valueOf(large != null));
         entry.row = row;
         entry.content = content;
         entry.expanded = expandedOneU;
-        entry.expandedLarge = expandedOneU;
+        entry.setLargeView(expandedLarge);
 
         return true;
     }
@@ -634,4 +641,232 @@
         }
     }
 
+    /**
+     * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
+     * about the failure.
+     *
+     * WARNING: this will call back into us.  Don't hold any locks.
+     */
+    void handleNotificationError(IBinder key, StatusBarNotification n, String message) {
+        removeNotification(key);
+        try {
+            mBarService.onNotificationError(n.pkg, n.tag, n.id, n.uid, n.initialPid, message);
+        } catch (RemoteException ex) {
+            // The end is nigh.
+        }
+    }
+
+    protected StatusBarNotification removeNotificationViews(IBinder key) {
+        NotificationData.Entry entry = mNotificationData.remove(key);
+        if (entry == null) {
+            Slog.w(TAG, "removeNotification for unknown key: " + key);
+            return null;
+        }
+        // Remove the expanded view.
+        ViewGroup rowParent = (ViewGroup)entry.row.getParent();
+        if (rowParent != null) rowParent.removeView(entry.row);
+        updateExpansionStates();
+        updateNotificationIcons();
+
+        return entry.notification;
+    }
+
+    protected StatusBarIconView addNotificationViews(IBinder key,
+            StatusBarNotification notification) {
+        if (DEBUG) {
+            Slog.d(TAG, "addNotificationViews(key=" + key + ", notification=" + notification);
+        }
+        // Construct the icon.
+        final StatusBarIconView iconView = new StatusBarIconView(mContext,
+                notification.pkg + "/0x" + Integer.toHexString(notification.id),
+                notification.notification);
+        iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+
+        final StatusBarIcon ic = new StatusBarIcon(notification.pkg,
+                    notification.notification.icon,
+                    notification.notification.iconLevel,
+                    notification.notification.number,
+                    notification.notification.tickerText);
+        if (!iconView.set(ic)) {
+            handleNotificationError(key, notification, "Couldn't create icon: " + ic);
+            return null;
+        }
+        // Construct the expanded view.
+        NotificationData.Entry entry = new NotificationData.Entry(key, notification, iconView);
+        if (!inflateViews(entry, mPile)) {
+            handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
+                    + notification);
+            return null;
+        }
+
+        // Add the expanded view and icon.
+        int pos = mNotificationData.add(entry);
+        if (DEBUG) {
+            Slog.d(TAG, "addNotificationViews: added at " + pos);
+        }
+        updateExpansionStates();
+        updateNotificationIcons();
+
+        return iconView;
+    }
+
+    protected boolean expandView(NotificationData.Entry entry, boolean expand) {
+        if (entry.expandable()) {
+            int rowHeight =
+                    mContext.getResources().getDimensionPixelSize(R.dimen.notification_height);
+            ViewGroup.LayoutParams lp = entry.row.getLayoutParams();
+            if (expand) {
+                lp.height = ViewGroup.LayoutParams.WRAP_CONTENT;
+            } else {
+                lp.height = rowHeight;
+            }
+            entry.row.setLayoutParams(lp);
+            return expand;
+        } else {
+            return false;
+        }
+    }
+
+    protected void updateExpansionStates() {
+        int N = mNotificationData.size();
+        for (int i = 0; i < N; i++) {
+            NotificationData.Entry entry = mNotificationData.get(i);
+            if (i == (N-1)) {
+                if (DEBUG) Slog.d(TAG, "expanding top notification at " + i);
+                expandView(entry, true);
+            } else {
+                if (!entry.userExpanded()) {
+                    if (DEBUG) Slog.d(TAG, "collapsing notification at " + i);
+                    expandView(entry, false);
+                } else {
+                    if (DEBUG) Slog.d(TAG, "ignoring user-modified notification at " + i);
+                }
+            }
+        }
+    }
+
+    protected abstract void haltTicker();
+    protected abstract void setAreThereNotifications();
+    protected abstract void updateNotificationIcons();
+    protected abstract void tick(IBinder key, StatusBarNotification n, boolean firstTime);
+    protected abstract void updateExpandedViewPos(int expandedPosition);
+    protected abstract int getExpandedViewMaxHeight();
+
+    protected boolean isTopNotification(ViewGroup parent, NotificationData.Entry entry) {
+        return parent.indexOfChild(entry.row) == 0;
+    }
+
+    public void updateNotification(IBinder key, StatusBarNotification notification) {
+        if (DEBUG) Slog.d(TAG, "updateNotification(" + key + " -> " + notification + ")");
+
+        final NotificationData.Entry oldEntry = mNotificationData.findByKey(key);
+        if (oldEntry == null) {
+            Slog.w(TAG, "updateNotification for unknown key: " + key);
+            return;
+        }
+
+        final StatusBarNotification oldNotification = oldEntry.notification;
+
+        // XXX: modify when we do something more intelligent with the two content views
+        final RemoteViews oldContentView = (oldNotification.notification.bigContentView != null)
+                ? oldNotification.notification.bigContentView
+                : oldNotification.notification.contentView;
+        final RemoteViews contentView = (notification.notification.bigContentView != null)
+                ? notification.notification.bigContentView
+                : notification.notification.contentView;
+
+        if (DEBUG) {
+            Slog.d(TAG, "old notification: when=" + oldNotification.notification.when
+                    + " ongoing=" + oldNotification.isOngoing()
+                    + " expanded=" + oldEntry.expanded
+                    + " contentView=" + oldContentView
+                    + " rowParent=" + oldEntry.row.getParent());
+            Slog.d(TAG, "new notification: when=" + notification.notification.when
+                    + " ongoing=" + oldNotification.isOngoing()
+                    + " contentView=" + contentView);
+        }
+
+        // Can we just reapply the RemoteViews in place?  If when didn't change, the order
+        // didn't change.
+        boolean contentsUnchanged = oldEntry.expanded != null
+                && contentView != null && oldContentView != null
+                && contentView.getPackage() != null
+                && oldContentView.getPackage() != null
+                && oldContentView.getPackage().equals(contentView.getPackage())
+                && oldContentView.getLayoutId() == contentView.getLayoutId();
+        ViewGroup rowParent = (ViewGroup) oldEntry.row.getParent();
+        boolean orderUnchanged = notification.notification.when==oldNotification.notification.when
+                && notification.score == oldNotification.score;
+                // score now encompasses/supersedes isOngoing()
+
+        boolean updateTicker = notification.notification.tickerText != null
+                && !TextUtils.equals(notification.notification.tickerText,
+                        oldEntry.notification.notification.tickerText);
+        boolean isTopAnyway = isTopNotification(rowParent, oldEntry);
+        if (contentsUnchanged && (orderUnchanged || isTopAnyway)) {
+            if (DEBUG) Slog.d(TAG, "reusing notification for key: " + key);
+            oldEntry.notification = notification;
+            try {
+                // Reapply the RemoteViews
+                contentView.reapply(mContext, oldEntry.content);
+                // update the contentIntent
+                final PendingIntent contentIntent = notification.notification.contentIntent;
+                if (contentIntent != null) {
+                    final View.OnClickListener listener = makeClicker(contentIntent,
+                            notification.pkg, notification.tag, notification.id);
+                    oldEntry.content.setOnClickListener(listener);
+                } else {
+                    oldEntry.content.setOnClickListener(null);
+                }
+                // Update the icon.
+                final StatusBarIcon ic = new StatusBarIcon(notification.pkg,
+                        notification.notification.icon, notification.notification.iconLevel,
+                        notification.notification.number,
+                        notification.notification.tickerText);
+                if (!oldEntry.icon.set(ic)) {
+                    handleNotificationError(key, notification, "Couldn't update icon: " + ic);
+                    return;
+                }
+                updateExpansionStates();
+            }
+            catch (RuntimeException e) {
+                // It failed to add cleanly.  Log, and remove the view from the panel.
+                Slog.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
+                removeNotificationViews(key);
+                addNotificationViews(key, notification);
+            }
+        } else {
+            if (DEBUG) Slog.d(TAG, "not reusing notification for key: " + key);
+            if (DEBUG) Slog.d(TAG, "contents was " + (contentsUnchanged ? "unchanged" : "changed"));
+            if (DEBUG) Slog.d(TAG, "order was " + (orderUnchanged ? "unchanged" : "changed"));
+            if (DEBUG) Slog.d(TAG, "notification is " + (isTopAnyway ? "top" : "not top"));
+            removeNotificationViews(key);
+            addNotificationViews(key, notification);
+        }
+
+        // Update the veto button accordingly (and as a result, whether this row is
+        // swipe-dismissable)
+        updateNotificationVetoButton(oldEntry.row, notification);
+
+        // Restart the ticker if it's still running
+        if (updateTicker) {
+            haltTicker();
+            tick(key, notification, false);
+        }
+
+        // Recalculate the position of the sliding windows and the titles.
+        setAreThereNotifications();
+        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
+
+        // See if we need to update the intruder.
+        if (ENABLE_INTRUDERS && oldNotification == mCurrentlyIntrudingNotification) {
+            if (DEBUG) Slog.d(TAG, "updating the current intruder:" + notification);
+            // XXX: this is a hack for Alarms. The real implementation will need to *update*
+            // the intruder.
+            if (notification.notification.fullScreenIntent == null) { // TODO(dsandler): consistent logic with add()
+                if (DEBUG) Slog.d(TAG, "no longer intrudes!");
+                mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 3ff85d9..1a07ed3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -22,6 +22,7 @@
 import android.widget.ImageView;
 
 import com.android.internal.statusbar.StatusBarNotification;
+import com.android.systemui.R;
 
 import java.util.Comparator;
 import java.util.ArrayList;
@@ -38,13 +39,32 @@
         public View content; // takes the click events and sends the PendingIntent
         public View expanded; // the inflated RemoteViews
         public ImageView largeIcon;
-        public View expandedLarge;
+        protected View expandedLarge;
         public Entry() {}
         public Entry(IBinder key, StatusBarNotification n, StatusBarIconView ic) {
             this.key = key;
             this.notification = n;
             this.icon = ic;
         }
+        public void setLargeView(View expandedLarge) {
+            this.expandedLarge = expandedLarge;
+            writeBooleanTag(row, R.id.expandable_tag, expandedLarge != null);
+        }
+        public View getLargeView() {
+            return expandedLarge;
+        }
+        /**
+         * Return whether the entry can be expanded.
+         */
+        public boolean expandable() {
+            return NotificationData.getIsExpandable(row);
+        }
+        /**
+         * Return whether the entry has been manually expanded by the user.
+         */
+        public boolean userExpanded() {
+            return NotificationData.getUserExpanded(row);
+        }
     }
     private final ArrayList<Entry> mEntries = new ArrayList<Entry>();
     private final Comparator<Entry> mEntryCmp = new Comparator<Entry>() {
@@ -134,4 +154,41 @@
         }
         return false;
     }
+
+    protected static boolean readBooleanTag(View view, int id)  {
+        if (view != null) {
+            Object value = view.getTag(id);
+            return value != null && value instanceof Boolean && ((Boolean) value).booleanValue();
+        }
+        return false;
+    }
+
+    protected static boolean writeBooleanTag(View view, int id, boolean value)  {
+        if (view != null) {
+            view.setTag(id, Boolean.valueOf(value));
+            return value;
+        }
+        return false;
+    }
+
+    /**
+     * Return whether the entry can be expanded.
+     */
+    public static boolean getIsExpandable(View row) {
+        return readBooleanTag(row, R.id.expandable_tag);
+    }
+
+    /**
+     * Return whether the entry has been manually expanded by the user.
+     */
+    public static boolean getUserExpanded(View row) {
+        return readBooleanTag(row, R.id.user_expanded_tag);
+    }
+
+    /**
+     * Set whether the entry has been manually expanded by the user.
+     */
+    public static boolean setUserExpanded(View row, boolean userExpanded) {
+        return writeBooleanTag(row, R.id.user_expanded_tag, userExpanded);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index f5f2e28..d3fbdab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -42,7 +42,6 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.provider.Settings;
-import android.text.TextUtils;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.Slog;
@@ -105,16 +104,10 @@
     public static final String ACTION_STATUSBAR_START
             = "com.android.internal.policy.statusbar.START";
 
-    private static final boolean ENABLE_INTRUDERS = false;
     private static final boolean DIM_BEHIND_EXPANDED_PANEL = false;
 
-    static final int EXPANDED_LEAVE_ALONE = -10000;
-    static final int EXPANDED_FULL_OPEN = -10001;
-
     private static final int MSG_OPEN_NOTIFICATION_PANEL = 1000;
     private static final int MSG_CLOSE_NOTIFICATION_PANEL = 1001;
-    private static final int MSG_SHOW_INTRUDER = 1002;
-    private static final int MSG_HIDE_INTRUDER = 1003;
     // 1020-1030 reserved for BaseStatusBar
 
     // will likely move to a resource or other tunable param at some point
@@ -179,10 +172,6 @@
     CloseDragHandle mCloseView;
     private int mCloseViewHeight;
 
-    // all notifications
-    NotificationData mNotificationData = new NotificationData();
-    NotificationRowLayout mPile;
-
     // position
     int[] mPositionTmp = new int[2];
     boolean mExpanded;
@@ -519,7 +508,7 @@
             toggleRecentApps();
         }
     };
-    private StatusBarNotification mCurrentlyIntrudingNotification;
+
     View.OnTouchListener mHomeSearchActionListener = new View.OnTouchListener() {
         public boolean onTouch(View v, MotionEvent event) {
             switch(event.getAction()) {
@@ -693,7 +682,7 @@
 
             // show the ticker if there isn't an intruder too
             if (mCurrentlyIntrudingNotification == null) {
-                tick(notification);
+                tick(null, notification, true);
             }
         }
 
@@ -702,117 +691,6 @@
         updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
     }
 
-    public void updateNotification(IBinder key, StatusBarNotification notification) {
-        if (DEBUG) Slog.d(TAG, "updateNotification(" + key + " -> " + notification + ")");
-
-        final NotificationData.Entry oldEntry = mNotificationData.findByKey(key);
-        if (oldEntry == null) {
-            Slog.w(TAG, "updateNotification for unknown key: " + key);
-            return;
-        }
-
-        final StatusBarNotification oldNotification = oldEntry.notification;
-
-        // XXX: modify when we do something more intelligent with the two content views
-        final RemoteViews oldContentView = (oldNotification.notification.bigContentView != null) 
-                ? oldNotification.notification.bigContentView
-                : oldNotification.notification.contentView;
-        final RemoteViews contentView = (notification.notification.bigContentView != null) 
-                ? notification.notification.bigContentView
-                : notification.notification.contentView;
-
-        if (DEBUG) {
-            Slog.d(TAG, "old notification: when=" + oldNotification.notification.when
-                    + " ongoing=" + oldNotification.isOngoing()
-                    + " expanded=" + oldEntry.expanded
-                    + " contentView=" + oldContentView
-                    + " rowParent=" + oldEntry.row.getParent());
-            Slog.d(TAG, "new notification: when=" + notification.notification.when
-                    + " ongoing=" + oldNotification.isOngoing()
-                    + " contentView=" + contentView);
-        }
-
-
-        // Can we just reapply the RemoteViews in place?  If when didn't change, the order
-        // didn't change.
-        boolean contentsUnchanged = oldEntry.expanded != null
-                && contentView != null && oldContentView != null
-                && contentView.getPackage() != null
-                && oldContentView.getPackage() != null
-                && oldContentView.getPackage().equals(contentView.getPackage())
-                && oldContentView.getLayoutId() == contentView.getLayoutId();
-        ViewGroup rowParent = (ViewGroup) oldEntry.row.getParent();
-        boolean orderUnchanged = notification.notification.when==oldNotification.notification.when
-                && notification.score == oldNotification.score;
-                // score now encompasses/supersedes isOngoing()
-
-        boolean updateTicker = notification.notification.tickerText != null
-                && !TextUtils.equals(notification.notification.tickerText,
-                        oldEntry.notification.notification.tickerText);
-        boolean isFirstAnyway = rowParent.indexOfChild(oldEntry.row) == 0;
-        if (contentsUnchanged && (orderUnchanged || isFirstAnyway)) {
-            if (DEBUG) Slog.d(TAG, "reusing notification for key: " + key);
-            oldEntry.notification = notification;
-            try {
-                // Reapply the RemoteViews
-                contentView.reapply(mContext, oldEntry.content);
-                // update the contentIntent
-                final PendingIntent contentIntent = notification.notification.contentIntent;
-                if (contentIntent != null) {
-                    final View.OnClickListener listener = new NotificationClicker(contentIntent,
-                            notification.pkg, notification.tag, notification.id);
-                    oldEntry.content.setOnClickListener(listener);
-                } else {
-                    oldEntry.content.setOnClickListener(null);
-                }
-                // Update the icon.
-                final StatusBarIcon ic = new StatusBarIcon(notification.pkg,
-                        notification.notification.icon, notification.notification.iconLevel,
-                        notification.notification.number,
-                        notification.notification.tickerText);
-                if (!oldEntry.icon.set(ic)) {
-                    handleNotificationError(key, notification, "Couldn't update icon: " + ic);
-                    return;
-                }
-            }
-            catch (RuntimeException e) {
-                // It failed to add cleanly.  Log, and remove the view from the panel.
-                Slog.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
-                removeNotificationViews(key);
-                addNotificationViews(key, notification);
-            }
-        } else {
-            if (SPEW) Slog.d(TAG, "not reusing notification");
-            removeNotificationViews(key);
-            addNotificationViews(key, notification);
-        }
-
-        // Update the veto button accordingly (and as a result, whether this row is
-        // swipe-dismissable)
-        updateNotificationVetoButton(oldEntry.row, notification);
-
-        // Restart the ticker if it's still running
-        if (updateTicker) {
-            mTicker.halt();
-            tick(notification);
-        }
-
-        // Recalculate the position of the sliding windows and the titles.
-        setAreThereNotifications();
-        updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
-
-        // See if we need to update the intruder.
-        if (ENABLE_INTRUDERS && oldNotification == mCurrentlyIntrudingNotification) {
-            if (DEBUG) Slog.d(TAG, "updating the current intruder:" + notification);
-            // XXX: this is a hack for Alarms. The real implementation will need to *update* 
-            // the intruder.
-            if (notification.notification.fullScreenIntent == null) { // TODO(dsandler): consistent logic with add()
-                if (DEBUG) Slog.d(TAG, "no longer intrudes!");
-                mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
-            }
-        }
-    }
-
     public void removeNotification(IBinder key) {
         StatusBarNotification old = removeNotificationViews(key);
         if (SPEW) Slog.d(TAG, "removeNotification key=" + key + " old=" + old);
@@ -841,44 +719,6 @@
         updateRecentsPanel();
     }
 
-
-    StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) {
-        if (DEBUG) {
-            Slog.d(TAG, "addNotificationViews(key=" + key + ", notification=" + notification);
-        }
-        // Construct the icon.
-        final StatusBarIconView iconView = new StatusBarIconView(mContext,
-                notification.pkg + "/0x" + Integer.toHexString(notification.id),
-                notification.notification);
-        iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
-
-        final StatusBarIcon ic = new StatusBarIcon(notification.pkg,
-                    notification.notification.icon,
-                    notification.notification.iconLevel,
-                    notification.notification.number,
-                    notification.notification.tickerText);
-        if (!iconView.set(ic)) {
-            handleNotificationError(key, notification, "Couldn't create icon: " + ic);
-            return null;
-        }
-        // Construct the expanded view.
-        NotificationData.Entry entry = new NotificationData.Entry(key, notification, iconView);
-        if (!inflateViews(entry, mPile)) {
-            handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
-                    + notification);
-            return null;
-        }
-
-        // Add the expanded view and icon.
-        int pos = mNotificationData.add(entry);
-        if (DEBUG) {
-            Slog.d(TAG, "addNotificationViews: added at " + pos);
-        }
-        updateNotificationIcons();
-
-        return iconView;
-    }
-
     private void loadNotificationShade() {
         int N = mNotificationData.size();
 
@@ -915,7 +755,8 @@
         updateNotificationIcons();
     }
 
-    private void updateNotificationIcons() {
+    @Override
+    protected void updateNotificationIcons() {
         loadNotificationShade();
 
         final LinearLayout.LayoutParams params
@@ -956,21 +797,8 @@
         }
     }
 
-    StatusBarNotification removeNotificationViews(IBinder key) {
-        NotificationData.Entry entry = mNotificationData.remove(key);
-        if (entry == null) {
-            Slog.w(TAG, "removeNotification for unknown key: " + key);
-            return null;
-        }
-        // Remove the expanded view.
-        ViewGroup rowParent = (ViewGroup)entry.row.getParent();
-        if (rowParent != null) rowParent.removeView(entry.row);
-        updateNotificationIcons();
-
-        return entry.notification;
-    }
-
-    private void setAreThereNotifications() {
+    @Override
+    protected void setAreThereNotifications() {
         final boolean any = mNotificationData.size() > 0;
 
         final boolean clearable = any && mNotificationData.hasClearableItems();
@@ -1754,7 +1582,8 @@
         }
     }
 
-    private void tick(StatusBarNotification n) {
+    @Override
+    protected void tick(IBinder key, StatusBarNotification n, boolean firstTime) {
         // no ticking in lights-out mode
         if (!areLightsOn()) return;
         
@@ -1770,21 +1599,6 @@
         }
     }
 
-    /**
-     * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService
-     * about the failure.
-     *
-     * WARNING: this will call back into us.  Don't hold any locks.
-     */
-    void handleNotificationError(IBinder key, StatusBarNotification n, String message) {
-        removeNotification(key);
-        try {
-            mBarService.onNotificationError(n.pkg, n.tag, n.id, n.uid, n.initialPid, message);
-        } catch (RemoteException ex) {
-            // The end is nigh.
-        }
-    }
-
     private class MyTicker extends Ticker {
         MyTicker(Context context, View sb) {
             super(context, sb);
@@ -1957,11 +1771,13 @@
         return a < 0f ? 0f : (a > 1f ? 1f : a);
     }
 
-    int getExpandedViewMaxHeight() {
+    @Override
+    protected int getExpandedViewMaxHeight() {
         return mDisplayMetrics.heightPixels - mNotificationPanelMarginBottomPx;
     }
 
-    void updateExpandedViewPos(int expandedPosition) {
+    @Override
+    protected void updateExpandedViewPos(int expandedPosition) {
         if (SPEW) {
             Slog.d(TAG, "updateExpandedViewPos before expandedPosition=" + expandedPosition
                     //+ " mTrackingParams.y=" + ((mTrackingParams == null) ? "?" : mTrackingParams.y)
@@ -2288,5 +2104,10 @@
             vibrate();
         }
     };
+
+    @Override
+    protected void haltTicker() {
+        mTicker.halt();
+    }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index a8f5c64..a9cc62a4d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -31,6 +31,7 @@
 import android.widget.FrameLayout;
 
 import com.android.systemui.R;
+import com.android.systemui.statusbar.BaseStatusBar;
 import com.android.systemui.statusbar.policy.FixedSizeDrawable;
 
 public class PhoneStatusBarView extends FrameLayout {
@@ -95,7 +96,7 @@
     @Override
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
         super.onSizeChanged(w, h, oldw, oldh);
-        mService.updateExpandedViewPos(PhoneStatusBar.EXPANDED_LEAVE_ALONE);
+        mService.updateExpandedViewPos(BaseStatusBar.EXPANDED_LEAVE_ALONE);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
index 03dfd1c..0fe7a0a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
@@ -39,6 +39,7 @@
 import com.android.systemui.Gefingerpoken;
 import com.android.systemui.R;
 import com.android.systemui.SwipeHelper;
+import com.android.systemui.statusbar.NotificationData;
 
 import java.util.HashMap;
 
@@ -175,9 +176,11 @@
     }
 
     public boolean canChildBeExpanded(View v) {
-        Object isExpandable = v.getTag(R.id.expandable_tag);
-        return isExpandable != null && isExpandable instanceof Boolean &&
-                ((Boolean)isExpandable).booleanValue();
+        return NotificationData.getIsExpandable(v);
+    }
+
+    public boolean setUserExpandedChild(View v, boolean userExpanded) {
+        return NotificationData.setUserExpanded(v, userExpanded);
     }
 
     public void onChildDismissed(View v) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index b0830ee..906d1aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -109,7 +109,6 @@
     private static final boolean FAKE_SPACE_BAR = true;
 
     // Notification "peeking" (flyover preview of individual notifications)
-    final static boolean NOTIFICATION_PEEK_ENABLED = false;
     final static int NOTIFICATION_PEEK_HOLD_THRESH = 200; // ms
     final static int NOTIFICATION_PEEK_FADE_DELAY = 3000; // ms
 
@@ -127,9 +126,6 @@
 
     IWindowManager mWindowManager;
 
-    // tracking all current notifications
-    private NotificationData mNotificationData = new NotificationData();
-
     TabletStatusBarView mStatusBarView;
     View mNotificationArea;
     View mNotificationTrigger;
@@ -160,8 +156,6 @@
     int mNotificationPeekTapDuration;
     int mNotificationFlingVelocity;
 
-    NotificationRowLayout mPile;
-
     BatteryController mBatteryController;
     BluetoothController mBluetoothController;
     LocationController mLocationController;
@@ -290,47 +284,6 @@
 
         WindowManagerImpl.getDefault().addView(mNotificationPanel, lp);
 
-        // Notification preview window
-        if (NOTIFICATION_PEEK_ENABLED) {
-            mNotificationPeekWindow = (NotificationPeekPanel) View.inflate(context,
-                    R.layout.system_bar_notification_peek, null);
-            mNotificationPeekWindow.setBar(this);
-
-            mNotificationPeekRow = (ViewGroup) mNotificationPeekWindow.findViewById(R.id.content);
-            mNotificationPeekWindow.setVisibility(View.GONE);
-            mNotificationPeekWindow.setOnTouchListener(
-                    new TouchOutsideListener(MSG_CLOSE_NOTIFICATION_PEEK, mNotificationPeekWindow));
-            mNotificationPeekScrubRight = new LayoutTransition();
-            mNotificationPeekScrubRight.setAnimator(LayoutTransition.APPEARING,
-                    ObjectAnimator.ofInt(null, "left", -512, 0));
-            mNotificationPeekScrubRight.setAnimator(LayoutTransition.DISAPPEARING,
-                    ObjectAnimator.ofInt(null, "left", -512, 0));
-            mNotificationPeekScrubRight.setDuration(500);
-
-            mNotificationPeekScrubLeft = new LayoutTransition();
-            mNotificationPeekScrubLeft.setAnimator(LayoutTransition.APPEARING,
-                    ObjectAnimator.ofInt(null, "left", 512, 0));
-            mNotificationPeekScrubLeft.setAnimator(LayoutTransition.DISAPPEARING,
-                    ObjectAnimator.ofInt(null, "left", 512, 0));
-            mNotificationPeekScrubLeft.setDuration(500);
-
-            // XXX: setIgnoreChildren?
-            lp = new WindowManager.LayoutParams(
-                    512, // ViewGroup.LayoutParams.WRAP_CONTENT,
-                    ViewGroup.LayoutParams.WRAP_CONTENT,
-                    WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
-                    WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
-                        | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
-                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
-                    PixelFormat.TRANSLUCENT);
-            lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
-            lp.y = res.getDimensionPixelOffset(R.dimen.peek_window_y_offset);
-            lp.setTitle("NotificationPeekWindow");
-            lp.windowAnimations = com.android.internal.R.style.Animation_Toast;
-
-            WindowManagerImpl.getDefault().addView(mNotificationPeekWindow, lp);
-        }
-
         // Recents Panel
         mRecentTasksLoader = new RecentTasksLoader(context);
         updateRecentsPanel();
@@ -395,6 +348,11 @@
         scroller.setFillViewport(true);
     }
 
+    @Override
+    protected int getExpandedViewMaxHeight() {
+        return getNotificationPanelHeight();
+    }
+
     private int getNotificationPanelHeight() {
         final Resources res = mContext.getResources();
         final Display d = WindowManagerImpl.getDefault().getDefaultDisplay();
@@ -494,24 +452,16 @@
 
         // the whole right-hand side of the bar
         mNotificationArea = sb.findViewById(R.id.notificationArea);
-        if (!NOTIFICATION_PEEK_ENABLED) {
-            mNotificationArea.setOnTouchListener(new NotificationTriggerTouchListener());
-        }
+        mNotificationArea.setOnTouchListener(new NotificationTriggerTouchListener());
 
         // the button to open the notification area
         mNotificationTrigger = sb.findViewById(R.id.notificationTrigger);
-        if (NOTIFICATION_PEEK_ENABLED) {
-            mNotificationTrigger.setOnTouchListener(new NotificationTriggerTouchListener());
-        }
 
         // the more notifications icon
         mNotificationIconArea = (NotificationIconArea)sb.findViewById(R.id.notificationIcons);
 
         // where the icons go
         mIconLayout = (NotificationIconArea.IconLayout) sb.findViewById(R.id.icons);
-        if (NOTIFICATION_PEEK_ENABLED) {
-            mIconLayout.setOnTouchListener(new NotificationIconTouchListener());
-        }
 
         ViewConfiguration vc = ViewConfiguration.get(context);
         mNotificationPeekTapDuration = vc.getTapTimeout();
@@ -827,9 +777,6 @@
                 case MSG_OPEN_NOTIFICATION_PANEL:
                     if (DEBUG) Slog.d(TAG, "opening notifications panel");
                     if (!mNotificationPanel.isShowing()) {
-                        if (NOTIFICATION_PEEK_ENABLED) {
-                            mNotificationPeekWindow.setVisibility(View.GONE);
-                        }
                         mNotificationPanel.show(true, true);
                         mNotificationArea.setVisibility(View.INVISIBLE);
                         mTicker.halt();
@@ -916,106 +863,6 @@
         setAreThereNotifications();
     }
 
-    public void updateNotification(IBinder key, StatusBarNotification notification) {
-        if (DEBUG) Slog.d(TAG, "updateNotification(" + key + " -> " + notification + ")");
-
-        final NotificationData.Entry oldEntry = mNotificationData.findByKey(key);
-        if (oldEntry == null) {
-            Slog.w(TAG, "updateNotification for unknown key: " + key);
-            return;
-        }
-
-        final StatusBarNotification oldNotification = oldEntry.notification;
-
-        // XXX: modify when we do something more intelligent with the two content views
-        final RemoteViews oldContentView = (oldNotification.notification.bigContentView != null) 
-                ? oldNotification.notification.bigContentView
-                : oldNotification.notification.contentView;
-        final RemoteViews contentView = (notification.notification.bigContentView != null) 
-                ? notification.notification.bigContentView
-                : notification.notification.contentView;
-
-        if (DEBUG) {
-            Slog.d(TAG, "old notification: when=" + oldNotification.notification.when
-                    + " ongoing=" + oldNotification.isOngoing()
-                    + " expanded=" + oldEntry.expanded
-                    + " contentView=" + oldContentView
-                    + " rowParent=" + oldEntry.row.getParent());
-            Slog.d(TAG, "new notification: when=" + notification.notification.when
-                    + " ongoing=" + oldNotification.isOngoing()
-                    + " contentView=" + contentView);
-        }
-
-        // Can we just reapply the RemoteViews in place?  If when didn't change, the order
-        // didn't change.
-        boolean contentsUnchanged = oldEntry.expanded != null
-                && contentView != null && oldContentView != null
-                && contentView.getPackage() != null
-                && oldContentView.getPackage() != null
-                && oldContentView.getPackage().equals(contentView.getPackage())
-                && oldContentView.getLayoutId() == contentView.getLayoutId();
-        ViewGroup rowParent = (ViewGroup) oldEntry.row.getParent();
-        boolean orderUnchanged = notification.notification.when==oldNotification.notification.when
-                && notification.score == oldNotification.score;
-                // score now encompasses/supersedes isOngoing()
-        boolean updateTicker = notification.notification.tickerText != null
-                && !TextUtils.equals(notification.notification.tickerText,
-                        oldEntry.notification.notification.tickerText);
-        boolean isLastAnyway = rowParent.indexOfChild(oldEntry.row) == rowParent.getChildCount()-1;
-        if (contentsUnchanged && (orderUnchanged || isLastAnyway)) {
-            if (DEBUG) Slog.d(TAG, "reusing notification for key: " + key);
-            oldEntry.notification = notification;
-            try {
-                // Reapply the RemoteViews
-                contentView.reapply(mContext, oldEntry.content);
-                // update the contentIntent
-                final PendingIntent contentIntent = notification.notification.contentIntent;
-                if (contentIntent != null) {
-                    final View.OnClickListener listener = makeClicker(contentIntent,
-                            notification.pkg, notification.tag, notification.id);
-                    oldEntry.content.setOnClickListener(listener);
-                } else {
-                    oldEntry.content.setOnClickListener(null);
-                }
-                // Update the icon.
-                final StatusBarIcon ic = new StatusBarIcon(notification.pkg,
-                        notification.notification.icon, notification.notification.iconLevel,
-                        notification.notification.number,
-                        notification.notification.tickerText);
-                if (!oldEntry.icon.set(ic)) {
-                    handleNotificationError(key, notification, "Couldn't update icon: " + ic);
-                    return;
-                }
-
-                if (NOTIFICATION_PEEK_ENABLED && key == mNotificationPeekKey) {
-                    // must update the peek window
-                    Message peekMsg = mHandler.obtainMessage(MSG_OPEN_NOTIFICATION_PEEK);
-                    peekMsg.arg1 = mNotificationPeekIndex;
-                    mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PEEK);
-                    mHandler.sendMessage(peekMsg);
-                }
-            }
-            catch (RuntimeException e) {
-                // It failed to add cleanly.  Log, and remove the view from the panel.
-                Slog.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
-                removeNotificationViews(key);
-                addNotificationViews(key, notification);
-            }
-        } else {
-            if (DEBUG) Slog.d(TAG, "not reusing notification for key: " + key);
-            removeNotificationViews(key);
-            addNotificationViews(key, notification);
-        }
-
-        // Restart the ticker if it's still running
-        if (updateTicker) {
-            mTicker.halt();
-            tick(key, notification, false);
-        }
-
-        setAreThereNotifications();
-    }
-
     public void removeNotification(IBinder key) {
         if (DEBUG) Slog.d(TAG, "removeNotification(" + key + ")");
         removeNotificationViews(key);
@@ -1105,7 +952,8 @@
         return n.tickerView != null || !TextUtils.isEmpty(n.tickerText);
     }
 
-    private void tick(IBinder key, StatusBarNotification n, boolean firstTime) {
+    @Override
+    protected void tick(IBinder key, StatusBarNotification n, boolean firstTime) {
         // Don't show the ticker when the windowshade is open.
         if (mNotificationPanel.isShowing()) {
             return;
@@ -1134,11 +982,6 @@
     }
 
     public void animateExpand() {
-        if (NOTIFICATION_PEEK_ENABLED) {
-            mHandler.removeMessages(MSG_CLOSE_NOTIFICATION_PEEK);
-            mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PEEK);
-            mHandler.sendEmptyMessage(MSG_CLOSE_NOTIFICATION_PEEK);
-        }
         mHandler.removeMessages(MSG_OPEN_NOTIFICATION_PANEL);
         mHandler.sendEmptyMessage(MSG_OPEN_NOTIFICATION_PANEL);
     }
@@ -1158,10 +1001,6 @@
         mHandler.sendEmptyMessage(MSG_CLOSE_INPUT_METHODS_PANEL);
         mHandler.removeMessages(MSG_CLOSE_COMPAT_MODE_PANEL);
         mHandler.sendEmptyMessage(MSG_CLOSE_COMPAT_MODE_PANEL);
-        if (NOTIFICATION_PEEK_ENABLED) {
-            mHandler.removeMessages(MSG_CLOSE_NOTIFICATION_PEEK);
-            mHandler.sendEmptyMessage(MSG_CLOSE_NOTIFICATION_PEEK);
-        }
     }
 
     @Override // CommandQueue
@@ -1350,24 +1189,13 @@
         }
     }
 
-    private void setAreThereNotifications() {
+    @Override
+    protected void setAreThereNotifications() {
         if (mNotificationPanel != null) {
             mNotificationPanel.setClearable(mNotificationData.hasClearableItems());
         }
     }
 
-    /**
-     * Cancel this notification and tell the status bar service about the failure. Hold no locks.
-     */
-    void handleNotificationError(IBinder key, StatusBarNotification n, String message) {
-        removeNotification(key);
-        try {
-            mBarService.onNotificationError(n.pkg, n.tag, n.id, n.uid, n.initialPid, message);
-        } catch (RemoteException ex) {
-            // The end is nigh.
-        }
-    }
-
     private View.OnClickListener mOnClickListener = new View.OnClickListener() {
         public void onClick(View v) {
             if (v == mRecentButton) {
@@ -1405,28 +1233,6 @@
         mHandler.sendEmptyMessage(msg);
     }
 
-    StatusBarNotification removeNotificationViews(IBinder key) {
-        NotificationData.Entry entry = mNotificationData.remove(key);
-        if (entry == null) {
-            Slog.w(TAG, "removeNotification for unknown key: " + key);
-            return null;
-        }
-        // Remove the expanded view.
-        ViewGroup rowParent = (ViewGroup)entry.row.getParent();
-        if (rowParent != null) rowParent.removeView(entry.row);
-
-        if (NOTIFICATION_PEEK_ENABLED && key == mNotificationPeekKey) {
-            // must close the peek as well, since it's gone
-            mHandler.sendEmptyMessage(MSG_CLOSE_NOTIFICATION_PEEK);
-        }
-        // Remove the icon.
-//        ViewGroup iconParent = (ViewGroup)entry.icon.getParent();
-//        if (iconParent != null) iconParent.removeView(entry.icon);
-        updateNotificationIcons();
-
-        return entry.notification;
-    }
-
     private class NotificationTriggerTouchListener implements View.OnTouchListener {
         VelocityTracker mVT;
         float mInitialTouchX, mInitialTouchY;
@@ -1619,50 +1425,14 @@
         }
     }
 
-    StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) {
-        if (DEBUG) {
-            Slog.d(TAG, "addNotificationViews(key=" + key + ", notification=" + notification);
-        }
-        // Construct the icon.
-        final StatusBarIconView iconView = new StatusBarIconView(mContext,
-                notification.pkg + "/0x" + Integer.toHexString(notification.id),
-                notification.notification);
-        iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
-
-        final StatusBarIcon ic = new StatusBarIcon(notification.pkg,
-                    notification.notification.icon,
-                    notification.notification.iconLevel,
-                    notification.notification.number,
-                    notification.notification.tickerText);
-        if (!iconView.set(ic)) {
-            handleNotificationError(key, notification, "Couldn't attach StatusBarIcon: " + ic);
-            return null;
-        }
-        // Construct the expanded view.
-        NotificationData.Entry entry = new NotificationData.Entry(key, notification, iconView);
-        if (!inflateViews(entry, mPile)) {
-            handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
-                    + notification);
-            return null;
-        }
-
-        // Add the icon.
-        int pos = mNotificationData.add(entry);
-        if (DEBUG) {
-            Slog.d(TAG, "addNotificationViews: added at " + pos);
-        }
-        updateNotificationIcons();
-
-        return iconView;
-    }
-
     private void reloadAllNotificationIcons() {
         if (mIconLayout == null) return;
         mIconLayout.removeAllViews();
         updateNotificationIcons();
     }
 
-    private void updateNotificationIcons() {
+    @Override
+    protected void updateNotificationIcons() {
         // XXX: need to implement a new limited linear layout class
         // to avoid removing & readding everything
 
@@ -1835,6 +1605,19 @@
         mNetworkController.dump(fd, pw, args);
     }
 
+    @Override
+    protected boolean isTopNotification(ViewGroup parent, NotificationData.Entry entry) {
+        return parent.indexOfChild(entry.row) == parent.getChildCount()-1;
+    }
+
+    @Override
+    protected void haltTicker() {
+        mTicker.halt();
+    }
+
+    @Override
+    protected void updateExpandedViewPos(int expandedPosition) {
+    }
 }
 
 
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 97bfd6f..ad80273 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -552,7 +552,8 @@
 
     void applyOptionsLocked() {
         if (pendingOptions != null) {
-            switch (pendingOptions.getAnimationType()) {
+            final int animationType = pendingOptions.getAnimationType();
+            switch (animationType) {
                 case ActivityOptions.ANIM_CUSTOM:
                     service.mWindowManager.overridePendingAppTransition(
                             pendingOptions.getPackageName(),
@@ -571,10 +572,13 @@
                     }
                     break;
                 case ActivityOptions.ANIM_THUMBNAIL:
+                case ActivityOptions.ANIM_THUMBNAIL_DELAYED:
+                    boolean delayed = (animationType == ActivityOptions.ANIM_THUMBNAIL_DELAYED);
                     service.mWindowManager.overridePendingAppTransitionThumb(
                             pendingOptions.getThumbnail(),
                             pendingOptions.getStartX(), pendingOptions.getStartY(),
-                            pendingOptions.getOnAnimationStartListener());
+                            pendingOptions.getOnAnimationStartListener(),
+                            delayed);
                     if (intent.getSourceBounds() == null) {
                         intent.setSourceBounds(new Rect(pendingOptions.getStartX(),
                                 pendingOptions.getStartY(),
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index e710b33..0e93b0a 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -608,17 +608,19 @@
 
         // clamp threshold into safe range
         mPersistThreshold = MathUtils.constrain(thresholdBytes, 128 * KB_IN_BYTES, 2 * MB_IN_BYTES);
-        updatePersistThresholds();
-
         if (LOGV) {
             Slog.v(TAG, "advisePersistThreshold() given " + thresholdBytes + ", clamped to "
                     + mPersistThreshold);
         }
 
-        // persist if beyond new thresholds
+        // update and persist if beyond new thresholds
         final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
                 : System.currentTimeMillis();
         synchronized (mStatsLock) {
+            if (!mSystemReady) return;
+
+            updatePersistThresholds();
+
             mDevRecorder.maybePersistLocked(currentTime);
             mXtRecorder.maybePersistLocked(currentTime);
             mUidRecorder.maybePersistLocked(currentTime);
diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java
index 526c204..a115345c 100644
--- a/services/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/java/com/android/server/usb/UsbDeviceManager.java
@@ -322,8 +322,6 @@
                 String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim();
                 updateState(state);
                 mAdbEnabled = containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ADB);
-                mAudioSourceEnabled = containsFunction(mCurrentFunctions,
-                        UsbManager.USB_FUNCTION_AUDIO_SOURCE);
 
                 // Upgrade step for previous versions that used persist.service.adb.enable
                 String value = SystemProperties.get("persist.service.adb.enable", "");
@@ -537,26 +535,29 @@
             mContext.sendStickyBroadcast(intent);
         }
 
-        private void updateAudioSourceFunction(boolean enabled) {
-            // send a sticky broadcast containing current USB state
-            Intent intent = new Intent(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG);
-            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
-            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-            intent.putExtra("state", (enabled ? 1 : 0));
-            if (enabled) {
-                try {
-                    Scanner scanner = new Scanner(new File(AUDIO_SOURCE_PCM_PATH));
-                    int card = scanner.nextInt();
-                    int device = scanner.nextInt();
-                    intent.putExtra("card", card);
-                    intent.putExtra("device", device);
-                } catch (FileNotFoundException e) {
-                    Slog.e(TAG, "could not open audio source PCM file", e);
+        private void updateAudioSourceFunction() {
+            boolean enabled = containsFunction(mCurrentFunctions,
+                    UsbManager.USB_FUNCTION_AUDIO_SOURCE);
+            if (enabled != mAudioSourceEnabled) {
+                // send a sticky broadcast containing current USB state
+                Intent intent = new Intent(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG);
+                intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+                intent.putExtra("state", (enabled ? 1 : 0));
+                if (enabled) {
+                    try {
+                        Scanner scanner = new Scanner(new File(AUDIO_SOURCE_PCM_PATH));
+                        int card = scanner.nextInt();
+                        int device = scanner.nextInt();
+                        intent.putExtra("card", card);
+                        intent.putExtra("device", device);
+                    } catch (FileNotFoundException e) {
+                        Slog.e(TAG, "could not open audio source PCM file", e);
+                    }
                 }
+                mContext.sendStickyBroadcast(intent);
+                mAudioSourceEnabled = enabled;
             }
-
-            mContext.sendStickyBroadcast(intent);
-            mAudioSourceEnabled = enabled;
         }
 
         @Override
@@ -578,11 +579,7 @@
                     }
                     if (mBootCompleted) {
                         updateUsbState();
-                        boolean audioSourceEnabled = containsFunction(mCurrentFunctions,
-                                UsbManager.USB_FUNCTION_AUDIO_SOURCE);
-                        if (audioSourceEnabled != mAudioSourceEnabled) {
-                            updateAudioSourceFunction(audioSourceEnabled);
-                        }
+                        updateAudioSourceFunction();
                     }
                     break;
                 case MSG_ENABLE_ADB:
@@ -597,13 +594,13 @@
                     updateUsbNotification();
                     updateAdbNotification();
                     updateUsbState();
+                    updateAudioSourceFunction();
                     break;
                 case MSG_BOOT_COMPLETED:
                     mBootCompleted = true;
                     if (mCurrentAccessory != null) {
                         mSettingsManager.accessoryAttached(mCurrentAccessory);
                     }
-                    updateAudioSourceFunction(mAudioSourceEnabled);
                     break;
             }
         }
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index 5536559..146de34 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -454,6 +454,7 @@
         mPendingLayoutChanges = 0;
         mCurrentTime = SystemClock.uptimeMillis();
         mBulkUpdateParams = 0;
+        boolean wasAnimating = mAnimating;
         mAnimating = false;
         if (WindowManagerService.DEBUG_WINDOW_TRACE) {
             Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
@@ -509,6 +510,8 @@
 
         if (mAnimating) {
             mService.scheduleAnimationLocked();
+        } else if (wasAnimating) {
+            mService.requestTraversalLocked();
         }
         if (WindowManagerService.DEBUG_WINDOW_TRACE) {
             Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
@@ -548,6 +551,10 @@
         return mDimParams != null;
     }
 
+    boolean isDimming(final WindowStateAnimator winAnimator) {
+        return mDimParams != null && mDimParams.mDimWinAnimator == winAnimator;
+    }
+
     public void dump(PrintWriter pw, String prefix, boolean dumpAll) {
         if (mWindowDetachedWallpaper != null) {
             pw.print("  mWindowDetachedWallpaper="); pw.println(mWindowDetachedWallpaper);
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 8957edf..2efcb8e 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -512,6 +512,7 @@
     int mNextAppTransitionType = ActivityOptions.ANIM_NONE;
     String mNextAppTransitionPackage;
     Bitmap mNextAppTransitionThumbnail;
+    boolean mNextAppTransitionDelayed;
     IRemoteCallback mNextAppTransitionCallback;
     int mNextAppTransitionEnter;
     int mNextAppTransitionExit;
@@ -3176,7 +3177,7 @@
     }
 
     private Animation createThumbnailAnimationLocked(int transit,
-            boolean enter, boolean thumb) {
+            boolean enter, boolean thumb, boolean delayed) {
         Animation a;
         final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
         final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
@@ -3186,6 +3187,7 @@
         // it  is the standard duration for that.  Otherwise we use the longer
         // task transition duration.
         int duration;
+        int delayDuration = delayed ? 200 : 0;
         switch (transit) {
             case WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN:
             case WindowManagerPolicy.TRANSIT_ACTIVITY_CLOSE:
@@ -3193,7 +3195,7 @@
                         com.android.internal.R.integer.config_shortAnimTime);
                 break;
             default:
-                duration = 300;
+                duration = delayed ? 200 : 300;
                 break;
         }
         if (thumb) {
@@ -3201,6 +3203,7 @@
             // filling the screen.
             float scaleW = mAppDisplayWidth/thumbWidth;
             float scaleH = mAppDisplayHeight/thumbHeight;
+
             Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
                     computePivot(mNextAppTransitionStartX, 1/scaleW),
                     computePivot(mNextAppTransitionStartY, 1/scaleH));
@@ -3210,17 +3213,38 @@
             set.addAnimation(scale);
             alpha.setDuration(duration);
             set.addAnimation(alpha);
+            set.setFillBefore(true);
+            if (delayDuration > 0) {
+                set.setStartOffset(delayDuration);
+            }
             a = set;
         } else if (enter) {
             // Entering app zooms out from the center of the thumbnail.
-            float scaleW = thumbWidth/mAppDisplayWidth;
-            float scaleH = thumbHeight/mAppDisplayHeight;
-            a = new ScaleAnimation(scaleW, 1, scaleH, 1,
+            float scaleW = thumbWidth / mAppDisplayWidth;
+            float scaleH = thumbHeight / mAppDisplayHeight;
+            AnimationSet set = new AnimationSet(true);
+            Animation scale = new ScaleAnimation(scaleW, 1, scaleH, 1,
                     computePivot(mNextAppTransitionStartX, scaleW),
                     computePivot(mNextAppTransitionStartY, scaleH));
-            a.setDuration(duration);
+            scale.setDuration(duration);
+            scale.setFillBefore(true);
+            set.addAnimation(scale);
+            // Need to set an alpha animation on the entering app window
+            // in case it appears one frame before the thumbnail window
+            // (this solves flicker)
+            Animation alpha = new AlphaAnimation(0, 1);
+            alpha.setDuration(1);
+            alpha.setFillAfter(true);
+            set.addAnimation(alpha);
+            a = set;
+            if (delayDuration > 0) {
+                a.setStartOffset(delayDuration);
+            }
         } else {
             a = createExitAnimationLocked(transit, duration);
+            if (delayDuration > 0) {
+                a.setStartOffset(delayDuration);
+            }
         }
         a.setFillAfter(true);
         final Interpolator interpolator = AnimationUtils.loadInterpolator(mContext,
@@ -3252,12 +3276,18 @@
                 if (DEBUG_ANIM) Slog.v(TAG, "applyAnimation: wtoken=" + wtoken
                         + " anim=" + a + " nextAppTransition=ANIM_SCALE_UP"
                         + " transit=" + transit + " Callers " + Debug.getCallers(3));
-            } else if (mNextAppTransitionType == ActivityOptions.ANIM_THUMBNAIL) {
-                a = createThumbnailAnimationLocked(transit, enter, false);
+            } else if (mNextAppTransitionType == ActivityOptions.ANIM_THUMBNAIL ||
+                    mNextAppTransitionType == ActivityOptions.ANIM_THUMBNAIL_DELAYED) {
+                boolean delayed = (mNextAppTransitionType == ActivityOptions.ANIM_THUMBNAIL_DELAYED);
+                a = createThumbnailAnimationLocked(transit, enter, false, delayed);
                 initialized = true;
-                if (DEBUG_ANIM) Slog.v(TAG, "applyAnimation: wtoken=" + wtoken
-                        + " anim=" + a + " nextAppTransition=ANIM_THUMBNAIL"
-                        + " transit=" + transit + " Callers " + Debug.getCallers(3));
+
+                if (DEBUG_ANIM) {
+                    String animName = delayed ? "ANIM_THUMBNAIL_DELAYED" : "ANIM_THUMBNAIL";
+                    Slog.v(TAG, "applyAnimation: wtoken=" + wtoken
+                            + " anim=" + a + " nextAppTransition=" + animName
+                            + " transit=" + transit + " Callers " + Debug.getCallers(3));
+                }
             } else {
                 int animAttr = 0;
                 switch (transit) {
@@ -3879,11 +3909,13 @@
     }
 
     public void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX,
-            int startY, IRemoteCallback startedCallback) {
+            int startY, IRemoteCallback startedCallback, boolean delayed) {
         if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
-            mNextAppTransitionType = ActivityOptions.ANIM_THUMBNAIL;
+            mNextAppTransitionType =
+                    delayed ? ActivityOptions.ANIM_THUMBNAIL_DELAYED : ActivityOptions.ANIM_THUMBNAIL;
             mNextAppTransitionPackage = null;
             mNextAppTransitionThumbnail = srcThumb;
+            mNextAppTransitionDelayed = delayed;
             mNextAppTransitionStartX = startX;
             mNextAppTransitionStartY = startY;
             mNextAppTransitionCallback = startedCallback;
@@ -7510,7 +7542,10 @@
         }
 
         for (i=0; i<N; i++) {
-            WindowState w = mWindows.get(i);
+            final WindowState w = mWindows.get(i);
+            final WindowStateAnimator winAnimator = w.mWinAnimator;
+            boolean layerChanged = false;
+            int oldLayer = w.mLayer;
             if (w.mBaseLayer == curBaseLayer || w.mIsImWindow
                     || (i > 0 && w.mIsWallpaper)) {
                 curLayer += WINDOW_LAYER_MULTIPLIER;
@@ -7519,22 +7554,33 @@
                 curBaseLayer = curLayer = w.mBaseLayer;
                 w.mLayer = curLayer;
             }
+            if (w.mLayer != oldLayer) {
+                layerChanged = true;
+            }
+            oldLayer = winAnimator.mAnimLayer;
             if (w.mTargetAppToken != null) {
-                w.mWinAnimator.mAnimLayer =
+                winAnimator.mAnimLayer =
                         w.mLayer + w.mTargetAppToken.mAppAnimator.animLayerAdjustment;
             } else if (w.mAppToken != null) {
-                w.mWinAnimator.mAnimLayer =
+                winAnimator.mAnimLayer =
                         w.mLayer + w.mAppToken.mAppAnimator.animLayerAdjustment;
             } else {
-                w.mWinAnimator.mAnimLayer = w.mLayer;
+                winAnimator.mAnimLayer = w.mLayer;
             }
             if (w.mIsImWindow) {
-                w.mWinAnimator.mAnimLayer += mInputMethodAnimLayerAdjustment;
+                winAnimator.mAnimLayer += mInputMethodAnimLayerAdjustment;
             } else if (w.mIsWallpaper) {
-                w.mWinAnimator.mAnimLayer += mWallpaperAnimLayerAdjustment;
+                winAnimator.mAnimLayer += mWallpaperAnimLayerAdjustment;
+            }
+            if (winAnimator.mAnimLayer != oldLayer) {
+                layerChanged = true;
+            }
+            if (layerChanged && mAnimator.isDimming(winAnimator)) {
+                // Force an animation pass just to update the mDimAnimator layer.
+                scheduleAnimationLocked();
             }
             if (DEBUG_LAYERS) Slog.v(TAG, "Assign layer " + w + ": "
-                    + w.mWinAnimator.mAnimLayer);
+                    + winAnimator.mAnimLayer);
             //System.out.println(
             //    "Assigned layer " + curLayer + " to " + w.mClient.asBinder());
         }
@@ -8024,7 +8070,8 @@
                     drawSurface.unlockCanvasAndPost(c);
                     drawSurface.release();
                     topOpeningApp.mAppAnimator.thumbnailLayer = topOpeningLayer;
-                    Animation anim = createThumbnailAnimationLocked(transit, true, true);
+                    Animation anim = createThumbnailAnimationLocked(
+                            transit, true, true, mNextAppTransitionDelayed);
                     topOpeningApp.mAppAnimator.thumbnailAnimation = anim;
                     anim.restrictDuration(MAX_ANIMATION_DURATION);
                     anim.scaleCurrentDuration(mTransitionAnimationScale);
@@ -8237,7 +8284,8 @@
             if (!mInnerFields.mDimming) {
                 //Slog.i(TAG, "DIM BEHIND: " + w);
                 mInnerFields.mDimming = true;
-                if (!mAnimator.isDimming()) {
+                final WindowStateAnimator winAnimator = w.mWinAnimator;
+                if (!mAnimator.isDimming(winAnimator)) {
                     final int width, height;
                     if (attrs.type == WindowManager.LayoutParams.TYPE_BOOT_PROGRESS) {
                         width = mCurDisplayWidth;
@@ -8246,7 +8294,7 @@
                         width = innerDw;
                         height = innerDh;
                     }
-                    mAnimator.startDimming(w.mWinAnimator, w.mExiting ? 0 : w.mAttrs.dimAmount,
+                    mAnimator.startDimming(winAnimator, w.mExiting ? 0 : w.mAttrs.dimAmount,
                             width, height);
                 }
             }
@@ -9602,10 +9650,12 @@
                             pw.println(mNextAppTransitionStartHeight);
                     break;
                 case ActivityOptions.ANIM_THUMBNAIL:
+                case ActivityOptions.ANIM_THUMBNAIL_DELAYED:
                     pw.print("  mNextAppTransitionThumbnail=");
                             pw.print(mNextAppTransitionThumbnail);
                             pw.print(" mNextAppTransitionStartX="); pw.print(mNextAppTransitionStartX);
                             pw.print(" mNextAppTransitionStartY="); pw.println(mNextAppTransitionStartY);
+                            pw.print(" mNextAppTransitionDelayed="); pw.println(mNextAppTransitionDelayed);
                     break;
             }
             pw.print("  mStartingIconInTransition="); pw.print(mStartingIconInTransition);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
index 85b67d5..a4b2125 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
@@ -240,7 +240,7 @@
 
     @Override
     public void overridePendingAppTransitionThumb(Bitmap srcThumb, int startX, int startY,
-            IRemoteCallback startedCallback) throws RemoteException {
+            IRemoteCallback startedCallback, boolean delayed) throws RemoteException {
         // TODO Auto-generated method stub
     }