Merge "Unhide notification category."
diff --git a/core/java/android/view/animation/ClipRectAnimation.java b/core/java/android/view/animation/ClipRectAnimation.java
new file mode 100644
index 0000000..2361501
--- /dev/null
+++ b/core/java/android/view/animation/ClipRectAnimation.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014 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 android.view.animation;
+
+import android.graphics.Rect;
+
+/**
+ * An animation that controls the clip of an object. See the
+ * {@link android.view.animation full package} description for details and
+ * sample code.
+ *
+ * @hide
+ */
+public class ClipRectAnimation extends Animation {
+    private Rect mFromRect = new Rect();
+    private Rect mToRect = new Rect();
+
+    /**
+     * Constructor to use when building a ClipRectAnimation from code
+     *
+     * @param fromClip the clip rect to animate from
+     * @param toClip the clip rect to animate to
+     */
+    public ClipRectAnimation(Rect fromClip, Rect toClip) {
+        if (fromClip == null || toClip == null) {
+            throw new RuntimeException("Expected non-null animation clip rects");
+        }
+        mFromRect.set(fromClip);
+        mToRect.set(toClip);
+    }
+
+    @Override
+    protected void applyTransformation(float it, Transformation tr) {
+        int l = mFromRect.left + (int) ((mToRect.left - mFromRect.left) * it);
+        int t = mFromRect.top + (int) ((mToRect.top - mFromRect.top) * it);
+        int r = mFromRect.right + (int) ((mToRect.right - mFromRect.right) * it);
+        int b = mFromRect.bottom + (int) ((mToRect.bottom - mFromRect.bottom) * it);
+        tr.setClipRect(l, t, r, b);
+    }
+
+    @Override
+    public boolean willChangeTransformationMatrix() {
+        return false;
+    }
+}
diff --git a/core/java/android/view/animation/Transformation.java b/core/java/android/view/animation/Transformation.java
index 890909b..2f4fe73 100644
--- a/core/java/android/view/animation/Transformation.java
+++ b/core/java/android/view/animation/Transformation.java
@@ -17,6 +17,7 @@
 package android.view.animation;
 
 import android.graphics.Matrix;
+import android.graphics.Rect;
 
 import java.io.PrintWriter;
 
@@ -47,6 +48,9 @@
     protected float mAlpha;
     protected int mTransformationType;
 
+    private boolean mHasClipRect;
+    private Rect mClipRect = new Rect();
+
     /**
      * Creates a new transformation with alpha = 1 and the identity matrix.
      */
@@ -65,6 +69,8 @@
         } else {
             mMatrix.reset();
         }
+        mClipRect.setEmpty();
+        mHasClipRect = false;
         mAlpha = 1.0f;
         mTransformationType = TYPE_BOTH;
     }
@@ -98,9 +104,15 @@
     public void set(Transformation t) {
         mAlpha = t.getAlpha();
         mMatrix.set(t.getMatrix());
+        if (t.mHasClipRect) {
+            setClipRect(t.getClipRect());
+        } else {
+            mHasClipRect = false;
+            mClipRect.setEmpty();
+        }
         mTransformationType = t.getTransformationType();
     }
-    
+
     /**
      * Apply this Transformation to an existing Transformation, e.g. apply
      * a scale effect to something that has already been rotated.
@@ -109,6 +121,9 @@
     public void compose(Transformation t) {
         mAlpha *= t.getAlpha();
         mMatrix.preConcat(t.getMatrix());
+        if (t.mHasClipRect) {
+            setClipRect(t.getClipRect());
+        }
     }
     
     /**
@@ -119,6 +134,9 @@
     public void postCompose(Transformation t) {
         mAlpha *= t.getAlpha();
         mMatrix.postConcat(t.getMatrix());
+        if (t.mHasClipRect) {
+            setClipRect(t.getClipRect());
+        }
     }
 
     /**
@@ -138,6 +156,39 @@
     }
 
     /**
+     * Sets the current Transform's clip rect
+     * @hide
+     */
+    public void setClipRect(Rect r) {
+        setClipRect(r.left, r.top, r.right, r.bottom);
+    }
+
+    /**
+     * Sets the current Transform's clip rect
+     * @hide
+     */
+    public void setClipRect(int l, int t, int r, int b) {
+        mClipRect.set(l, t, r, b);
+        mHasClipRect = true;
+    }
+
+    /**
+     * Returns the current Transform's clip rect
+     * @hide
+     */
+    public Rect getClipRect() {
+        return mClipRect;
+    }
+
+    /**
+     * Returns whether the current Transform's clip rect is set
+     * @hide
+     */
+    public boolean hasClipRect() {
+        return mHasClipRect;
+    }
+
+    /**
      * @return The degree of transparency
      */
     public float getAlpha() {
diff --git a/core/res/res/drawable-xhdpi/cab_background_top_holo_dark.9.png b/core/res/res/drawable-xhdpi/cab_background_top_holo_dark.9.png
index 6b31579..7b6d48b 100644
--- a/core/res/res/drawable-xhdpi/cab_background_top_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/cab_background_top_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/cab_background_top_holo_light.9.png b/core/res/res/drawable-xhdpi/cab_background_top_holo_light.9.png
index df0121b..bafe878 100644
--- a/core/res/res/drawable-xhdpi/cab_background_top_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/cab_background_top_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/cab_background_top_holo_dark.9.png b/core/res/res/drawable-xxhdpi/cab_background_top_holo_dark.9.png
index 418f322..cbb4f4c 100644
--- a/core/res/res/drawable-xxhdpi/cab_background_top_holo_dark.9.png
+++ b/core/res/res/drawable-xxhdpi/cab_background_top_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/cab_background_top_holo_light.9.png b/core/res/res/drawable-xxhdpi/cab_background_top_holo_light.9.png
index a5a59d4..6d467f7 100644
--- a/core/res/res/drawable-xxhdpi/cab_background_top_holo_light.9.png
+++ b/core/res/res/drawable-xxhdpi/cab_background_top_holo_light.9.png
Binary files differ
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index cb8155b..cdd789d 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -3226,11 +3226,13 @@
     const int casterVertexCount = casterVertices2d.size();
     Vector3 casterPolygon[casterVertexCount];
     float minZ = FLT_MAX;
+    float maxZ = -FLT_MAX;
     for (int i = 0; i < casterVertexCount; i++) {
         const Vertex& point2d = casterVertices2d[i];
         casterPolygon[i] = Vector3(point2d.x, point2d.y, 0);
         mapPointFakeZ(casterPolygon[i], casterTransformXY, casterTransformZ);
         minZ = fmin(minZ, casterPolygon[i].z);
+        maxZ = fmax(maxZ, casterPolygon[i].z);
     }
 
     // map the centroid of the caster into 3d
@@ -3248,6 +3250,15 @@
         }
         centroid3d.z += casterLift;
     }
+
+    // Check whether we want to draw the shadow at all by checking the caster's
+    // bounds against clip.
+    // We only have ortho projection, so we can just ignore the Z in caster for
+    // simple rejection calculation.
+    Rect localClip = mSnapshot->getLocalClip();
+    Rect casterBounds(casterOutline->getBounds());
+    casterTransformXY.mapRect(casterBounds);
+
     bool isCasterOpaque = (casterAlpha == 1.0f);
     // draw caster's shadows
     if (mCaches.propertyAmbientShadowStrength > 0) {
@@ -3255,7 +3266,7 @@
         VertexBuffer ambientShadowVertexBuffer;
         VertexBufferMode vertexBufferMode = ShadowTessellator::tessellateAmbientShadow(
                 isCasterOpaque, casterPolygon, casterVertexCount, centroid3d,
-                ambientShadowVertexBuffer);
+                casterBounds, localClip, maxZ, ambientShadowVertexBuffer);
         drawVertexBuffer(vertexBufferMode, ambientShadowVertexBuffer, &paint);
     }
 
@@ -3266,7 +3277,8 @@
                 mCaches.propertyLightPosYScale, mCaches.propertyLightPosZScale);
         VertexBufferMode vertexBufferMode = ShadowTessellator::tessellateSpotShadow(
                 isCasterOpaque, casterPolygon, casterVertexCount, lightPosScale,
-                *currentTransform(), getWidth(), getHeight(), spotShadowVertexBuffer);
+                *currentTransform(), getWidth(), getHeight(), casterBounds, localClip,
+                spotShadowVertexBuffer);
         drawVertexBuffer(vertexBufferMode, spotShadowVertexBuffer, &paint);
     }
 
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index c230149..0083b77 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -18,6 +18,7 @@
 #define ANDROID_HWUI_RECT_H
 
 #include <cmath>
+#include <SkRect.h>
 
 #include <utils/Log.h>
 
@@ -68,6 +69,13 @@
             bottom(height) {
     }
 
+    inline Rect(const SkRect& rect):
+            left(rect.fLeft),
+            top(rect.fTop),
+            right(rect.fRight),
+            bottom(rect.fBottom) {
+    }
+
     friend int operator==(const Rect& a, const Rect& b) {
         return !memcmp(&a, &b, sizeof(a));
     }
diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp
index 771904a..4d0edfb 100644
--- a/libs/hwui/ShadowTessellator.cpp
+++ b/libs/hwui/ShadowTessellator.cpp
@@ -35,7 +35,8 @@
 
 VertexBufferMode ShadowTessellator::tessellateAmbientShadow(bool isCasterOpaque,
         const Vector3* casterPolygon, int casterVertexCount,
-        const Vector3& centroid3d, VertexBuffer& shadowVertexBuffer) {
+        const Vector3& centroid3d, const Rect& casterBounds,
+        const Rect& localClip, float maxZ, VertexBuffer& shadowVertexBuffer) {
     ATRACE_CALL();
 
     // A bunch of parameters to tweak the shadow.
@@ -43,6 +44,16 @@
     const float heightFactor = 1.0f / 128;
     const float geomFactor = 64;
 
+    Rect ambientShadowBounds(casterBounds);
+    ambientShadowBounds.outset(maxZ * geomFactor * heightFactor);
+
+    if (!localClip.intersects(ambientShadowBounds)) {
+#if DEBUG_SHADOW
+        ALOGD("Ambient shadow is out of clip rect!");
+#endif
+        return kVertexBufferMode_OnePolyRingShadow;
+    }
+
     return AmbientShadow::createAmbientShadow(isCasterOpaque, casterPolygon,
             casterVertexCount, centroid3d, heightFactor, geomFactor,
             shadowVertexBuffer);
@@ -52,7 +63,8 @@
 VertexBufferMode ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque,
         const Vector3* casterPolygon, int casterVertexCount,
         const Vector3& lightPosScale, const mat4& receiverTransform,
-        int screenWidth, int screenHeight, VertexBuffer& shadowVertexBuffer) {
+        int screenWidth, int screenHeight, const Rect& casterBounds,
+        const Rect& localClip, VertexBuffer& shadowVertexBuffer) {
     ATRACE_CALL();
 
     // A bunch of parameters to tweak the shadow.
@@ -73,6 +85,18 @@
     const float lightSize = maximal / 4;
     const int lightVertexCount = 8;
 
+    // Now light and caster are both in local space, we will check whether
+    // the shadow is within the clip area.
+    Rect lightRect = Rect(lightCenter.x - lightSize, lightCenter.y - lightSize,
+            lightCenter.x + lightSize, lightCenter.y + lightSize);
+    lightRect.unionWith(localClip);
+    if (!lightRect.intersects(casterBounds)) {
+#if DEBUG_SHADOW
+        ALOGD("Spot shadow is out of clip rect!");
+#endif
+        return kVertexBufferMode_OnePolyRingShadow;
+    }
+
     VertexBufferMode mode = SpotShadow::createSpotShadow(isCasterOpaque,
             casterPolygon, casterVertexCount, lightCenter, lightSize,
             lightVertexCount, shadowVertexBuffer);
diff --git a/libs/hwui/ShadowTessellator.h b/libs/hwui/ShadowTessellator.h
index ab039fa..ff3de74 100644
--- a/libs/hwui/ShadowTessellator.h
+++ b/libs/hwui/ShadowTessellator.h
@@ -66,12 +66,14 @@
 public:
     static VertexBufferMode tessellateAmbientShadow(bool isCasterOpaque,
             const Vector3* casterPolygon, int casterVertexCount,
-            const Vector3& centroid3d, VertexBuffer& shadowVertexBuffer);
+            const Vector3& centroid3d,  const Rect& casterBounds,
+            const Rect& localClip, float maxZ, VertexBuffer& shadowVertexBuffer);
 
     static VertexBufferMode tessellateSpotShadow(bool isCasterOpaque,
             const Vector3* casterPolygon, int casterVertexCount,
             const Vector3& lightPosScale, const mat4& receiverTransform,
-            int screenWidth, int screenHeight, VertexBuffer& shadowVertexBuffer);
+            int screenWidth, int screenHeight, const Rect& casterBounds,
+            const Rect& localClip, VertexBuffer& shadowVertexBuffer);
 
     static void generateShadowIndices(uint16_t*  shadowIndices);
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index ed981ed..3d47cb6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.graphics.Rect;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
@@ -31,6 +32,7 @@
     DisplayMetrics mDisplayMetrics;
 
     public Rect systemInsets = new Rect();
+    public Rect displayRect = new Rect();
 
     /** Private constructor */
     private RecentsConfiguration() {}
@@ -51,10 +53,11 @@
 
     /** Updates the state, given the specified context */
     void update(Context context) {
-        mDisplayMetrics = context.getResources().getDisplayMetrics();
+        Resources res = context.getResources();
+        DisplayMetrics dm = res.getDisplayMetrics();
+        mDisplayMetrics = dm;
 
-        boolean isPortrait = context.getResources().getConfiguration().orientation ==
-                Configuration.ORIENTATION_PORTRAIT;
+        displayRect.set(0, 0, dm.widthPixels, dm.heightPixels);
     }
 
     public void updateSystemInsets(Rect insets) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 77b78f3..d997222 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -17,6 +17,7 @@
 package com.android.systemui.recents.views;
 
 import android.app.ActivityOptions;
+import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
@@ -210,11 +211,14 @@
                 int offsetX = 0;
                 int offsetY = 0;
                 if (tv == null) {
-                    // Launch the activity
+                    // If there is no actual task view, then use the stack view as the source view
+                    // and then offset to the expected transform rect, but bound this to just
+                    // outside the display rect (to ensure we don't animate from too far away)
+                    RecentsConfiguration config = RecentsConfiguration.getInstance();
                     sourceView = stackView;
                     transform = stackView.getStackTransform(stack.indexOfTask(task));
                     offsetX = transform.rect.left;
-                    offsetY = transform.rect.top;
+                    offsetY = Math.min(transform.rect.top, config.displayRect.height());
                 } else {
                     transform = stackView.getStackTransform(stack.indexOfTask(task));
                 }
@@ -242,10 +246,14 @@
                 i.setFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
                         | Intent.FLAG_ACTIVITY_TASK_ON_HOME
                         | Intent.FLAG_ACTIVITY_NEW_TASK);
-                if (opts != null) {
-                    getContext().startActivityAsUser(i, opts.toBundle(), UserHandle.CURRENT);
-                } else {
-                    getContext().startActivityAsUser(i, UserHandle.CURRENT);
+                try {
+                    if (opts != null) {
+                        getContext().startActivityAsUser(i, opts.toBundle(), UserHandle.CURRENT);
+                    } else {
+                        getContext().startActivityAsUser(i, UserHandle.CURRENT);
+                    }
+                } catch (ActivityNotFoundException anfe) {
+                    Console.logError(getContext(), "Could not start Activity");
                 }
 
                 Console.logTraceTime(Constants.DebugFlags.App.TimeRecentsLaunchTask,
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 756e06a..7d8b5af 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -19,18 +19,22 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Point;
+import android.graphics.Rect;
 import android.os.Debug;
 import android.os.Handler;
 import android.os.IRemoteCallback;
+import android.os.SystemProperties;
 import android.util.Slog;
 import android.view.WindowManager;
 import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
 import android.view.animation.AnimationSet;
 import android.view.animation.AnimationUtils;
+import android.view.animation.ClipRectAnimation;
 import android.view.animation.Interpolator;
 import android.view.animation.ScaleAnimation;
 
+import android.view.animation.TranslateAnimation;
 import com.android.internal.util.DumpUtils.Dump;
 import com.android.server.AttributeCache;
 import com.android.server.wm.WindowManagerService.H;
@@ -125,6 +129,12 @@
     private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN = 4;
     private int mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE;
 
+    // These are the possible states for the enter/exit activities during a thumbnail transition
+    private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_UP = 0;
+    private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_UP = 1;
+    private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN = 2;
+    private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN = 3;
+
     private String mNextAppTransitionPackage;
     private Bitmap mNextAppTransitionThumbnail;
     // Used for thumbnail transitions. True if we're scaling up, false if scaling down
@@ -148,10 +158,13 @@
     private final Interpolator mThumbnailFadeoutInterpolator;
 
     private int mCurrentUserId = 0;
+    private boolean mUseAlternateThumbnailAnimation;
 
     AppTransition(Context context, Handler h) {
         mContext = context;
         mH = h;
+        mUseAlternateThumbnailAnimation =
+                SystemProperties.getBoolean("persist.anim.use_alt_thumbnail", false);
         mConfigShortAnimTime = context.getResources().getInteger(
                 com.android.internal.R.integer.config_shortAnimTime);
         mDecelerateInterpolator = AnimationUtils.loadInterpolator(context,
@@ -384,80 +397,10 @@
         return a;
     }
 
-    Animation createThumbnailAnimationLocked(int transit, boolean enter, boolean thumb,
-                                    int appWidth, int appHeight) {
-        Animation a;
-        final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
-        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
-        final int thumbHeightI = mNextAppTransitionThumbnail.getHeight();
-        final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
-        if (thumb) {
-            // Animation for zooming thumbnail from its initial size to
-            // filling the screen.
-            if (mNextAppTransitionScaleUp) {
-                float scaleW = appWidth / thumbWidth;
-                float scaleH = appHeight / thumbHeight;
-                Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
-                        computePivot(mNextAppTransitionStartX, 1 / scaleW),
-                        computePivot(mNextAppTransitionStartY, 1 / scaleH));
-                scale.setInterpolator(mDecelerateInterpolator);
-
-                Animation alpha = new AlphaAnimation(1, 0);
-                alpha.setInterpolator(mThumbnailFadeoutInterpolator);
-
-                // This AnimationSet uses the Interpolators assigned above.
-                AnimationSet set = new AnimationSet(false);
-                set.addAnimation(scale);
-                set.addAnimation(alpha);
-                a = set;
-            } else {
-                float scaleW = appWidth / thumbWidth;
-                float scaleH = appHeight / thumbHeight;
-                a = new ScaleAnimation(scaleW, 1, scaleH, 1,
-                        computePivot(mNextAppTransitionStartX, 1 / scaleW),
-                        computePivot(mNextAppTransitionStartY, 1 / scaleH));
-            }
-        } else if (enter) {
-            // Entering app zooms out from the center of the thumbnail.
-            if (mNextAppTransitionScaleUp) {
-                float scaleW = thumbWidth / appWidth;
-                float scaleH = thumbHeight / appHeight;
-                a = new ScaleAnimation(scaleW, 1, scaleH, 1,
-                        computePivot(mNextAppTransitionStartX, scaleW),
-                        computePivot(mNextAppTransitionStartY, scaleH));
-            } else {
-                // noop animation
-                a = new AlphaAnimation(1, 1);
-            }
-        } else {
-            // Exiting app
-            if (mNextAppTransitionScaleUp) {
-                if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
-                    // Fade out while bringing up selected activity. This keeps the
-                    // current activity from showing through a launching wallpaper
-                    // activity.
-                    a = new AlphaAnimation(1, 0);
-                } else {
-                    // noop animation
-                    a = new AlphaAnimation(1, 1);
-                }
-            } else {
-                float scaleW = thumbWidth / appWidth;
-                float scaleH = thumbHeight / appHeight;
-                Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
-                        computePivot(mNextAppTransitionStartX, scaleW),
-                        computePivot(mNextAppTransitionStartY, scaleH));
-
-                Animation alpha = new AlphaAnimation(1, 0);
-
-                AnimationSet set = new AnimationSet(true);
-                set.addAnimation(scale);
-                set.addAnimation(alpha);
-                set.setZAdjustment(Animation.ZORDER_TOP);
-                a = set;
-            }
-        }
-
+    /**
+     * Prepares the specified animation with a standard duration, interpolator, etc.
+     */
+    Animation prepareThumbnailAnimation(Animation a, int appWidth, int appHeight, int transit) {
         // Pick the desired duration.  If this is an inter-activity transition,
         // it  is the standard duration for that.  Otherwise we use the longer
         // task transition duration.
@@ -478,9 +421,223 @@
         return a;
     }
 
+    /**
+     * Return the current thumbnail transition state.
+     */
+    int getThumbnailTransitionState(boolean enter) {
+        if (enter) {
+            if (mNextAppTransitionScaleUp) {
+                return THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
+            } else {
+                return THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN;
+            }
+        } else {
+            if (mNextAppTransitionScaleUp) {
+                return THUMBNAIL_TRANSITION_EXIT_SCALE_UP;
+            } else {
+                return THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN;
+            }
+        }
+    }
+
+    /**
+     * This animation runs for the thumbnail that gets cross faded with the enter/exit activity
+     * when a thumbnail is specified with the activity options.
+     */
+    Animation createThumbnailScaleAnimationLocked(int appWidth, int appHeight, int transit) {
+        Animation a;
+        final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
+        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
+        final int thumbHeightI = mNextAppTransitionThumbnail.getHeight();
+        final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
+
+        if (mNextAppTransitionScaleUp) {
+            // Animation for the thumbnail zooming from its initial size to the full screen
+            float scaleW = appWidth / thumbWidth;
+            float scaleH = appHeight / thumbHeight;
+            Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
+                    computePivot(mNextAppTransitionStartX, 1 / scaleW),
+                    computePivot(mNextAppTransitionStartY, 1 / scaleH));
+            scale.setInterpolator(mDecelerateInterpolator);
+
+            Animation alpha = new AlphaAnimation(1, 0);
+            alpha.setInterpolator(mThumbnailFadeoutInterpolator);
+
+            // This AnimationSet uses the Interpolators assigned above.
+            AnimationSet set = new AnimationSet(false);
+            set.addAnimation(scale);
+            set.addAnimation(alpha);
+            a = set;
+        } else {
+            // Animation for the thumbnail zooming down from the full screen to its final size
+            float scaleW = appWidth / thumbWidth;
+            float scaleH = appHeight / thumbHeight;
+            a = new ScaleAnimation(scaleW, 1, scaleH, 1,
+                    computePivot(mNextAppTransitionStartX, 1 / scaleW),
+                    computePivot(mNextAppTransitionStartY, 1 / scaleH));
+        }
+
+        return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
+    }
+
+    /**
+     * This alternate animation is created when we are doing a thumbnail transition, for the
+     * activity that is leaving, and the activity that is entering.
+     */
+    Animation createAlternateThumbnailEnterExitAnimationLocked(int thumbTransitState, int appWidth,
+                                                    int appHeight, int transit,
+                                                    Rect containingFrame, Rect contentInsets) {
+        Animation a;
+        final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
+        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
+        final int thumbHeightI = mNextAppTransitionThumbnail.getHeight();
+        final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
+
+        switch (thumbTransitState) {
+            case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
+                // Entering app scales up with the thumbnail
+                float scale = thumbWidth / appWidth;
+                int unscaledThumbHeight = (int) (thumbHeight / scale);
+                int scaledTopDecor = (int) (scale * contentInsets.top);
+                Rect fromClipRect = new Rect(containingFrame);
+                fromClipRect.bottom = (fromClipRect.top + unscaledThumbHeight);
+                Rect toClipRect = new Rect(containingFrame);
+
+                Animation scaleAnim = new ScaleAnimation(scale, 1, scale, 1,
+                        computePivot(mNextAppTransitionStartX, scale),
+                        computePivot(mNextAppTransitionStartY, scale));
+                Animation alphaAnim = new AlphaAnimation(1, 1);
+                Animation clipAnim = new ClipRectAnimation(fromClipRect, toClipRect);
+                Animation translateAnim = new TranslateAnimation(0, 0, -scaledTopDecor, 0);
+
+                AnimationSet set = new AnimationSet(true);
+                set.addAnimation(alphaAnim);
+                set.addAnimation(clipAnim);
+                set.addAnimation(scaleAnim);
+                set.addAnimation(translateAnim);
+                a = set;
+                break;
+            }
+            case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
+                // Exiting app while the thumbnail is scaling up should fade
+                if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
+                    // Fade out while bringing up selected activity. This keeps the
+                    // current activity from showing through a launching wallpaper
+                    // activity.
+                    a = new AlphaAnimation(1, 0);
+                } else {
+                    // noop animation
+                    a = new AlphaAnimation(1, 1);
+                }
+                break;
+            }
+            case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: {
+                // Entering the other app, it should just be visible while we scale the thumbnail
+                // down above it
+                a = new AlphaAnimation(1, 1);
+                break;
+            }
+            case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
+                // Exiting the current app, the app should scale down with the thumbnail
+                float scale = thumbWidth / appWidth;
+                int unscaledThumbHeight = (int) (thumbHeight / scale);
+                int scaledTopDecor = (int) (scale * contentInsets.top);
+                Rect fromClipRect = new Rect(containingFrame);
+                Rect toClipRect = new Rect(containingFrame);
+                toClipRect.bottom = (toClipRect.top + unscaledThumbHeight);
+
+                Animation scaleAnim = new ScaleAnimation(1, scale, 1, scale,
+                        computePivot(mNextAppTransitionStartX, scale),
+                        computePivot(mNextAppTransitionStartY, scale));
+                Animation alphaAnim = new AlphaAnimation(1, 1);
+                Animation clipAnim = new ClipRectAnimation(fromClipRect, toClipRect);
+                Animation translateAnim = new TranslateAnimation(0, 0, 0, -scaledTopDecor);
+
+                AnimationSet set = new AnimationSet(true);
+                set.addAnimation(alphaAnim);
+                set.addAnimation(clipAnim);
+                set.addAnimation(scaleAnim);
+                set.addAnimation(translateAnim);
+
+                a = set;
+                a.setZAdjustment(Animation.ZORDER_TOP);
+                break;
+            }
+            default:
+                throw new RuntimeException("Invalid thumbnail transition state");
+        }
+
+        return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
+    }
+
+    /**
+     * This animation is created when we are doing a thumbnail transition, for the activity that is
+     * leaving, and the activity that is entering.
+     */
+    Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState, int appWidth,
+                                                    int appHeight, int transit) {
+        Animation a;
+        final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
+        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
+        final int thumbHeightI = mNextAppTransitionThumbnail.getHeight();
+        final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
+
+        switch (thumbTransitState) {
+            case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
+                // Entering app scales up with the thumbnail
+                float scaleW = thumbWidth / appWidth;
+                float scaleH = thumbHeight / appHeight;
+                a = new ScaleAnimation(scaleW, 1, scaleH, 1,
+                        computePivot(mNextAppTransitionStartX, scaleW),
+                        computePivot(mNextAppTransitionStartY, scaleH));
+                break;
+            }
+            case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
+                // Exiting app while the thumbnail is scaling up should fade or stay in place
+                if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
+                    // Fade out while bringing up selected activity. This keeps the
+                    // current activity from showing through a launching wallpaper
+                    // activity.
+                    a = new AlphaAnimation(1, 0);
+                } else {
+                    // noop animation
+                    a = new AlphaAnimation(1, 1);
+                }
+                break;
+            }
+            case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: {
+                // Entering the other app, it should just be visible while we scale the thumbnail
+                // down above it
+                a = new AlphaAnimation(1, 1);
+                break;
+            }
+            case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
+                // Exiting the current app, the app should scale down with the thumbnail
+                float scaleW = thumbWidth / appWidth;
+                float scaleH = thumbHeight / appHeight;
+                Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
+                        computePivot(mNextAppTransitionStartX, scaleW),
+                        computePivot(mNextAppTransitionStartY, scaleH));
+
+                Animation alpha = new AlphaAnimation(1, 0);
+
+                AnimationSet set = new AnimationSet(true);
+                set.addAnimation(scale);
+                set.addAnimation(alpha);
+                set.setZAdjustment(Animation.ZORDER_TOP);
+                a = set;
+                break;
+            }
+            default:
+                throw new RuntimeException("Invalid thumbnail transition state");
+        }
+
+        return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
+    }
+
 
     Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
-                            int appWidth, int appHeight) {
+                            int appWidth, int appHeight, Rect containingFrame, Rect contentInsets) {
         Animation a;
         if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
             a = loadAnimation(mNextAppTransitionPackage, enter ?
@@ -501,7 +658,14 @@
                 mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN) {
             mNextAppTransitionScaleUp =
                     (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP);
-            a = createThumbnailAnimationLocked(transit, enter, false, appWidth, appHeight);
+            if (mUseAlternateThumbnailAnimation) {
+                a = createAlternateThumbnailEnterExitAnimationLocked(
+                        getThumbnailTransitionState(enter), appWidth, appHeight, transit,
+                        containingFrame, contentInsets);
+            } else {
+                a = createThumbnailEnterExitAnimationLocked(getThumbnailTransitionState(enter),
+                        appWidth, appHeight, transit);
+            }
             if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
                 String animName = mNextAppTransitionScaleUp ?
                         "ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN";
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index adfb2bd..4f80f1f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3155,7 +3155,22 @@
             final int height = displayInfo.appHeight;
             if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, "applyAnimation: atoken="
                     + atoken);
-            Animation a = mAppTransition.loadAnimation(lp, transit, enter, width, height);
+
+            // Determine the visible rect to calculate the thumbnail clip
+            WindowState win = atoken.findMainWindow();
+            Rect containingFrame = new Rect(0, 0, width, height);
+            Rect contentInsets = new Rect();
+            if (win != null) {
+                if (win.mContainingFrame != null) {
+                    containingFrame.set(win.mContainingFrame);
+                }
+                if (win.mContentInsets != null) {
+                    contentInsets.set(win.mContentInsets);
+                }
+            }
+
+            Animation a = mAppTransition.loadAnimation(lp, transit, enter, width, height,
+                    containingFrame, contentInsets);
             if (a != null) {
                 if (DEBUG_ANIM) {
                     RuntimeException e = null;
@@ -8608,11 +8623,13 @@
                 wtoken.deferClearAllDrawn = false;
             }
 
+            boolean useAlternateThumbnailAnimation =
+                            SystemProperties.getBoolean("persist.anim.use_alt_thumbnail", false);
             AppWindowAnimator appAnimator =
                     topOpeningApp == null ? null : topOpeningApp.mAppAnimator;
             Bitmap nextAppTransitionThumbnail = mAppTransition.getNextAppTransitionThumbnail();
-            if (nextAppTransitionThumbnail != null && appAnimator != null
-                    && appAnimator.animation != null) {
+            if (!useAlternateThumbnailAnimation && nextAppTransitionThumbnail != null
+                    && appAnimator != null && appAnimator.animation != null) {
                 // This thumbnail animation is very special, we need to have
                 // an extra surface with the thumbnail included with the animation.
                 Rect dirty = new Rect(0, 0, nextAppTransitionThumbnail.getWidth(),
@@ -8636,8 +8653,8 @@
                     drawSurface.release();
                     appAnimator.thumbnailLayer = topOpeningLayer;
                     DisplayInfo displayInfo = getDefaultDisplayInfoLocked();
-                    Animation anim = mAppTransition.createThumbnailAnimationLocked(
-                            transit, true, true, displayInfo.appWidth, displayInfo.appHeight);
+                    Animation anim = mAppTransition.createThumbnailScaleAnimationLocked(
+                            displayInfo.appWidth, displayInfo.appHeight, transit);
                     appAnimator.thumbnailAnimation = anim;
                     anim.restrictDuration(MAX_ANIMATION_DURATION);
                     anim.scaleCurrentDuration(mTransitionAnimationScale);
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 2cd6000..6b3c368 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -113,6 +113,11 @@
     float mAlpha = 0;
     float mLastAlpha = 0;
 
+    boolean mHasClipRect;
+    Rect mClipRect = new Rect();
+    Rect mTmpClipRect = new Rect();
+    Rect mLastClipRect = new Rect();
+
     // Used to save animation distances between the time they are calculated and when they are
     // used.
     int mAnimDw;
@@ -951,6 +956,7 @@
             if (screenAnimation) {
                 tmpMatrix.postConcat(screenRotationAnimation.getEnterTransformation().getMatrix());
             }
+
             //TODO (multidisplay): Magnification is supported only for the default display.
             if (mService.mAccessibilityController != null && displayId == Display.DEFAULT_DISPLAY) {
                 MagnificationSpec spec = mService.mAccessibilityController
@@ -985,6 +991,7 @@
             // transforming since it is more important to have that
             // animation be smooth.
             mShownAlpha = mAlpha;
+            mHasClipRect = false;
             if (!mService.mLimitedAlphaCompositing
                     || (!PixelFormat.formatHasAlpha(mWin.mAttrs.format)
                     || (mWin.isIdentityMatrix(mDsDx, mDtDx, mDsDy, mDtDy)
@@ -998,6 +1005,10 @@
                 }
                 if (appTransformation != null) {
                     mShownAlpha *= appTransformation.getAlpha();
+                    if (appTransformation.hasClipRect()) {
+                        mClipRect.set(appTransformation.getClipRect());
+                        mHasClipRect = true;
+                    }
                 }
                 if (mAnimator.mUniverseBackground != null) {
                     mShownAlpha *= mAnimator.mUniverseBackground.mUniverseTransform.getAlpha();
@@ -1149,15 +1160,32 @@
             applyDecorRect(w.mDecorFrame);
         }
 
-        if (!w.mSystemDecorRect.equals(w.mLastSystemDecorRect)) {
-            w.mLastSystemDecorRect.set(w.mSystemDecorRect);
+        // By default, the clip rect is the system decor rect
+        Rect clipRect = w.mSystemDecorRect;
+        if (mHasClipRect) {
+
+            // If we have an animated clip rect, intersect it with the system decor rect
+            // NOTE: We are adding a temporary workaround due to the status bar not always reporting
+            // the correct system decor rect.  In such cases, we take into account the specified
+            // content insets as well.
+            int offsetTop = Math.max(w.mSystemDecorRect.top, w.mContentInsets.top);
+            mTmpClipRect.set(w.mSystemDecorRect);
+            mTmpClipRect.offset(0, -offsetTop);
+            mTmpClipRect.intersect(mClipRect);
+            mTmpClipRect.offset(0, offsetTop);
+            clipRect = mTmpClipRect;
+
+        }
+
+        if (!clipRect.equals(mLastClipRect)) {
+            mLastClipRect.set(clipRect);
             try {
                 if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
-                        "CROP " + w.mSystemDecorRect.toShortString(), null);
-                mSurfaceControl.setWindowCrop(w.mSystemDecorRect);
+                        "CROP " + clipRect.toShortString(), null);
+                mSurfaceControl.setWindowCrop(clipRect);
             } catch (RuntimeException e) {
                 Slog.w(TAG, "Error setting crop surface of " + w
-                        + " crop=" + w.mSystemDecorRect.toShortString(), e);
+                        + " crop=" + clipRect.toShortString(), e);
                 if (!recoveringMemory) {
                     mService.reclaimSomeSurfaceMemoryLocked(this, "crop", true);
                 }
@@ -1302,8 +1330,8 @@
                     mSurfaceLayer = mAnimLayer;
                     mSurfaceControl.setLayer(mAnimLayer);
                     mSurfaceControl.setMatrix(
-                        mDsDx*w.mHScale, mDtDx*w.mVScale,
-                        mDsDy*w.mHScale, mDtDy*w.mVScale);
+                            mDsDx * w.mHScale, mDtDx * w.mVScale,
+                            mDsDy * w.mHScale, mDtDy * w.mVScale);
 
                     if (mLastHidden && mDrawState == HAS_DRAWN) {
                         if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index efbfb33..502ee18 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -90,7 +90,6 @@
     private static final int MSG_SYSTEM_READY = 3;
     private static final int MSG_BOOT_COMPLETED = 4;
     private static final int MSG_USER_SWITCHED = 5;
-    private static final int MSG_START_ACCESSORY_MODE = 6;
 
     private static final int AUDIO_MODE_NONE = 0;
     private static final int AUDIO_MODE_SOURCE = 1;
@@ -153,7 +152,7 @@
                 mHandler.updateState(state);
             } else if ("START".equals(accessory)) {
                 if (DEBUG) Slog.d(TAG, "got accessory start");
-                 mHandler.sendEmptyMessage(MSG_START_ACCESSORY_MODE);
+                startAccessoryMode();
             }
         }
     };
@@ -171,7 +170,7 @@
 
         if (nativeIsStartRequested()) {
             if (DEBUG) Slog.d(TAG, "accessory attached at boot");
-             mHandler.sendEmptyMessage(MSG_START_ACCESSORY_MODE);
+            startAccessoryMode();
         }
 
         boolean secureAdbEnabled = SystemProperties.getBoolean("ro.adb.secure", false);
@@ -233,8 +232,6 @@
             functions = UsbManager.USB_FUNCTION_AUDIO_SOURCE;
         }
 
-        if (DEBUG) Slog.d(TAG, "startAccessoryMode: " + functions);
-
         if (functions != null) {
             mAccessoryModeRequestTime = SystemClock.elapsedRealtime();
             setCurrentFunctions(functions, false);
@@ -309,7 +306,6 @@
         // current USB state
         private boolean mConnected;
         private boolean mConfigured;
-        private boolean mAccessoryStartPending;
         private String mCurrentFunctions;
         private String mDefaultFunctions;
         private UsbAccessory mCurrentAccessory;
@@ -538,7 +534,6 @@
 
             if (mConfigured && enteringAccessoryMode) {
                 // successfully entered accessory mode
-                mAccessoryModeRequestTime = 0;
 
                 if (mAccessoryStrings != null) {
                     mCurrentAccessory = new UsbAccessory(mAccessoryStrings);
@@ -616,11 +611,6 @@
                 case MSG_UPDATE_STATE:
                     mConnected = (msg.arg1 == 1);
                     mConfigured = (msg.arg2 == 1);
-
-                    if (!mConnected) {
-                        mAccessoryStartPending = false;
-                    }
-
                     updateUsbNotification();
                     updateAdbNotification();
                     if (containsFunction(mCurrentFunctions,
@@ -634,10 +624,6 @@
                         updateUsbState();
                         updateAudioSourceFunction();
                     }
-                    if (mConnected && mConfigured && mAccessoryStartPending) {
-                        startAccessoryMode();
-                        mAccessoryStartPending = false;
-                    }
                     break;
                 case MSG_ENABLE_ADB:
                     setAdbEnabled(msg.arg1 == 1);
@@ -674,16 +660,6 @@
                     mCurrentUser = msg.arg1;
                     break;
                 }
-                case MSG_START_ACCESSORY_MODE:
-                    if (mConnected && mConfigured) {
-                        startAccessoryMode();
-                    } else {
-                        // we sometimes receive the kernel "accessory start" uevent
-                        // before the "configured" uevent. In this case we need to defer
-                        // handling this event until after we received the configured event
-                        mAccessoryStartPending = true;
-                    }
-                    break;
             }
         }
 
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index dd2cbc1..743a26c 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -458,44 +458,8 @@
     }
 
     @Override
-    public IBinder getFocusedWindowToken() {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public void setInputFilter(IInputFilter filter) throws RemoteException {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void getWindowFrame(IBinder token, Rect outFrame) {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void setMagnificationCallbacks(IMagnificationCallbacks callbacks) {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void setMagnificationSpec(MagnificationSpec spec) {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public MagnificationSpec getCompatibleMagnificationSpecForWindow(IBinder windowToken) {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
     public boolean isRotationFrozen() throws RemoteException {
         // TODO Auto-generated method stub
         return false;
     }
-
-    @Override
-    public void setTouchExplorationEnabled(boolean enabled) {
-    }
 }