am 03f7ebfe: Merge "Relax permission requirements for posting notifications across users" into jb-mr1-dev

* commit '03f7ebfeaadb3f03c9a9a6405276fb702ad11fe1':
  Relax permission requirements for posting notifications across users
diff --git a/core/res/res/layout-port/keyguard_host_view.xml b/core/res/res/layout-port/keyguard_host_view.xml
index 5444471..a3c8105 100644
--- a/core/res/res/layout-port/keyguard_host_view.xml
+++ b/core/res/res/layout-port/keyguard_host_view.xml
@@ -36,7 +36,8 @@
 
         <FrameLayout
             android:layout_width="match_parent"
-            android:layout_height="match_parent">
+            android:layout_height="match_parent"
+            androidprv:layout_childType="widgets">
             <include layout="@layout/keyguard_widget_pager"
                 android:id="@+id/app_widget_container"
                 android:layout_width="match_parent"
@@ -51,12 +52,11 @@
 
         <com.android.internal.policy.impl.keyguard.KeyguardSecurityContainer
             android:id="@+id/keyguard_security_container"
-            android:layout_width="@dimen/keyguard_security_width"
+            android:layout_width="wrap_content"
             android:layout_height="@dimen/keyguard_security_height"
             androidprv:layout_childType="challenge"
-            android:layout_marginLeft="@dimen/kg_edge_swipe_region_size"
-            android:layout_marginRight="@dimen/kg_edge_swipe_region_size"
             android:background="@drawable/kg_bouncer_bg_white"
+            android:padding="0dp"
             android:gravity="bottom|center_horizontal">
             <com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper
                 android:id="@+id/view_flipper"
@@ -64,9 +64,7 @@
                 android:layout_height="match_parent"
                 android:clipChildren="false"
                 android:clipToPadding="false"
-                android:paddingLeft="@dimen/keyguard_security_view_margin"
                 android:paddingTop="@dimen/keyguard_security_view_margin"
-                android:paddingRight="@dimen/keyguard_security_view_margin"
                 android:gravity="center">
             </com.android.internal.policy.impl.keyguard.KeyguardSecurityViewFlipper>
         </com.android.internal.policy.impl.keyguard.KeyguardSecurityContainer>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index d186c4a..627099c 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5798,6 +5798,8 @@
         <!-- Scrim. This will block access to child views that
              come before it in the child list in bouncer mode. -->
         <enum name="scrim" value="4" />
+        <!-- The home for widgets. All widgets will be descendents of this. -->
+        <enum name="widgets" value="5" />
     </attr>
 
     <declare-styleable name="SlidingChallengeLayout">
diff --git a/core/res/res/values/integers.xml b/core/res/res/values/integers.xml
index d1df2a4..053fc85 100644
--- a/core/res/res/values/integers.xml
+++ b/core/res/res/values/integers.xml
@@ -18,7 +18,7 @@
 -->
 <resources>
     <integer name="kg_carousel_angle">75</integer>
-    <integer name="kg_security_flip_duration">75</integer>
-    <integer name="kg_security_fade_duration">75</integer>
+    <integer name="kg_security_flip_duration">100</integer>
+    <integer name="kg_security_fade_duration">100</integer>
     <integer name="kg_glowpad_rotation_offset">0</integer>
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 159eec1..a86405d 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2283,7 +2283,7 @@
     <!-- Accessibility description of the event of collapsing an unlock area. [CHAR_LIMIT=none] -->
     <string name="keyguard_accessibility_unlock_area_collapsed">Unlock area collapsed.</string>
     <!-- Accessibility description of a lock screen widget. [CHAR_LIMIT=none] -->
-    <string name="keyguard_accessibility_widget">%1$s widget.</string>
+    <string name="keyguard_accessibility_widget"><xliff:g id="widget_index">%1$s</xliff:g> widget.</string>
     <!-- Accessibility description of the lock screen user selector widget. [CHAR_LIMIT=none] -->
     <string name="keyguard_accessibility_user_selector">User selector</string>
     <!-- Accessibility description of the lock screen status widget. [CHAR_LIMIT=none] -->
@@ -2292,6 +2292,14 @@
     <string name="keyguard_accessibility_camera">Camera</string>
     <!-- Accessibility description of the lock media control widget. [CHAR_LIMIT=none] -->
     <string name="keygaurd_accessibility_media_controls">Media controls</string>
+    <!-- Accessibility description of widget reordering start. [CHAR_LIMIT=none] -->
+    <string name="keyguard_accessibility_widget_reorder_start">Widget reordering started.</string>
+    <!-- Accessibility description of widget reordering end. [CHAR_LIMIT=none] -->
+    <string name="keyguard_accessibility_widget_reorder_end">Widget reordering ended.</string>
+    <!-- Accessibility description of the a widget deletion event. [CHAR_LIMIT=none] -->
+    <string name="keyguard_accessibility_widget_deleted">Widget <xliff:g id="widget_index">%1$s</xliff:g> deleted.</string>
+    <!-- Accessibility description of the button to expand the lock area. [CHAR_LIMIT=none] -->
+    <string name="keyguard_accessibility_expand_lock_area">Expand unlock area.</string>
 
     <!-- Password keyboard strings. Used by LockScreen and Settings --><skip />
     <!-- Label for "switch to symbols" key.  Must be short to fit on key! -->
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index c34ca3e..7f7a51c 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -174,7 +174,7 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Skærmen er nu låst i liggende retning."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Skærmen er nu låst i stående retning."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="7219575858348719790">"Dagdrømme"</string>
+    <string name="start_dreams" msgid="7219575858348719790">"Dagdrøm"</string>
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Flytilstand"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Oplader, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index d5af0cd..d619de1 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -176,7 +176,7 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"La pantalla está bloqueada en modo horizontal."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"La pantalla está bloqueada en modo vertical."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="7219575858348719790">"Activar Daydreams"</string>
+    <string name="start_dreams" msgid="7219575858348719790">"Activar protector"</string>
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Modo de avión"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Cargando (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index e8bc08d..19de9b6 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -174,7 +174,7 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"La pantalla está bloqueada en modo horizontal."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"La pantalla está bloqueada en modo vertical."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="7219575858348719790">"Suspender"</string>
+    <string name="start_dreams" msgid="7219575858348719790">"Salvapantallas"</string>
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Modo avión"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Cargando (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index a12ccf0..babbcce 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -176,7 +176,7 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"L\'écran est verrouillé en mode paysage."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"L\'écran est verrouillé en mode portrait."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="7219575858348719790">"Écran de veille"</string>
+    <string name="start_dreams" msgid="7219575858348719790">"Écran de veille interactif"</string>
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Mode avion"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"En charge (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index a05aa8f..c454bb1 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -174,7 +174,7 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Ekran jest zablokowany w orientacji poziomej."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Ekran jest zablokowany w orientacji pionowej."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="7219575858348719790">"Śnij na jawie"</string>
+    <string name="start_dreams" msgid="7219575858348719790">"Wygaszacz ekranu"</string>
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Tryb samolotowy"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Ładowanie (<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>)"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 1ca085c..1b8370b 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -174,7 +174,7 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"O ecrã está bloqueado na orientação horizontal."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"O ecrã está bloqueado na orientação vertical."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="7219575858348719790">"Sonho"</string>
+    <string name="start_dreams" msgid="7219575858348719790">"Daydream"</string>
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Modo de avião"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"A carregar, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index fba3da5..7e2d030 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -172,7 +172,7 @@
     <string name="accessibility_rotation_lock_on_landscape" msgid="6731197337665366273">"Skrini imefungwa sasa katika uelekezo wa mandhari."</string>
     <string name="accessibility_rotation_lock_on_portrait" msgid="5809367521644012115">"Skrini imefungwa katika uelekeo wa picha."</string>
     <string name="jelly_bean_dream_name" msgid="5992026543636816792">"BeanFlinger"</string>
-    <string name="start_dreams" msgid="7219575858348719790">"Ndoto ya mchana"</string>
+    <string name="start_dreams" msgid="7219575858348719790">"Hali Tulivu"</string>
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
     <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"Modi ya ndege"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"Inachaji, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
index e929fb1..96cd7ac 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
@@ -670,13 +670,10 @@
         // Find and show this child.
         final int childCount = mSecurityViewContainer.getChildCount();
 
-        // Do flip animation to the next screen
-        if (false) {
-            mSecurityViewContainer.setInAnimation(
-                    AnimationUtils.loadAnimation(mContext, R.anim.keyguard_security_animate_in));
-            mSecurityViewContainer.setOutAnimation(
-                    AnimationUtils.loadAnimation(mContext, R.anim.keyguard_security_animate_out));
-        }
+        mSecurityViewContainer.setInAnimation(
+                AnimationUtils.loadAnimation(mContext, R.anim.keyguard_security_fade_in));
+        mSecurityViewContainer.setOutAnimation(
+                AnimationUtils.loadAnimation(mContext, R.anim.keyguard_security_fade_out));
         final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
         for (int i = 0; i < childCount; i++) {
             if (mSecurityViewContainer.getChildAt(i).getId() == securityViewIdForMode) {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMessageArea.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMessageArea.java
index ca78cf9..5e331e1 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMessageArea.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardMessageArea.java
@@ -16,6 +16,9 @@
 
 package com.android.internal.policy.impl.keyguard;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.os.Handler;
@@ -39,11 +42,15 @@
     static final int BATTERY_LOW_ICON = 0; //R.drawable.ic_lock_idle_low_battery;
 
     static final int SECURITY_MESSAGE_DURATION = 5000;
-    static final String SEPARATOR = " ";
+    protected static final int FADE_DURATION = 750;
+    static final String SEPARATOR = "  ";
 
     // are we showing battery information?
     boolean mShowingBatteryInfo = false;
 
+    // is the bouncer up?
+    boolean mShowingBouncer = false;
+
     // last known plugged in state
     boolean mPluggedIn = false;
 
@@ -68,7 +75,11 @@
         public void run() {
             mMessage = null;
             mShowingMessage = false;
-            update();
+            if (mShowingBouncer) {
+                hideMessage(FADE_DURATION, true);
+            } else {
+                update();
+            }
         }
     };
 
@@ -103,6 +114,18 @@
         }
 
         @Override
+        public void showBouncer(int duration) {
+            mMessageArea.hideMessage(duration, false);
+            mMessageArea.mShowingBouncer = true;
+        }
+
+        @Override
+        public void hideBouncer(int duration) {
+            mMessageArea.showMessage(duration);
+            mMessageArea.mShowingBouncer = false;
+        }
+
+        @Override
         public void setTimeout(int timeoutMs) {
             mMessageArea.mTimeout = timeoutMs;
         }
@@ -139,6 +162,7 @@
     }
 
     public void securityMessageChanged() {
+        setAlpha(1f);
         mShowingMessage = true;
         update();
         mHandler.removeCallbacks(mClearMessageRunnable);
@@ -212,4 +236,23 @@
         return string;
     }
 
+    private void hideMessage(int duration, boolean thenUpdate) {
+        Animator anim = ObjectAnimator.ofFloat(this, "alpha", 0f);
+        anim.setDuration(duration);
+        if (thenUpdate) {
+            anim.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                        public void onAnimationEnd(Animator animation) {
+                        update();
+                    }
+                });
+        }
+        anim.start();
+    }
+
+    private void showMessage(int duration) {
+        Animator anim = ObjectAnimator.ofFloat(this, "alpha", 1f);
+        anim.setDuration(duration);
+        anim.start();
+    }
 }
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityContainer.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityContainer.java
index f6a90c5..04ab0a2 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityContainer.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSecurityContainer.java
@@ -1,11 +1,20 @@
 package com.android.internal.policy.impl.keyguard;
 
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
 import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.widget.FrameLayout;
 
+import com.android.internal.R;
+
 public class KeyguardSecurityContainer extends FrameLayout {
 
+    private float mBackgroundAlpha;
+    private Drawable mBackgroundDrawable;
+
     public KeyguardSecurityContainer(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
@@ -16,5 +25,44 @@
 
     public KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
+        mBackgroundDrawable = context.getResources().getDrawable(R.drawable.kg_bouncer_bg_white);
+    }
+
+    public void setBackgroundAlpha(float alpha) {
+        if (Float.compare(mBackgroundAlpha, alpha) != 0) {
+            mBackgroundAlpha = alpha;
+            invalidate();
+        }
+    }
+
+    public float getBackgroundAlpha() {
+        return mBackgroundAlpha;
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        if (mBackgroundAlpha > 0.0f && mBackgroundDrawable != null) {
+            Drawable bg = mBackgroundDrawable;
+            bg.setAlpha((int) (mBackgroundAlpha * 255));
+            bg.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight());
+            bg.draw(canvas);
+        }
+        super.dispatchDraw(canvas);
+    }
+
+    public void showBouncer(int duration) {
+        SecurityMessageDisplay message = new KeyguardMessageArea.Helper(this);
+        message.showBouncer(duration);
+        Animator anim = ObjectAnimator.ofFloat(this, "BackgroundAlpha", 1f);
+        anim.setDuration(duration);
+        anim.start();
+    }
+
+    public void hideBouncer(int duration) {
+        SecurityMessageDisplay message = new KeyguardMessageArea.Helper(this);
+        message.hideBouncer(duration);
+        Animator anim = ObjectAnimator.ofFloat(this, "BackgroundAlpha", 0f);
+        anim.setDuration(duration);
+        anim.start();
     }
 }
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusViewManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusViewManager.java
index b4bd6e9..7100f1c 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardStatusViewManager.java
@@ -327,6 +327,14 @@
     }
 
     @Override
+    public void showBouncer(int duration) {
+    }
+
+    @Override
+    public void hideBouncer(int duration) {
+    }
+
+    @Override
     public void setTimeout(int timeout_ms) {
     }
 
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/MultiPaneChallengeLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/MultiPaneChallengeLayout.java
index a207f5d..b38eb28 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/MultiPaneChallengeLayout.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/MultiPaneChallengeLayout.java
@@ -16,6 +16,10 @@
 
 package com.android.internal.policy.impl.keyguard;
 
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
@@ -35,8 +39,9 @@
 
     public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
     public static final int VERTICAL = LinearLayout.VERTICAL;
+    protected static final int ANIMATE_BOUNCE_DURATION = 750;
 
-    private View mChallengeView;
+    private KeyguardSecurityContainer mChallengeView;
     private View mUserSwitcherView;
     private View mScrimView;
     private OnBouncerStateChangedListener mBouncerListener;
@@ -87,7 +92,19 @@
         if (mIsBouncing) return;
         mIsBouncing = true;
         if (mScrimView != null) {
-            mScrimView.setVisibility(GONE);
+            if (mChallengeView != null) {
+                mChallengeView.showBouncer(ANIMATE_BOUNCE_DURATION);
+            }
+
+            Animator anim = ObjectAnimator.ofFloat(mScrimView, "alpha", 1f);
+            anim.setDuration(ANIMATE_BOUNCE_DURATION);
+            anim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    mScrimView.setVisibility(VISIBLE);
+                }
+            });
+            anim.start();
         }
         if (mBouncerListener != null) {
             mBouncerListener.onBouncerStateChanged(true);
@@ -99,7 +116,19 @@
         if (!mIsBouncing) return;
         mIsBouncing = false;
         if (mScrimView != null) {
-            mScrimView.setVisibility(GONE);
+            if (mChallengeView != null) {
+                mChallengeView.hideBouncer(ANIMATE_BOUNCE_DURATION);
+            }
+
+            Animator anim = ObjectAnimator.ofFloat(mScrimView, "alpha", 0f);
+            anim.setDuration(ANIMATE_BOUNCE_DURATION);
+            anim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mScrimView.setVisibility(INVISIBLE);
+                }
+            });
+            anim.start();
         }
         if (mBouncerListener != null) {
             mBouncerListener.onBouncerStateChanged(false);
@@ -131,7 +160,8 @@
             mScrimView.setOnClickListener(null);
         }
         mScrimView = scrim;
-        mScrimView.setVisibility(mIsBouncing ? VISIBLE : GONE);
+        mScrimView.setAlpha(mIsBouncing ? 1.0f : 0.0f);
+        mScrimView.setVisibility(mIsBouncing ? VISIBLE : INVISIBLE);
         mScrimView.setFocusable(true);
         mScrimView.setOnClickListener(mScrimClickListener);
     }
@@ -165,7 +195,11 @@
                     throw new IllegalStateException(
                             "There may only be one child of type challenge");
                 }
-                mChallengeView = child;
+                if (!(child instanceof KeyguardSecurityContainer)) {
+                    throw new IllegalArgumentException(
+                            "Challenge must be a KeyguardSecurityContainer");
+                }
+                mChallengeView = (KeyguardSecurityContainer) child;
             } else if (lp.childType == LayoutParams.CHILD_TYPE_USER_SWITCHER) {
                 if (mUserSwitcherView != null) {
                     throw new IllegalStateException(
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/SecurityMessageDisplay.java b/policy/src/com/android/internal/policy/impl/keyguard/SecurityMessageDisplay.java
index ec6472f..7760279 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/SecurityMessageDisplay.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/SecurityMessageDisplay.java
@@ -24,4 +24,8 @@
     public void setMessage(int resId, boolean important, Object... formatArgs);
 
     public void setTimeout(int timeout_ms);
+
+    public void showBouncer(int animationDuration);
+
+    public void hideBouncer(int animationDuration);
 }
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java
index 74b05dd..7cf995c 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java
@@ -16,11 +16,14 @@
 
 package com.android.internal.policy.impl.keyguard;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Paint;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
@@ -61,10 +64,12 @@
     private Drawable mHandleDrawable;
     private Drawable mFrameDrawable;
     private Drawable mDragIconDrawable;
+    private boolean mEdgeCaptured;
 
     // Initialized during measurement from child layoutparams
     private View mChallengeView;
     private View mScrimView;
+    private View mWidgetsView;
 
     // Range: 0 (fully hidden) to 1 (fully visible)
     private float mChallengeOffset = 1.f;
@@ -110,9 +115,14 @@
 
     float mHandleAlpha;
     float mFrameAlpha;
+    float mFrameAnimationTarget = Float.MIN_VALUE;
     private ObjectAnimator mHandleAnimation;
     private ObjectAnimator mFrameAnimation;
 
+    private final Rect mTempRect = new Rect();
+
+    private boolean mHasGlowpad;
+
     static final Property<SlidingChallengeLayout, Float> HANDLE_ALPHA =
             new FloatProperty<SlidingChallengeLayout>("handleAlpha") {
         @Override
@@ -293,21 +303,43 @@
         mHandleAnimation.start();
     }
 
-    void animateFrame(boolean visible, boolean full) {
+    void animateFrame(final boolean visible, final boolean full) {
         if (mFrameDrawable == null) return;
 
-        if (mFrameAnimation != null) {
-            mFrameAnimation.cancel();
-            mFrameAnimation = null;
-        }
         final float targetAlpha = visible ? (full ? 1.f : 0.5f) : 0.f;
-        if (targetAlpha == mFrameAlpha) {
+        if (mFrameAnimation != null && targetAlpha != mFrameAnimationTarget) {
+            mFrameAnimation.cancel();
+            mFrameAnimationTarget = Float.MIN_VALUE;
+        }
+        if (targetAlpha == mFrameAlpha || targetAlpha == mFrameAnimationTarget) {
             return;
         }
+        mFrameAnimationTarget = targetAlpha;
 
         mFrameAnimation = ObjectAnimator.ofFloat(this, FRAME_ALPHA, targetAlpha);
         mFrameAnimation.setInterpolator(sHandleFadeInterpolator);
         mFrameAnimation.setDuration(HANDLE_ANIMATE_DURATION);
+        mFrameAnimation.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mFrameAnimationTarget = Float.MIN_VALUE;
+
+                if (!visible && full && mChallengeView != null) {
+                    // Mess with padding/margin to remove insets on the bouncer frame.
+                    mChallengeView.setPadding(0, 0, 0, 0);
+                    LayoutParams lp = (LayoutParams) mChallengeView.getLayoutParams();
+                    lp.leftMargin = lp.rightMargin = getChallengeMargin(true);
+                    mChallengeView.setLayoutParams(lp);
+                }
+                mFrameAnimation = null;
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                mFrameAnimationTarget = Float.MIN_VALUE;
+                mFrameAnimation = null;
+            }
+        });
         mFrameAnimation.start();
     }
 
@@ -370,7 +402,9 @@
             mScrollState = state;
 
             animateHandle(state == SCROLL_STATE_IDLE && !mChallengeShowing);
-            animateFrame(false , false);
+            if (!mIsBouncing) {
+                animateFrame(false, false);
+            }
             if (mScrollListener != null) {
                 mScrollListener.onScrollStateChanged(state);
             }
@@ -380,6 +414,7 @@
     void completeChallengeScroll() {
         setChallengeShowing(mChallengeOffset != 0);
         setScrollState(SCROLL_STATE_IDLE);
+        mChallengeView.setLayerType(LAYER_TYPE_NONE, null);
     }
 
     void setScrimView(View scrim) {
@@ -461,7 +496,22 @@
         if (mScrimView != null) {
             mScrimView.setVisibility(VISIBLE);
         }
+
+        // Mess with padding/margin to inset the bouncer frame.
+        // We have more space available to us otherwise.
+        if (mChallengeView != null) {
+            if (mFrameDrawable == null || !mFrameDrawable.getPadding(mTempRect)) {
+                mTempRect.set(0, 0, 0, 0);
+            }
+            mChallengeView.setPadding(mTempRect.left, mTempRect.top, mTempRect.right,
+                    mTempRect.bottom);
+            final LayoutParams lp = (LayoutParams) mChallengeView.getLayoutParams();
+            lp.leftMargin = lp.rightMargin = getChallengeMargin(false);
+            mChallengeView.setLayoutParams(lp);
+        }
+
         animateFrame(true, true);
+
         if (mBouncerListener != null) {
             mBouncerListener.onBouncerStateChanged(true);
         }
@@ -470,17 +520,21 @@
     @Override
     public void hideBouncer() {
         if (!mIsBouncing) return;
-        setChallengeShowing(false);
+        showChallenge(false);
         mIsBouncing = false;
         if (mScrimView != null) {
             mScrimView.setVisibility(GONE);
         }
-        animateFrame(false, false);
+        animateFrame(false, true);
         if (mBouncerListener != null) {
             mBouncerListener.onBouncerStateChanged(false);
         }
     }
 
+    private int getChallengeMargin(boolean expanded) {
+        return expanded && mHasGlowpad ? 0 : mDragHandleEdgeSlop;
+    }
+
     @Override
     public void requestDisallowInterceptTouchEvent(boolean allowIntercept) {
         // We'll intercept whoever we feel like! ...as long as it isn't a challenge view.
@@ -495,8 +549,6 @@
         }
         mVelocityTracker.addMovement(ev);
 
-        //Log.v(TAG, "onIntercept: " + ev);
-
         final int action = ev.getActionMasked();
         switch (action) {
             case MotionEvent.ACTION_DOWN:
@@ -525,6 +577,7 @@
                         mGestureStartY = y;
                         mGestureStartChallengeBottom = getChallengeBottom();
                         mDragging = true;
+                        mChallengeView.setLayerType(LAYER_TYPE_HARDWARE, null);
                     } else if (isInChallengeView(x, y)) {
                         mBlockDrag = true;
                     }
@@ -601,6 +654,7 @@
                             mActivePointerId = ev.getPointerId(i);
                             mGestureStartChallengeBottom = getChallengeBottom();
                             mDragging = true;
+                            mChallengeView.setLayerType(LAYER_TYPE_HARDWARE, null);
                             break;
                         }
                     }
@@ -631,6 +685,52 @@
     }
 
     /**
+     * The lifecycle of touch events is subtle and it's very easy to do something
+     * that will cause bugs that will be nasty to track when overriding this method.
+     * Normally one should always override onInterceptTouchEvent instead.
+     *
+     * To put it another way, don't try this at home.
+     */
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent ev) {
+        final int action = ev.getActionMasked();
+        boolean handled = false;
+        if (action == MotionEvent.ACTION_DOWN) {
+            // Defensive programming: if we didn't get the UP or CANCEL, reset anyway.
+            mEdgeCaptured = false;
+        }
+        if (mWidgetsView != null && !mIsBouncing && (mEdgeCaptured || isEdgeSwipeBeginEvent(ev))) {
+            // Normally we would need to do a lot of extra stuff here.
+            // We can only get away with this because we haven't padded in
+            // the widget pager or otherwise transformed it during layout.
+            // We also don't support things like splitting MotionEvents.
+
+            // We set handled to captured even if dispatch is returning false here so that
+            // we don't send a different view a busted or incomplete event stream.
+            handled = mEdgeCaptured |= mWidgetsView.dispatchTouchEvent(ev);
+        }
+
+        if (!handled && !mEdgeCaptured) {
+            handled = super.dispatchTouchEvent(ev);
+        }
+
+        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+            mEdgeCaptured = false;
+        }
+
+        return handled;
+    }
+
+    private boolean isEdgeSwipeBeginEvent(MotionEvent ev) {
+        if (ev.getActionMasked() != MotionEvent.ACTION_DOWN) {
+            return false;
+        }
+
+        final float x = ev.getX();
+        return x < mDragHandleEdgeSlop || x >= getWidth() - mDragHandleEdgeSlop;
+    }
+
+    /**
      * We only want to add additional vertical space to the drag handle when the panel is fully
      * closed.
      */
@@ -699,8 +799,16 @@
                 }
                 // We're going to play silly games with the frame's background drawable later.
                 mFrameDrawable = mChallengeView.getBackground();
+
+                if (!mHasLayout) {
+                    // Set up the margin correctly based on our content for the first run.
+                    mHasGlowpad = child.findViewById(R.id.keyguard_selector_view) != null;
+                    lp.leftMargin = lp.rightMargin = getChallengeMargin(true);
+                }
             } else if (lp.childType == LayoutParams.CHILD_TYPE_SCRIM) {
                 setScrimView(child);
+            } else if (lp.childType == LayoutParams.CHILD_TYPE_WIDGETS) {
+                mWidgetsView = child;
             }
 
             if (child.getVisibility() == GONE) continue;
@@ -980,6 +1088,7 @@
         public static final int CHILD_TYPE_NONE = 0;
         public static final int CHILD_TYPE_CHALLENGE = 2;
         public static final int CHILD_TYPE_SCRIM = 4;
+        public static final int CHILD_TYPE_WIDGETS = 5;
 
         public LayoutParams() {
             this(MATCH_PARENT, WRAP_CONTENT);