Merge "Don't show ECA in landscape mode on phones." into jb-mr1-lockscreen-dev
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index fdaf39e..0fe2a8e 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -1,16 +1,16 @@
 /*
 ** Copyright 2006, 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 
+** 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 
+**     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 
+** 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.
 */
 
@@ -107,7 +107,7 @@
     Configuration updateOrientationFromAppTokens(in Configuration currentConfig,
             IBinder freezeThisOneIfNeeded);
     void setNewConfiguration(in Configuration config);
-    
+
     void startFreezingScreen(int exitAnim, int enterAnim);
     void stopFreezingScreen();
 
@@ -121,13 +121,13 @@
     void dismissKeyguard();
 
     void closeSystemDialogs(String reason);
-    
+
     // These can only be called with the SET_ANIMATON_SCALE permission.
     float getAnimationScale(int which);
     float[] getAnimationScales();
     void setAnimationScale(int which, float scale);
     void setAnimationScales(in float[] scales);
-    
+
     // For testing
     void setInTouchMode(boolean showFocus);
 
@@ -161,7 +161,7 @@
      * {@link android.view.Surface}.
      */
     int getRotation();
-    
+
     /**
      * Watch the rotation of the screen.  Returns the current rotation,
      * calls back when it changes.
@@ -175,24 +175,24 @@
      */
     int getPreferredOptionsPanelGravity();
 
-	/**
-	 * Lock the device orientation to the specified rotation, or to the
-	 * current rotation if -1.  Sensor input will be ignored until
-	 * thawRotation() is called.
-	 * @hide
-	 */
-	void freezeRotation(int rotation);
+    /**
+     * Lock the device orientation to the specified rotation, or to the
+     * current rotation if -1.  Sensor input will be ignored until
+     * thawRotation() is called.
+     * @hide
+     */
+    void freezeRotation(int rotation);
 
-	/**
-	 * Release the orientation lock imposed by freezeRotation().
-	 * @hide
-	 */
-	void thawRotation();
+    /**
+     * Release the orientation lock imposed by freezeRotation().
+     * @hide
+     */
+    void thawRotation();
 
-	/**
-	 * Create a screenshot of the applications currently displayed.
-	 */
-	Bitmap screenshotApplications(IBinder appToken, int displayId, int maxWidth, int maxHeight);
+    /**
+     * Create a screenshot of the applications currently displayed.
+     */
+    Bitmap screenshotApplications(IBinder appToken, int displayId, int maxWidth, int maxHeight);
 
     /**
      * Called by the status bar to notify Views of changes to System UI visiblity.
@@ -259,4 +259,10 @@
      * Device is in safe mode.
      */
     boolean isSafeModeEnabled();
+
+    /**
+     * Tell keyguard to show the assistant (Intent.ACTION_ASSIST) after asking for the user's
+     * credentials.
+     */
+    void showAssistant();
 }
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 4ccb502..26739b3 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -1136,4 +1136,10 @@
      * @param args additional arguments to the dump request.
      */
     public void dump(String prefix, PrintWriter writer, String[] args);
+
+    /**
+     * Ask keyguard to invoke the assist intent after dismissing keyguard
+     * {@link android.content.Intent#ACTION_ASSIST}
+     */
+    public void showAssistant();
 }
diff --git a/core/res/res/anim/keyguard_action_assist_enter.xml b/core/res/res/anim/keyguard_action_assist_enter.xml
new file mode 100644
index 0000000..f3333b7
--- /dev/null
+++ b/core/res/res/anim/keyguard_action_assist_enter.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+        android:shareInterpolator="false" android:zAdjustment="top">
+
+    <alpha android:fromAlpha="0" android:toAlpha="1.0"
+            android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+            android:interpolator="@android:interpolator/decelerate_cubic"
+            android:duration="300"/>
+
+    <translate android:fromYDelta="100%" android:toYDelta="0"
+            android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+            android:interpolator="@android:interpolator/decelerate_cubic"
+            android:duration="300" />
+</set>
diff --git a/core/res/res/anim/keyguard_action_assist_exit.xml b/core/res/res/anim/keyguard_action_assist_exit.xml
new file mode 100644
index 0000000..b4ed278
--- /dev/null
+++ b/core/res/res/anim/keyguard_action_assist_exit.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+       android:interpolator="@android:anim/accelerate_interpolator"
+       android:fromXDelta="0" android:toXDelta="0"
+       android:duration="300" />
diff --git a/core/res/res/layout/keyguard_selector_view.xml b/core/res/res/layout/keyguard_selector_view.xml
index c5061bb..dfacb6a 100644
--- a/core/res/res/layout/keyguard_selector_view.xml
+++ b/core/res/res/layout/keyguard_selector_view.xml
@@ -43,6 +43,14 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content" />
 
+        <View
+            android:id="@+id/keyguard_selector_view_frame"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_marginLeft="16dp"
+            android:layout_marginRight="16dp"
+            android:background="@*android:drawable/kg_bouncer_bg_white"/>
+
         <include layout="@layout/keyguard_glow_pad_container" />
 
         <include layout="@layout/keyguard_eca"
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 86f8665..68a0289 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1189,6 +1189,9 @@
   <java-symbol type="anim" name="keyguard_security_animate_out" />
   <java-symbol type="anim" name="keyguard_security_fade_in" />
   <java-symbol type="anim" name="keyguard_security_fade_out" />
+  <java-symbol type="anim" name="keyguard_action_assist_exit" />
+  <java-symbol type="anim" name="keyguard_action_assist_enter" />
+
   <java-symbol type="array" name="config_keyboardTapVibePattern" />
   <java-symbol type="array" name="config_longPressVibePattern" />
   <java-symbol type="array" name="config_safeModeDisabledVibePattern" />
@@ -1350,6 +1353,7 @@
   <java-symbol type="id" name="multi_pane_challenge" />
   <java-symbol type="id" name="keyguard_user_selector" />
   <java-symbol type="id" name="key_enter" />
+  <java-symbol type="id" name="keyguard_selector_view_frame" />
   <java-symbol type="integer" name="config_carDockRotation" />
   <java-symbol type="integer" name="config_defaultUiModeType" />
   <java-symbol type="integer" name="config_deskDockRotation" />
diff --git a/packages/SystemUI/src/com/android/systemui/SearchPanelView.java b/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
index 4b0e818..76613e0 100644
--- a/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
@@ -26,11 +26,13 @@
 import android.content.Intent;
 import android.content.res.Resources;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.Vibrator;
 import android.provider.Settings;
 import android.util.AttributeSet;
 import android.util.Slog;
+import android.view.IWindowManager;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
@@ -62,6 +64,7 @@
     private boolean mShowing;
     private View mSearchTargetsContainer;
     private GlowPadView mGlowPadView;
+    private IWindowManager mWm;
 
     public SearchPanelView(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
@@ -70,6 +73,7 @@
     public SearchPanelView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
         mContext = context;
+        mWm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
     }
 
     private void startAssistActivity() {
@@ -77,28 +81,13 @@
 
         // Close Recent Apps if needed
         mBar.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_SEARCH_PANEL);
-        // Launch Assist
-        Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
-                .getAssistIntent(mContext, UserHandle.USER_CURRENT);
-        if (intent == null) return;
 
-        // Dismiss the keyguard if possible. XXX: TODO: invoke bouncer.
         try {
-            ActivityManagerNative.getDefault().dismissKeyguardOnNextActivity();
+            mWm.showAssistant();
         } catch (RemoteException e) {
+            // too bad, so sad...
         }
-
-        try {
-            ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
-                    R.anim.search_launch_enter, R.anim.search_launch_exit,
-                    getHandler(), this);
-            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-            mContext.startActivityAsUser(intent, opts.toBundle(),
-                    new UserHandle(UserHandle.USER_CURRENT));
-        } catch (ActivityNotFoundException e) {
-            Slog.w(TAG, "Activity not found for " + intent.getAction());
-            onAnimationStarted();
-        }
+        onAnimationStarted();
     }
 
     class GlowPadTriggerListener implements GlowPadView.OnTriggerListener {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 54bf20d..d9c07f8 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -4540,6 +4540,11 @@
     }
 
     @Override
+    public void showAssistant() {
+        mKeyguardMediator.showAssistant();
+    }
+
+    @Override
     public void dump(String prefix, PrintWriter pw, String[] args) {
         pw.print(prefix); pw.print("mSafeMode="); pw.print(mSafeMode);
                 pw.print(" mSystemReady="); pw.print(mSystemReady);
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/ChallengeLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/ChallengeLayout.java
index 605a738..8ece559 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/ChallengeLayout.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/ChallengeLayout.java
@@ -59,6 +59,11 @@
     boolean isBouncing();
 
     /**
+     * Returns the duration of the bounce animation.
+     */
+    int getBouncerAnimationDuration();
+
+    /**
      * Set a listener that will respond to changes in bouncer state.
      *
      * @param listener listener to register
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 e89888c..368cfce 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
@@ -20,11 +20,13 @@
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.AlertDialog;
+import android.app.SearchManager;
 import android.app.admin.DevicePolicyManager;
 import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
+import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -37,6 +39,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -156,6 +159,8 @@
         }
     };
 
+    private SlidingChallengeLayout mSlidingChallengeLayout;
+
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
         boolean result = super.onTouchEvent(ev);
@@ -196,17 +201,17 @@
         mAppWidgetContainer.setDeleteDropTarget(deleteDropTarget);
         mAppWidgetContainer.setMinScale(0.5f);
 
-        SlidingChallengeLayout slider =
-                (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
-        if (slider != null) {
-            slider.setOnChallengeScrolledListener(mViewStateManager);
+        mSlidingChallengeLayout = (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
+        if (mSlidingChallengeLayout != null) {
+            mSlidingChallengeLayout.setOnChallengeScrolledListener(mViewStateManager);
         }
         mAppWidgetContainer.setViewStateManager(mViewStateManager);
         mAppWidgetContainer.setLockPatternUtils(mLockPatternUtils);
 
-        ChallengeLayout challenge = slider != null ? slider :
+        ChallengeLayout challenge = mSlidingChallengeLayout != null ? mSlidingChallengeLayout :
             (ChallengeLayout) findViewById(R.id.multi_pane_challenge);
         challenge.setOnBouncerStateChangedListener(mViewStateManager);
+        mAppWidgetContainer.setBouncerAnimationDuration(challenge.getBouncerAnimationDuration());
         mViewStateManager.setPagedView(mAppWidgetContainer);
         mViewStateManager.setChallengeLayout(challenge);
         mSecurityViewContainer = (KeyguardSecurityViewFlipper) findViewById(R.id.view_flipper);
@@ -732,6 +737,10 @@
         mAppWidgetContainer.setVisibility(
                 isSimOrAccount && fullScreenEnabled ? View.GONE : View.VISIBLE);
 
+        if (mSlidingChallengeLayout != null) {
+            mSlidingChallengeLayout.setChallengeInteractive(!fullScreenEnabled);
+        }
+
         // Emulate Activity life cycle
         if (oldView != null) {
             oldView.onPause();
@@ -1436,4 +1445,31 @@
     public void dismiss() {
         showNextSecurityScreenOrFinish(false);
     }
+
+    public void showAssistant() {
+        final Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
+          .getAssistIntent(mContext, UserHandle.USER_CURRENT);
+
+        if (intent == null) return;
+
+        final ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
+                R.anim.keyguard_action_assist_enter, R.anim.keyguard_action_assist_exit,
+                getHandler(), null);
+
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        setOnDismissAction(new OnDismissAction() {
+            @Override
+            public boolean onDismiss() {
+                try {
+                    mContext.startActivityAsUser(intent, opts.toBundle(),
+                                new UserHandle(UserHandle.USER_CURRENT));
+                    } catch (ActivityNotFoundException e) {
+                        Slog.w(TAG, "Activity not found for " + intent.getAction());
+                    }
+                return false;
+            }
+        });
+
+        mViewStateManager.showBouncer(true);
+    }
 }
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java
index 062f1ec..76cbbd5 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardSelectorView.java
@@ -21,6 +21,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.graphics.drawable.Drawable;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.AttributeSet;
@@ -45,9 +46,12 @@
     private GlowPadView mGlowPadView;
     private ObjectAnimator mAnim;
     private View mFadeView;
+    private boolean mIsBouncing;
     private boolean mCameraDisabled;
     private boolean mSearchDisabled;
     private LockPatternUtils mLockPatternUtils;
+    private SecurityMessageDisplay mSecurityMessageDisplay;
+    private Drawable mBouncerFrame;
 
     OnTriggerListener mOnTriggerListener = new OnTriggerListener() {
 
@@ -80,7 +84,9 @@
         }
 
         public void onReleased(View v, int handle) {
-            doTransition(mFadeView, 1.0f);
+            if (!mIsBouncing) {
+                doTransition(mFadeView, 1.0f);
+            }
         }
 
         public void onGrabbed(View v, int handle) {
@@ -143,6 +149,10 @@
         mGlowPadView = (GlowPadView) findViewById(R.id.glow_pad_view);
         mGlowPadView.setOnTriggerListener(mOnTriggerListener);
         updateTargets();
+
+        mSecurityMessageDisplay = new KeyguardMessageArea.Helper(this);
+        View bouncerFrameView = findViewById(R.id.keyguard_selector_view_frame);
+        mBouncerFrame = bouncerFrameView.getBackground();
     }
 
     public void setCarrierArea(View carrierArea) {
@@ -264,9 +274,15 @@
 
     @Override
     public void showBouncer(int duration) {
+        mIsBouncing = true;
+        KeyguardSecurityViewHelper.
+                showBouncer(mSecurityMessageDisplay, mFadeView, mBouncerFrame, duration);
     }
 
     @Override
     public void hideBouncer(int duration) {
+        mIsBouncing = false;
+        KeyguardSecurityViewHelper.
+                hideBouncer(mSecurityMessageDisplay, mFadeView, mBouncerFrame, duration);
     }
 }
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
index 365c530..c81ff55 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewManager.java
@@ -168,6 +168,7 @@
 
             int flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                     | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
+                    | WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN
                     | WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 
             if (!mNeedsInput) {
@@ -424,4 +425,10 @@
     public synchronized boolean isShowing() {
         return (mKeyguardHost != null && mKeyguardHost.getVisibility() == View.VISIBLE);
     }
+
+    public void showAssistant() {
+        if (mKeyguardView != null) {
+            mKeyguardView.showAssistant();
+        }
+    }
 }
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
index 128930a..23c642d 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewMediator.java
@@ -116,6 +116,7 @@
     private static final int KEYGUARD_DONE_AUTHENTICATING = 11;
     private static final int SET_HIDDEN = 12;
     private static final int KEYGUARD_TIMEOUT = 13;
+    private static final int SHOW_ASSISTANT = 14;
 
     /**
      * The default amount of time we stay awake (used for all key input)
@@ -1130,6 +1131,9 @@
                         doKeyguardLocked((Bundle) msg.obj);
                     }
                     break;
+                case SHOW_ASSISTANT:
+                    handleShowAssistant();
+                    break;
             }
         }
     };
@@ -1393,4 +1397,13 @@
         return mKeyguardDonePending || !isSecure();
     }
 
+    public void showAssistant() {
+        Message msg = mHandler.obtainMessage(SHOW_ASSISTANT);
+        mHandler.sendMessage(msg);
+    }
+
+    public void handleShowAssistant() {
+        mKeyguardViewManager.showAssistant();
+    }
+
 }
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java
index b82d7c7..945e9c2 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardViewStateManager.java
@@ -120,6 +120,7 @@
         // once the user swipes a page we clear that behavior
         if (mKeyguardHostView != null) {
             mKeyguardHostView.clearAppWidgetToShow();
+            mKeyguardHostView.setOnDismissAction(null);
         }
         if (mHideHintsRunnable != null) {
             mMainQueue.removeCallbacks(mHideHintsRunnable);
@@ -220,6 +221,11 @@
                     mKeyguardWidgetPager.setWidgetToResetOnPageFadeOut(mPageListeningToSlider);
                 }
             }
+            if (frame.isSmall()) {
+                // This is to make sure that if the scroller animation gets cut off midway
+                // that the frame doesn't stay in a partial down position.
+                frame.setFrameHeight(frame.getSmallFrameHeight());
+            }
             if (scrollState != SlidingChallengeLayout.SCROLL_STATE_FADING) {
                 frame.hideFrame(this);
             }
@@ -250,7 +256,7 @@
                 if (!frame.isSmall()) {
                     // We need to fetch the final page, in case the pages are in motion.
                     mPageListeningToSlider = mKeyguardWidgetPager.getNextPage();
-                    frame.shrinkWidget();
+                    frame.shrinkWidget(false);
                 }
             } else {
                 if (!frame.isSmall()) {
@@ -269,7 +275,7 @@
     public void onScrollPositionChanged(float scrollPosition, int challengeTop) {
         mChallengeTop = challengeTop;
         KeyguardWidgetFrame frame = mKeyguardWidgetPager.getWidgetPageAt(mPageListeningToSlider);
-        if (frame != null && !mKeyguardWidgetPager.isPageMoving()) {
+        if (frame != null && mLastScrollState != SlidingChallengeLayout.SCROLL_STATE_FADING) {
             frame.adjustFrame(getChallengeTopRelativeToFrame(frame, mChallengeTop));
         }
     }
@@ -311,6 +317,9 @@
             mKeyguardWidgetPager.zoomOutToBouncer();
         } else {
             mKeyguardWidgetPager.zoomInFromBouncer();
+            if (mKeyguardHostView != null) {
+                mKeyguardHostView.setOnDismissAction(null);
+            }
         }
     }
 }
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java
index d9953bf..4c7ab6c 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java
@@ -329,6 +329,7 @@
 
     public void setMaxChallengeTop(int top) {
         boolean dirty = mMaxChallengeTop != top;
+        mMaxChallengeTop = top;
         mSmallWidgetHeight = top - getPaddingTop();
         mSmallFrameHeight = top + getPaddingBottom();
         if (dirty && mIsSmall) {
@@ -348,10 +349,21 @@
         setFrameHeight(frameHeight);
     }
 
-    public void shrinkWidget() {
+    public void shrinkWidget(boolean alsoShrinkFrame) {
         mIsSmall = true;
         setWidgetHeight(mSmallWidgetHeight);
-        setFrameHeight(mSmallFrameHeight);
+
+        if (alsoShrinkFrame) {
+            setFrameHeight(mSmallFrameHeight);
+        }
+    }
+
+    public int getSmallFrameHeight() {
+        return mSmallFrameHeight;
+    }
+
+    public void shrinkWidget() {
+        shrinkWidget(true);
     }
 
     public void setWidgetLockedSmall(boolean locked) {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
index 7df1512..c21e04f 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
@@ -36,6 +36,7 @@
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
+import android.view.animation.DecelerateInterpolator;
 import android.widget.FrameLayout;
 
 import com.android.internal.widget.LockPatternUtils;
@@ -71,7 +72,7 @@
     private int mWidgetToResetAfterFadeOut;
 
     // Bouncer
-    protected int BOUNCER_ZOOM_IN_OUT_DURATION = 250;
+    private int mBouncerZoomInOutDuration = 250;
     private float BOUNCER_SCALE_FACTOR = 0.67f;
 
     // Background worker thread: used here for persistence, also made available to widget frames
@@ -747,6 +748,10 @@
         }
     }
 
+    void setBouncerAnimationDuration(int duration) {
+        mBouncerZoomInOutDuration = duration;
+    }
+
     // Zoom in after the bouncer is dismissed
     void zoomInFromBouncer() {
         if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) {
@@ -755,10 +760,11 @@
         final View currentPage = getPageAt(getCurrentPage());
         if (currentPage.getScaleX() < 1f || currentPage.getScaleY() < 1f) {
             mZoomInOutAnim = new AnimatorSet();
-            mZoomInOutAnim.setDuration(BOUNCER_ZOOM_IN_OUT_DURATION);
             mZoomInOutAnim.playTogether(
                     ObjectAnimator.ofFloat(currentPage, "scaleX", 1f),
                     ObjectAnimator.ofFloat(currentPage , "scaleY", 1f));
+            mZoomInOutAnim.setDuration(mBouncerZoomInOutDuration);
+            mZoomInOutAnim.setInterpolator(new DecelerateInterpolator(1.5f));
             mZoomInOutAnim.start();
         }
     }
@@ -768,18 +774,22 @@
         if (mZoomInOutAnim != null && mZoomInOutAnim.isRunning()) {
             mZoomInOutAnim.cancel();
         }
-        View currentPage = getPageAt(getCurrentPage());
-        currentPage.setPivotY(0);
-        // Note: we are working around the issue that setting the x-pivot to the same value as it
-        //       was does not actually work.
-        currentPage.setPivotX(0);
-        currentPage.setPivotX(currentPage.getMeasuredWidth() / 2);
+        int curPage = getCurrentPage();
+        View currentPage = getPageAt(curPage);
+        if (shouldSetTopAlignedPivotForWidget(curPage)) {
+            currentPage.setPivotY(0);
+            // Note: we are working around the issue that setting the x-pivot to the same value as it
+            //       was does not actually work.
+            currentPage.setPivotX(0);
+            currentPage.setPivotX(currentPage.getMeasuredWidth() / 2);
+        }
         if (!(currentPage.getScaleX() < 1f || currentPage.getScaleY() < 1f)) {
             mZoomInOutAnim = new AnimatorSet();
-            mZoomInOutAnim.setDuration(BOUNCER_ZOOM_IN_OUT_DURATION);
             mZoomInOutAnim.playTogether(
                     ObjectAnimator.ofFloat(currentPage, "scaleX", BOUNCER_SCALE_FACTOR),
                     ObjectAnimator.ofFloat(currentPage, "scaleY", BOUNCER_SCALE_FACTOR));
+            mZoomInOutAnim.setDuration(mBouncerZoomInOutDuration);
+            mZoomInOutAnim.setInterpolator(new DecelerateInterpolator(1.5f));
             mZoomInOutAnim.start();
         }
     }
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 026f1bd..0ca46c3 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/MultiPaneChallengeLayout.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/MultiPaneChallengeLayout.java
@@ -40,7 +40,7 @@
 
     public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
     public static final int VERTICAL = LinearLayout.VERTICAL;
-    protected static final int ANIMATE_BOUNCE_DURATION = 750;
+    public static final int ANIMATE_BOUNCE_DURATION = 350;
 
     private KeyguardSecurityContainer mChallengeView;
     private View mUserSwitcherView;
@@ -97,6 +97,11 @@
     }
 
     @Override
+    public int getBouncerAnimationDuration() {
+        return ANIMATE_BOUNCE_DURATION;
+    }
+
+    @Override
     public void showBouncer() {
         if (mIsBouncing) return;
         mIsBouncing = true;
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 c734423..073225f 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java
@@ -56,7 +56,7 @@
     private static final int DRAG_HANDLE_OPEN_ABOVE = 8; // dp
     private static final int DRAG_HANDLE_OPEN_BELOW = 0; // dp
 
-    private static final int HANDLE_ANIMATE_DURATION = 200; // ms
+    private static final int HANDLE_ANIMATE_DURATION = 250; // ms
 
     // Drawn to show the drag handle in closed state; crossfades to the challenge view
     // when challenge is fully visible
@@ -469,24 +469,30 @@
     }
 
     @Override
+    public int getBouncerAnimationDuration() {
+        return HANDLE_ANIMATE_DURATION;
+    }
+
+    @Override
     public void showBouncer() {
         if (mIsBouncing) return;
         mWasChallengeShowing = mChallengeShowing;
         mIsBouncing = true;
         showChallenge(true);
         if (mScrimView != null) {
-            mScrimView.setVisibility(VISIBLE);
+            Animator anim = ObjectAnimator.ofFloat(mScrimView, "alpha", 1f);
+            anim.setDuration(HANDLE_ANIMATE_DURATION);
+            anim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    mScrimView.setVisibility(VISIBLE);
+                }
+            });
+            anim.start();
         }
         if (mChallengeView != null) {
             mChallengeView.showBouncer(HANDLE_ANIMATE_DURATION);
         }
-        // Mess with padding/margin to inset the bouncer frame.
-        // We have more space available to us otherwise.
-        if (mChallengeView != null) {
-            final LayoutParams lp = (LayoutParams) mChallengeView.getLayoutParams();
-            lp.leftMargin = lp.rightMargin = getChallengeMargin(false);
-            mChallengeView.setLayoutParams(lp);
-        }
 
         if (mBouncerListener != null) {
             mBouncerListener.onBouncerStateChanged(true);
@@ -498,8 +504,17 @@
         if (!mIsBouncing) return;
         if (!mWasChallengeShowing) showChallenge(false);
         mIsBouncing = false;
+
         if (mScrimView != null) {
-            mScrimView.setVisibility(GONE);
+            Animator anim = ObjectAnimator.ofFloat(mScrimView, "alpha", 0f);
+            anim.setDuration(HANDLE_ANIMATE_DURATION);
+            anim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mScrimView.setVisibility(GONE);
+                }
+            });
+            anim.start();
         }
         if (mChallengeView != null) {
             mChallengeView.hideBouncer(HANDLE_ANIMATE_DURATION);
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 4659c9d..7e4490e 100755
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -10352,6 +10352,15 @@
         return mSafeMode;
     }
 
+    public void showAssistant() {
+        // TODO: What permission?
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER)
+                != PackageManager.PERMISSION_GRANTED) {
+            return;
+        }
+        mPolicy.showAssistant();
+    }
+
     void dumpPolicyLocked(PrintWriter pw, String[] args, boolean dumpAll) {
         pw.println("WINDOW MANAGER POLICY STATE (dumpsys window policy)");
         mPolicy.dump("    ", pw, args);
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index fa2cb50..3e625f9 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -453,6 +453,11 @@
     }
 
     @Override
+    public void showAssistant() {
+
+    }
+
+    @Override
     public IBinder getFocusedWindowToken() {
         // TODO Auto-generated method stub
         return null;